Skip to content

Commit

Permalink
Respect EditorConfig settings (#2760)
Browse files Browse the repository at this point in the history
This fixes #42.
It adds support for .editorconfig's `indent_style`, `indent_size`, `tab_width`,
and `max_line_length` properties.

It doesn't support the `end_of_line` property as described in
#42 (comment),
but that could be added later. The same goes for `quote_type`
(prettier/prettier-atom#293 (comment)).

* Make test .prettierrc not set config for all file extensions

This makes it easier to keep tests isolated.
  • Loading branch information
josephfrazier authored Nov 8, 2017
1 parent 9652ad7 commit 8f58ca0
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 19 deletions.
8 changes: 8 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,11 @@ For more information on how to use the CLI to locate a file, see the [CLI](cli.m
## Configuration Schema

If you'd like a JSON schema to validate your configuration, one is available here: http://json.schemastore.org/prettierrc.

## EditorConfig

If an [`.editorconfig` file](http://editorconfig.org/) is in your project, Prettier will parse it and convert its properties to the corresponding prettier configuration. This configuration will be overridden by `.prettierrc`, etc. Currently, the following EditorConfig properties are supported:

* `indent_style`
* `indent_size`/`tab_width`
* `max_line_length`
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
"cosmiconfig": "3.1.0",
"dashify": "0.2.2",
"diff": "3.2.0",
"editorconfig": "0.14.2",
"editorconfig-to-prettier": "0.0.1",
"emoji-regex": "6.5.1",
"escape-string-regexp": "1.0.5",
"esutils": "2.0.2",
Expand All @@ -37,6 +39,7 @@
"minimatch": "3.0.4",
"minimist": "1.2.0",
"parse5": "3.0.3",
"path-root": "0.1.1",
"postcss-less": "1.1.1",
"postcss-media-query-parser": "0.2.3",
"postcss-scss": "1.0.2",
Expand Down
43 changes: 43 additions & 0 deletions src/resolve-config-editorconfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"use strict";

const editorconfig = require("editorconfig");
const mem = require("mem");
const pathRoot = require("path-root");
const editorConfigToPrettier = require("editorconfig-to-prettier");

const maybeParse = (filePath, config, parse) => {
const root = filePath && pathRoot(filePath);
return filePath && !config && parse(filePath, { root });
};

const editorconfigAsyncNoCache = (filePath, config) => {
return Promise.resolve(maybeParse(filePath, config, editorconfig.parse)).then(
editorConfigToPrettier
);
};
const editorconfigAsyncWithCache = mem(editorconfigAsyncNoCache);

const editorconfigSyncNoCache = (filePath, config) => {
return editorConfigToPrettier(
maybeParse(filePath, config, editorconfig.parseSync)
);
};
const editorconfigSyncWithCache = mem(editorconfigSyncNoCache);

function getLoadFunction(opts) {
if (opts.sync) {
return opts.cache ? editorconfigSyncWithCache : editorconfigSyncNoCache;
}

return opts.cache ? editorconfigAsyncWithCache : editorconfigAsyncNoCache;
}

function clearCache() {
mem.clear(editorconfigSyncWithCache);
mem.clear(editorconfigAsyncWithCache);
}

module.exports = {
getLoadFunction,
clearCache
};
44 changes: 33 additions & 11 deletions src/resolve-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const minimatch = require("minimatch");
const path = require("path");
const mem = require("mem");

const resolveEditorConfig = require("./resolve-config-editorconfig");

const getExplorerMemoized = mem(opts =>
cosmiconfig("prettier", {
sync: opts.sync,
Expand All @@ -26,23 +28,43 @@ function getLoadFunction(opts) {
return getExplorerMemoized(opts).load;
}

function resolveConfig(filePath, opts) {
function _resolveConfig(filePath, opts, sync) {
opts = Object.assign({ useCache: true }, opts);
const load = getLoadFunction({ cache: !!opts.useCache, sync: false });
return load(filePath, opts.config).then(result => {
return !result ? null : mergeOverrides(result, filePath);
});
const loadOpts = { cache: !!opts.useCache, sync: !!sync };
const load = getLoadFunction(loadOpts);
const loadEditorConfig = resolveEditorConfig.getLoadFunction(loadOpts);
const arr = [load, loadEditorConfig].map(l => l(filePath, opts.config));

const unwrapAndMerge = arr => {
const result = arr[0];
const editorConfigured = arr[1];
const merged = Object.assign(
{},
editorConfigured,
mergeOverrides(Object.assign({}, result), filePath)
);

if (Object.keys(merged).length === 0) {
return null;
}

return merged;
};

if (loadOpts.sync) {
return unwrapAndMerge(arr);
}

return Promise.all(arr).then(unwrapAndMerge);
}

resolveConfig.sync = (filePath, opts) => {
opts = Object.assign({ useCache: true }, opts);
const load = getLoadFunction({ cache: !!opts.useCache, sync: true });
const result = load(filePath, opts.config);
return !result ? null : mergeOverrides(result, filePath);
};
const resolveConfig = (filePath, opts) => _resolveConfig(filePath, opts, false);

resolveConfig.sync = (filePath, opts) => _resolveConfig(filePath, opts, true);

function clearCache() {
mem.clear(getExplorerMemoized);
resolveEditorConfig.clearCache();
}

function resolveConfigFile(filePath) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,22 @@
exports[`CLI overrides take precedence (stderr) 1`] = `""`;

exports[`CLI overrides take precedence (stdout) 1`] = `
"console.log(
"function f() {
console.log(
\\"should have tab width 8\\"
)
}
function f() {
console.log(
\\"should have space width 2\\"
)
}
function f() {
console.log(
\\"should have space width 8\\"
)
}
console.log(
\\"jest/__best-tests__/file.js should have semi\\"
);
console.log(
Expand Down Expand Up @@ -84,7 +99,16 @@ exports[`resolves configuration file with --find-config-path file (write) 1`] =
exports[`resolves configuration from external files (stderr) 1`] = `""`;

exports[`resolves configuration from external files (stdout) 1`] = `
"console.log(\\"jest/__best-tests__/file.js should have semi\\");
"function f() {
console.log(\\"should have tab width 8\\")
}
function f() {
console.log(\\"should have space width 2\\")
}
function f() {
console.log(\\"should have space width 8\\")
}
console.log(\\"jest/__best-tests__/file.js should have semi\\");
console.log(\\"jest/Component.js should not have semi\\")
console.log(\\"jest/Component.test.js should have semi\\");
function js() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,22 @@ exports[`CLI overrides take lower precedence with --config-precedence file-overr
exports[`CLI overrides take precedence with --config-precedence cli-override (stderr) 1`] = `""`;
exports[`CLI overrides take precedence with --config-precedence cli-override (stdout) 1`] = `
"console.log(
"function f() {
console.log(
\\"should have tab width 8\\"
)
}
function f() {
console.log(
\\"should have space width 2\\"
)
}
function f() {
console.log(
\\"should have space width 8\\"
)
}
console.log(
\\"jest/__best-tests__/file.js should have semi\\"
);
console.log(
Expand Down Expand Up @@ -137,7 +152,22 @@ exports[`CLI overrides take precedence with --config-precedence cli-override (wr
exports[`CLI overrides take precedence without --config-precedence (stderr) 1`] = `""`;
exports[`CLI overrides take precedence without --config-precedence (stdout) 1`] = `
"console.log(
"function f() {
console.log(
\\"should have tab width 8\\"
)
}
function f() {
console.log(
\\"should have space width 2\\"
)
}
function f() {
console.log(
\\"should have space width 8\\"
)
}
console.log(
\\"jest/__best-tests__/file.js should have semi\\"
);
console.log(
Expand Down
92 changes: 92 additions & 0 deletions tests_integration/__tests__/config-resolution.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,98 @@ test("API resolveConfig.sync with file arg and extension override", () => {
});
});

test("API resolveConfig with file arg and .editorconfig", () => {
const file = path.resolve(
path.join(__dirname, "../cli/config/editorconfig/file.js")
);
return prettier.resolveConfig(file).then(result => {
expect(result).toMatchObject({
useTabs: true,
tabWidth: 8,
printWidth: 100
});
});
});

test("API resolveConfig.sync with file arg and .editorconfig", () => {
const file = path.resolve(
path.join(__dirname, "../cli/config/editorconfig/file.js")
);
expect(prettier.resolveConfig.sync(file)).toMatchObject({
useTabs: true,
tabWidth: 8,
printWidth: 100
});
});

test("API resolveConfig with nested file arg and .editorconfig", () => {
const file = path.resolve(
path.join(__dirname, "../cli/config/editorconfig/lib/file.js")
);
return prettier.resolveConfig(file).then(result => {
expect(result).toMatchObject({
useTabs: false,
tabWidth: 2,
printWidth: 100
});
});
});

test("API resolveConfig.sync with nested file arg and .editorconfig", () => {
const file = path.resolve(
path.join(__dirname, "../cli/config/editorconfig/lib/file.js")
);
expect(prettier.resolveConfig.sync(file)).toMatchObject({
useTabs: false,
tabWidth: 2,
printWidth: 100
});
});

test("API resolveConfig with nested file arg and .editorconfig and indent_size = tab", () => {
const file = path.resolve(
path.join(__dirname, "../cli/config/editorconfig/lib/indent_size=tab.js")
);
return prettier.resolveConfig(file).then(result => {
expect(result).toMatchObject({
useTabs: false,
tabWidth: 8,
printWidth: 100
});
});
});

test("API resolveConfig.sync with nested file arg and .editorconfig and indent_size = tab", () => {
const file = path.resolve(
path.join(__dirname, "../cli/config/editorconfig/lib/indent_size=tab.js")
);
expect(prettier.resolveConfig.sync(file)).toMatchObject({
useTabs: false,
tabWidth: 8,
printWidth: 100
});
});

test("API resolveConfig with missing file arg", () => {
const file = path.resolve(
path.join(__dirname, "../cli/config/editorconfig/file.shouldnotexist")
);
return prettier.resolveConfig(file).then(result => {
expect(result).toBeNull();
});
});

test("API resolveConfig.sync with missing file arg", () => {
const file = path.resolve(
path.join(__dirname, "../cli/config/editorconfig/file.shouldnotexist")
);
expect(prettier.resolveConfig.sync(file)).toBeNull();
});

test("API clearConfigCache", () => {
expect(() => prettier.clearConfigCache()).not.toThrowError();
});

test("API resolveConfig.sync overrides work with absolute paths", () => {
// Absolute path
const file = path.join(__dirname, "../cli/config/filepath/subfolder/file.js");
Expand Down
5 changes: 3 additions & 2 deletions tests_integration/cli/config/.prettierrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
semi: false

overrides:
- files: "*.js"
options:
semi: false
- files: "*.ts"
options:
semi: true
15 changes: 15 additions & 0 deletions tests_integration/cli/config/editorconfig/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
root = true

[*.js]
indent_style = tab
tab_width = 8
indent_size = 2 # overridden by tab_width since indent_style = tab
max_line_length = 100

# Indentation override for all JS under lib directory
[lib/**.js]
indent_style = space
indent_size = 2

[lib/indent_size=tab.js]
indent_size = tab
3 changes: 3 additions & 0 deletions tests_integration/cli/config/editorconfig/file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function f() {
console.log("should have tab width 8");
}
3 changes: 3 additions & 0 deletions tests_integration/cli/config/editorconfig/lib/file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function f() {
console.log("should have space width 2");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function f() {
console.log("should have space width 8");
}
4 changes: 4 additions & 0 deletions tests_integration/cli/config/js/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# This file should be overridden by prettier.config.js

[*]
tab_width = 1
7 changes: 7 additions & 0 deletions tests_integration/cli/config/package/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This file should be overridden by package.json

[*]
tab_width = 1

[*.ts]
tab_width = 1
Loading

0 comments on commit 8f58ca0

Please sign in to comment.