Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 5 commits into from Jan 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -964,6 +964,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 @@ -981,10 +987,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 @@ -105,6 +105,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 @@ -542,7 +542,7 @@ class ConfigArrayFactory {
*/
*_normalizeESLintIgnoreData(ignorePatterns, filePath, name) {
const elements = this._normalizeObjectConfigData(
{ ignorePatterns },
{ type: "ignore", ignorePatterns },
filePath,
name
);
Expand Down Expand Up @@ -644,6 +644,7 @@ class ConfigArrayFactory {
root,
rules,
settings,
type = "config",
overrides: overrideList = []
},
filePath,
Expand Down Expand Up @@ -675,6 +676,7 @@ class ConfigArrayFactory {
yield {

// Debug information.
type,
name,
filePath,

Expand Down Expand Up @@ -1024,6 +1026,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