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

New: support specifying extensions in the config (fixes #10828) #12555

Closed
wants to merge 4 commits into from
Closed
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
1 change: 1 addition & 0 deletions conf/config-schema.js
Expand Up @@ -8,6 +8,7 @@
const baseConfigProperties = {
env: { type: "object" },
extends: { $ref: "#/definitions/stringOrStrings" },
extensions: { type: "array", items: "string" },
globals: { type: "object" },
overrides: {
type: "array",
Expand Down
21 changes: 21 additions & 0 deletions docs/user-guide/configuring.md
Expand Up @@ -10,6 +10,7 @@ ESLint is designed to be completely configurable, meaning you can turn off every
There are several pieces of information that can be configured:

* **Environments** - which environments your script is designed to run in. Each environment brings with it a certain set of predefined global variables.
* **Extensions** - files with specified extensions will be linted.
* **Globals** - the additional global variables your script accesses during execution.
* **Rules** - which rules are enabled and at what error level.

Expand Down Expand Up @@ -129,6 +130,26 @@ 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`.

## Specifying Extensions

By default, ESLint only lints files whose names end with `.js`. If you want to let ESLint lint files whose names end with other extensions, you can specify it by passing an argument at CLI with `--ext`.

However, sometimes you may want this behavior can be defined in configuration file, which will be able to be shared so you don't need to specify it at CLI.

The ignoring configuration (`.eslintignore`) precedences this configuration. If `.eslintignore` contains the additional target files, those files will still be ignored.

Adding `extensions` property in configuration file will let ESLint lint files whose names end with those extensions without specifying it at CLI:

```json
{
"extensions": ["ts"]
}
```

Note that you don't need to add prefix to extension with dot (`.`).

With the configuration above, files whose names end with `.js` or `.ts` will be linted.

## Specifying Environments

An environment defines global variables that are predefined. The available environments are:
Expand Down
7 changes: 5 additions & 2 deletions lib/cli-engine/cli-engine.js
Expand Up @@ -789,7 +789,7 @@ class CLIEngine {
fix,
allowInlineConfig,
reportUnusedDisableDirectives,
extensionRegExp: fileEnumerator.extensionRegExp,
extensionRegExp: fileEnumerator.getExtensionRegExp(config, filePath),
linter
});

Expand Down Expand Up @@ -878,7 +878,10 @@ class CLIEngine {
fix,
allowInlineConfig,
reportUnusedDisableDirectives,
extensionRegExp: fileEnumerator.extensionRegExp,
extensionRegExp: fileEnumerator.getExtensionRegExp(
config,
resolvedFilename || path.join(cwd, "__placeholder__.js")
),
linter
}));
}
Expand Down
2 changes: 2 additions & 0 deletions lib/cli-engine/config-array-factory.js
Expand Up @@ -525,6 +525,7 @@ class ConfigArrayFactory {
{
env,
extends: extend,
extensions,
globals,
noInlineConfig,
parser: parserName,
Expand Down Expand Up @@ -568,6 +569,7 @@ class ConfigArrayFactory {
// Config data.
criteria: null,
env,
extensions,
globals,
noInlineConfig,
parser,
Expand Down
2 changes: 2 additions & 0 deletions lib/cli-engine/config-array/config-array.js
Expand Up @@ -53,6 +53,7 @@ const { ExtractedConfig } = require("./extracted-config");
* @property {string} filePath The path to the source file of this config element.
* @property {InstanceType<OverrideTester>|null} criteria The tester for the `files` and `excludedFiles` of this config element.
* @property {Record<string, boolean>|undefined} env The environment settings.
* @property {string[]|undefined} extensions The extensions to match files for directory patterns.
* @property {Record<string, GlobalConf>|undefined} globals The global variable settings.
* @property {boolean|undefined} noInlineConfig The flag that disables directive comments.
* @property {DependentParser|undefined} parser The parser loader.
Expand Down Expand Up @@ -262,6 +263,7 @@ function createConfig(instance, indices) {

// Merge others.
mergeWithoutOverwrite(config.env, element.env);
config.extensions = config.extensions.concat(element.extensions || []);
mergeWithoutOverwrite(config.globals, element.globals);
mergeWithoutOverwrite(config.parserOptions, element.parserOptions);
mergeWithoutOverwrite(config.settings, element.settings);
Expand Down
6 changes: 6 additions & 0 deletions lib/cli-engine/config-array/extracted-config.js
Expand Up @@ -41,6 +41,12 @@ class ExtractedConfig {
*/
this.env = {};

/**
* Extensions to match files for directory patterns.
* @type {string[]}
*/
this.extensions = [];

/**
* Global variables.
* @type {Record<string, GlobalConf>}
Expand Down
40 changes: 24 additions & 16 deletions lib/cli-engine/file-enumerator.js
Expand Up @@ -89,7 +89,7 @@ const IGNORED = 2;
* @typedef {Object} FileEnumeratorInternalSlots
* @property {CascadingConfigArrayFactory} configArrayFactory The factory for config arrays.
* @property {string} cwd The base directory to start lookup.
* @property {RegExp} extensionRegExp The RegExp to test if a string ends with specific file extensions.
* @property {string[]} baseExtensions The array to check if a string ends with specific file extensions.
* @property {boolean} globInputPaths Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file.
* @property {boolean} ignoreFlag The flag to check ignored files.
* @property {IgnoredPaths} ignoredPathsWithDotfiles The ignored paths but don't include dot files.
Expand Down Expand Up @@ -198,17 +198,7 @@ class FileEnumerator {
internalSlotsMap.set(this, {
configArrayFactory,
cwd,
extensionRegExp: new RegExp(
`.\\.(?:${extensions
.map(ext => escapeRegExp(
ext.startsWith(".")
? ext.slice(1)
: ext
))
.join("|")
})$`,
"u"
),
baseExtensions: extensions,
globInputPaths,
ignoreFlag: ignore,
ignoredPaths,
Expand All @@ -221,10 +211,27 @@ class FileEnumerator {

/**
* The `RegExp` object that tests if a file path has the allowed file extensions.
* @type {RegExp}
* @param {ConfigArray} [configArray] ESLint configuration.
* @param {string} [filePath] The absolute path to the target file.
* @returns {RegExp} The RegExp to test if a string ends with specific file extensions.
*/
get extensionRegExp() {
return internalSlotsMap.get(this).extensionRegExp;
getExtensionRegExp(configArray, filePath) {
let extensions = internalSlotsMap.get(this).baseExtensions;

if (configArray) {
const config = configArray.extractConfig(filePath);

extensions = extensions.concat(config.extensions || []);
}

// Use set to deduplicate.
const exts = new Set(
extensions
.filter(Boolean)
.map(ext => escapeRegExp(ext.startsWith(".") ? ext.slice(1) : ext))
);

return new RegExp(`.\\.${Array.from(exts).join("|")}$`, "u");
}

/**
Expand Down Expand Up @@ -386,7 +393,7 @@ class FileEnumerator {
return;
}
debug(`Enter the directory: ${directoryPath}`);
const { configArrayFactory, extensionRegExp } = internalSlotsMap.get(this);
const { configArrayFactory } = internalSlotsMap.get(this);

/** @type {ConfigArray|null} */
let config = null;
Expand All @@ -401,6 +408,7 @@ class FileEnumerator {
if (!config) {
config = configArrayFactory.getConfigArrayForFile(filePath);
}
const extensionRegExp = this.getExtensionRegExp(config, filePath);
const ignored = this._isIgnoredFile(filePath, options);
const flag = ignored ? IGNORED_SILENTLY : NONE;
const matched = options.selector
Expand Down
1 change: 1 addition & 0 deletions lib/shared/types.js
Expand Up @@ -29,6 +29,7 @@ module.exports = {};
* @typedef {Object} ConfigData
* @property {Record<string, boolean>} [env] The environment settings.
* @property {string | string[]} [extends] The path to other config files or the package name of shareable configs.
* @property {string[]} [extensions] The array of filename extensions that should be checked for code.
* @property {Record<string, GlobalConf>} [globals] The global variable settings.
* @property {boolean} [noInlineConfig] The flag that disables directive comments.
* @property {OverrideConfigData[]} [overrides] The override settings per kind of files.
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/configurations/extensions.json
@@ -0,0 +1,3 @@
{
"extensions": ["ts"]
}
1 change: 1 addition & 0 deletions tests/fixtures/extensions/extensions-js.js
@@ -0,0 +1 @@
function fn() {}
1 change: 1 addition & 0 deletions tests/fixtures/extensions/extensions-ts.ts
@@ -0,0 +1 @@
function fn() {}
56 changes: 55 additions & 1 deletion tests/lib/cli-engine/cli-engine.js
Expand Up @@ -633,7 +633,6 @@ describe("CLIEngine", () => {
assert.strictEqual(report.results[0].source, "var foo = 'bar'");
});


it("should not return a `source` property when no errors or warnings are present", () => {
engine = new CLIEngine({
useEslintrc: false,
Expand Down Expand Up @@ -753,6 +752,7 @@ describe("CLIEngine", () => {
assert.strictEqual(report.messages[0].message, "OK");
});
});

it("should warn when deprecated rules are found in a config", () => {
engine = new CLIEngine({
cwd: originalDir,
Expand Down Expand Up @@ -1157,6 +1157,60 @@ describe("CLIEngine", () => {
assert.strictEqual(report.results[0].messages.length, 0);
});

it("should process files with specifying extensions in configuration file", () => {

engine = new CLIEngine({
cwd: path.join(fixtureDir, "extensions"),
configFile: getFixturePath("configurations", "extensions.json")
});

let report = engine.executeOnFiles(["."]);

assert.lengthOf(report.results, 2);
assert.lengthOf(report.results[0].messages, 0);
assert.lengthOf(report.results[1].messages, 0);

engine = new CLIEngine({
cwd: path.join(fixtureDir, "extensions"),
configFile: getFixturePath("configurations", "es6.json")
});

report = engine.executeOnFiles(["."]);

assert.lengthOf(report.results, 1);
assert.lengthOf(report.results[0].messages, 0);
});

it("should ignoring configuration precedences extensions", () => {

engine = new CLIEngine({
cwd: path.join(fixtureDir, "extensions"),
configFile: getFixturePath("configurations", "extensions.json"),
ignorePattern: "*.ts"
});

const report = engine.executeOnFiles(["."]);

assert.lengthOf(report.results, 1);
assert.lengthOf(report.results[0].messages, 0);
});

it("should treat glob patterns over extensions defined in configuration", () => {

engine = new CLIEngine({
cwd: path.join(fixtureDir, "extensions"),

// This config doesn't specify about extensions.
configFile: getFixturePath("configurations", "es6.json")
});

const report = engine.executeOnFiles(["*.*"]);

assert.lengthOf(report.results, 2);
assert.lengthOf(report.results[0].messages, 0);
assert.lengthOf(report.results[1].messages, 0);
});

it("should return zero messages when given a config with environment set to browser", () => {

engine = new CLIEngine({
Expand Down
19 changes: 19 additions & 0 deletions tests/lib/cli-engine/config-array-factory.js
Expand Up @@ -29,6 +29,7 @@ function assertConfigArrayElement(actual, providedExpected) {
filePath: "",
criteria: null,
env: void 0,
extensions: void 0,
globals: void 0,
noInlineConfig: void 0,
parser: void 0,
Expand All @@ -54,6 +55,7 @@ function assertConfigArrayElement(actual, providedExpected) {
function assertConfig(actual, providedExpected) {
const expected = {
env: {},
extensions: [],
globals: {},
noInlineConfig: void 0,
parser: null,
Expand Down Expand Up @@ -461,6 +463,23 @@ describe("ConfigArrayFactory", () => {
});
});

describe("if the config data had 'extensions' property, the returned value", () => {
const extensions = ["ts"];
let configArray;

beforeEach(() => {
configArray = create({ extensions });
});

it("should have an element.", () => {
assert.strictEqual(configArray.length, 1);
});

it("should have the 'extensions' value in the element.", () => {
assertConfigArrayElement(configArray[0], { extensions });
});
});

describe("if the config data had 'globals' property, the returned value", () => {
const globals = { window: "readonly" };
let configArray;
Expand Down
7 changes: 7 additions & 0 deletions tests/lib/cli-engine/config-array/config-array.js
Expand Up @@ -424,6 +424,7 @@ describe("ConfigArray", () => {
assert.deepStrictEqual(result, {
configNameOfNoInlineConfig: "",
env: {},
extensions: [],
globals: {},
noInlineConfig: void 0,
parser: null,
Expand Down Expand Up @@ -456,6 +457,7 @@ describe("ConfigArray", () => {
assert.deepStrictEqual(result, {
configNameOfNoInlineConfig: "",
env: {},
extensions: [],
globals: {},
noInlineConfig: void 0,
parser: null,
Expand Down Expand Up @@ -550,6 +552,7 @@ describe("ConfigArray", () => {
ecmaFeatures: { jsx: true }
},
env: { browser: true },
extensions: ["ts"],
globals: { foo: false }
},
{
Expand All @@ -563,6 +566,7 @@ describe("ConfigArray", () => {
ecmaFeatures: { globalReturn: true }
},
env: { browser: false },
extensions: ["vue"],
globals: { foo: true }
}
];
Expand All @@ -582,6 +586,7 @@ describe("ConfigArray", () => {
env: {
browser: false
},
extensions: ["vue", "ts"],
globals: {
foo: true
},
Expand Down Expand Up @@ -623,6 +628,7 @@ describe("ConfigArray", () => {
ecmaFeatures: { jsx: true }
},
env: { browser: true },
extensions: ["ts"],
globals: { foo: false }
});
assert.deepStrictEqual(config[1], {
Expand All @@ -636,6 +642,7 @@ describe("ConfigArray", () => {
ecmaFeatures: { globalReturn: true }
},
env: { browser: false },
extensions: ["vue"],
globals: { foo: true }
});
});
Expand Down
4 changes: 4 additions & 0 deletions tests/lib/cli-engine/config-array/extracted-config.js
Expand Up @@ -21,6 +21,10 @@ describe("'ExtractedConfig' class", () => {
assert.deepStrictEqual(config.env, {});
});

it("should have 'extensions' property.", () => {
assert.deepStrictEqual(config.extensions, []);
});

it("should have 'globals' property.", () => {
assert.deepStrictEqual(config.globals, {});
});
Expand Down