Skip to content

Commit

Permalink
Breaking: lint overrides files (fixes #10828, refs eslint/rfcs#20) (#…
Browse files Browse the repository at this point in the history
…12677)

* Breaking: lint `overrides` files (fixes #10828, refs eslint/rfcs#20)

* update docs

* sort for tests

See also: #12700 (comment)
  • Loading branch information
mysticatea authored and btmills committed Jan 17, 2020
1 parent e59d775 commit 1aa021d
Show file tree
Hide file tree
Showing 11 changed files with 403 additions and 53 deletions.
2 changes: 1 addition & 1 deletion conf/default-cli-options.js
Expand Up @@ -12,7 +12,7 @@ module.exports = {
useEslintrc: true,
envs: [],
globals: [],
extensions: [".js"],
extensions: null,
ignore: true,
ignorePath: void 0,
cache: false,
Expand Down
16 changes: 8 additions & 8 deletions docs/user-guide/command-line-interface.md
Expand Up @@ -127,19 +127,19 @@ Examples:

#### `--ext`

This option allows you to specify which file extensions ESLint will use when searching for JavaScript files in the directories you specify.
By default, it uses `.js` as the only file extension.
This option allows you to specify which file extensions ESLint will use when searching for target files in the directories you specify.
By default, ESLint lints `*.js` files and the files that match the `overrides` entries of your configuration.

Examples:

# Use only .js2 extension
eslint . --ext .js2
# Use only .ts extension
eslint . --ext .ts

# Use both .js and .js2
eslint . --ext .js --ext .js2
# Use both .js and .ts
eslint . --ext .js --ext .ts

# Also use both .js and .js2
eslint . --ext .js,.js2
# Also use both .js and .ts
eslint . --ext .js,.ts

**Note:** `--ext` is only used when the arguments are directories. If you use glob patterns or file names, then `--ext` is ignored.

Expand Down
12 changes: 7 additions & 5 deletions docs/user-guide/configuring.md
Expand Up @@ -125,7 +125,7 @@ Processors may make named code blocks such as `0.js` and `1.js`. ESLint handles
}
```

ESLint checks the file extension of named code blocks then ignores those if [`--ext` CLI option](../user-guide/command-line-interface.md#--ext) didn't include the file extension. Be sure to specify the `--ext` option if you wanted to lint named code blocks other than `*.js`.
ESLint checks the file path of named code blocks then ignores those if any `overrides` entry didn't match the file path. Be sure to make `overrides` entry if you wanted to lint named code blocks other than `*.js`.

## Specifying Environments

Expand Down Expand Up @@ -990,6 +990,12 @@ In your `.eslintrc.json`:
}
```

### Specifying Target Files to Lint

If you specified directories with CLI (e.g., `eslint lib`), ESLint searches target files in the directory to lint. The target files are `*.js` or the files that match any of `overrides` entries (but exclude entries that are any of `files` end with `*`).

If you specified the [`--ext`](./command-line-interface#ext) command line option along with directories, the target files are only the files that have specified file extensions regardless of `overrides` entries.

## Comments in Configuration Files

Both the JSON and YAML configuration file formats support comments (`package.json` files should not include them). You can use JavaScript-style comments or YAML-style comments in either type of file and ESLint will safely ignore them. This allows your configuration files to be more human-friendly. For example:
Expand All @@ -1007,10 +1013,6 @@ Both the JSON and YAML configuration file formats support comments (`package.jso
}
```

## Specifying File extensions to Lint

Currently the sole method for telling ESLint which file extensions to lint is by specifying a comma separated list of extensions using the [`--ext`](./command-line-interface#ext) command line option. Note this flag only takes effect in conjunction with directories, and will be ignored if used with filenames or glob patterns.

## Ignoring Files and Directories

### `ignorePatterns` in config files
Expand Down
1 change: 1 addition & 0 deletions lib/cli-engine/cascading-config-array-factory.js
Expand Up @@ -106,6 +106,7 @@ function createBaseConfigArray({
*/
if (rulePaths && rulePaths.length > 0) {
baseConfigArray.push({
type: "config",
name: "--rulesdir",
filePath: "",
plugins: {
Expand Down
16 changes: 7 additions & 9 deletions lib/cli-engine/cli-engine.js
Expand Up @@ -57,7 +57,7 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]);
* @property {string} configFile The configuration file to use.
* @property {string} cwd The value to use for the current working directory.
* @property {string[]} envs An array of environments to load.
* @property {string[]} extensions An array of file extensions to check.
* @property {string[]|null} extensions An array of file extensions to check.
* @property {boolean|Function} fix Execute in autofix mode. If a function, should return a boolean.
* @property {string[]} fixTypes Array of rule types to apply fixes for.
* @property {string[]} globals An array of global variables to declare.
Expand Down Expand Up @@ -201,7 +201,7 @@ function calculateStatsPerRun(results) {
* @param {boolean} config.fix If `true` then it does fix.
* @param {boolean} config.allowInlineConfig If `true` then it uses directive comments.
* @param {boolean} config.reportUnusedDisableDirectives If `true` then it reports unused `eslint-disable` comments.
* @param {RegExp} config.extensionRegExp The `RegExp` object that tests if a file path has the allowed file extensions.
* @param {FileEnumerator} config.fileEnumerator The file enumerator to check if a path is a target or not.
* @param {Linter} config.linter The linter instance to verify.
* @returns {LintResult} The result of linting.
* @private
Expand All @@ -214,7 +214,7 @@ function verifyText({
fix,
allowInlineConfig,
reportUnusedDisableDirectives,
extensionRegExp,
fileEnumerator,
linter
}) {
const filePath = providedFilePath || "<text>";
Expand All @@ -238,13 +238,11 @@ function verifyText({

/**
* Check if the linter should adopt a given code block or not.
* Currently, the linter adopts code blocks if the name matches `--ext` option.
* In the future, `overrides` in the configuration would affect the adoption (https://github.com/eslint/rfcs/pull/20).
* @param {string} blockFilename The virtual filename of a code block.
* @returns {boolean} `true` if the linter should adopt the code block.
*/
filterCodeBlock(blockFilename) {
return extensionRegExp.test(blockFilename);
return fileEnumerator.isTargetPath(blockFilename);
}
}
);
Expand Down Expand Up @@ -704,7 +702,7 @@ class CLIEngine {
return patterns.filter(Boolean);
}

const extensions = options.extensions.map(ext => ext.replace(/^\./u, ""));
const extensions = (options.extensions || [".js"]).map(ext => ext.replace(/^\./u, ""));
const dirSuffix = `/**/*.{${extensions.join(",")}}`;

return patterns.filter(Boolean).map(pathname => {
Expand Down Expand Up @@ -803,7 +801,7 @@ class CLIEngine {
fix,
allowInlineConfig,
reportUnusedDisableDirectives,
extensionRegExp: fileEnumerator.extensionRegExp,
fileEnumerator,
linter
});

Expand Down Expand Up @@ -891,7 +889,7 @@ class CLIEngine {
fix,
allowInlineConfig,
reportUnusedDisableDirectives,
extensionRegExp: fileEnumerator.extensionRegExp,
fileEnumerator,
linter
}));
}
Expand Down
5 changes: 4 additions & 1 deletion lib/cli-engine/config-array-factory.js
Expand Up @@ -565,7 +565,7 @@ class ConfigArrayFactory {
*/
*_normalizeESLintIgnoreData(ignorePatterns, filePath, name) {
const elements = this._normalizeObjectConfigData(
{ ignorePatterns },
{ type: "ignore", ignorePatterns },
filePath,
name
);
Expand Down Expand Up @@ -667,6 +667,7 @@ class ConfigArrayFactory {
root,
rules,
settings,
type = "config",
overrides: overrideList = []
},
filePath,
Expand Down Expand Up @@ -698,6 +699,7 @@ class ConfigArrayFactory {
yield {

// Debug information.
type,
name,
filePath,

Expand Down Expand Up @@ -1047,6 +1049,7 @@ class ConfigArrayFactory {
if (processorId.startsWith(".")) {
yield* this._normalizeObjectConfigData(
{
type: "implicit-processor",
files: [`*${processorId}`],
processor: `${pluginId}/${processorId}`
},
Expand Down
19 changes: 19 additions & 0 deletions lib/cli-engine/config-array/config-array.js
Expand Up @@ -454,6 +454,25 @@ class ConfigArray extends Array {

return cache.get(cacheKey);
}

/**
* Check if a given path is an additional lint target.
* @param {string} filePath The absolute path to the target file.
* @returns {boolean} `true` if the file is an additional lint target.
*/
isAdditionalTargetPath(filePath) {
for (const { criteria, type } of this) {
if (
type === "config" &&
criteria &&
!criteria.endsWithWildcard &&
criteria.test(filePath)
) {
return true;
}
}
return false;
}
}

const exportObject = {
Expand Down
44 changes: 36 additions & 8 deletions lib/cli-engine/config-array/override-tester.js
Expand Up @@ -96,14 +96,22 @@ class OverrideTester {
static create(files, excludedFiles, basePath) {
const includePatterns = normalizePatterns(files);
const excludePatterns = normalizePatterns(excludedFiles);
const allPatterns = includePatterns.concat(excludePatterns);
let endsWithWildcard = false;

if (allPatterns.length === 0) {
if (includePatterns.length === 0) {
return null;
}

// Rejects absolute paths or relative paths to parents.
for (const pattern of allPatterns) {
for (const pattern of includePatterns) {
if (path.isAbsolute(pattern) || pattern.includes("..")) {
throw new Error(`Invalid override pattern (expected relative path not containing '..'): ${pattern}`);
}
if (pattern.endsWith("*")) {
endsWithWildcard = true;
}
}
for (const pattern of excludePatterns) {
if (path.isAbsolute(pattern) || pattern.includes("..")) {
throw new Error(`Invalid override pattern (expected relative path not containing '..'): ${pattern}`);
}
Expand All @@ -112,7 +120,11 @@ class OverrideTester {
const includes = toMatcher(includePatterns);
const excludes = toMatcher(excludePatterns);

return new OverrideTester([{ includes, excludes }], basePath);
return new OverrideTester(
[{ includes, excludes }],
basePath,
endsWithWildcard
);
}

/**
Expand All @@ -125,28 +137,44 @@ class OverrideTester {
*/
static and(a, b) {
if (!b) {
return a && new OverrideTester(a.patterns, a.basePath);
return a && new OverrideTester(
a.patterns,
a.basePath,
a.endsWithWildcard
);
}
if (!a) {
return new OverrideTester(b.patterns, b.basePath);
return new OverrideTester(
b.patterns,
b.basePath,
b.endsWithWildcard
);
}

assert.strictEqual(a.basePath, b.basePath);
return new OverrideTester(a.patterns.concat(b.patterns), a.basePath);
return new OverrideTester(
a.patterns.concat(b.patterns),
a.basePath,
a.endsWithWildcard || b.endsWithWildcard
);
}

/**
* Initialize this instance.
* @param {Pattern[]} patterns The matchers.
* @param {string} basePath The base path.
* @param {boolean} endsWithWildcard If `true` then a pattern ends with `*`.
*/
constructor(patterns, basePath) {
constructor(patterns, basePath, endsWithWildcard = false) {

/** @type {Pattern[]} */
this.patterns = patterns;

/** @type {string} */
this.basePath = basePath;

/** @type {boolean} */
this.endsWithWildcard = endsWithWildcard;
}

/**
Expand Down

0 comments on commit 1aa021d

Please sign in to comment.