Skip to content

Commit

Permalink
feat: support flat config in v8
Browse files Browse the repository at this point in the history
  • Loading branch information
lotmek authored and ljharb committed Oct 3, 2024
1 parent 0f977ce commit dfd671e
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 38 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Then run it with: `$ npm run --silent eslint-find-option-rules` (the `--silent`

```
available options are -a|--all-available, -c|--current, -d|--deprecated, -p|--plugin, -u|--unused
available flags are -n|--no-error, --no-core, -i/--include deprecated, and --ext .js
available flags are -n|--no-error, --no-core, -i/--include deprecated, --ext .js, and --flatConfig
```

By default it will error out only for `-d|--deprecated` and `-u|--unused`,
Expand All @@ -58,6 +58,8 @@ By default, deprecated rules will be omitted from the output of `-a|--all-availa

By default, rules will be searched for files having `.js` extension. If you want to find rules using another extension (`.json` for example), use the `--ext .json` flag (or `--ext .js --ext .json` if you need multiple extensions).

By default, ESLint will handle configs in Legacy mode. If you want to handle Flat config files, you need to add the `--flatConfig` flag.

**NOTE:** Deprecated rules are found by looking at the metadata of the rule definition. All core rules and many plugin rules use this flag to indicate deprecated rules. But if you find a plugin that does not mark their rules as deprecated in the rule metadata, please file a pull request with that project.

### Specify a file
Expand Down
7 changes: 5 additions & 2 deletions src/bin/find.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const options = {
n: [],
error: ['error'],
core: ['core'],
verbose: ['verbose', 'v']
verbose: ['verbose', 'v'],
flatConfig: ['flatConfig']
};
const optionsThatError = ['getUnusedRules', 'getDeprecatedRules'];

Expand All @@ -28,6 +29,7 @@ const argv = require('yargs')
})
.default('error', true)
.default('core', true)
.default('flatConfig', false)
.argv;
const getRuleURI = require('eslint-rule-documentation');
const getRuleFinder = require('../lib/rule-finder');
Expand All @@ -39,7 +41,8 @@ const specifiedFile = argv._[0];
const finderOptions = {
omitCore: !argv.core,
includeDeprecated: argv.include === 'deprecated',
ext: argv.ext
ext: argv.ext,
useFlatConfig: argv.flatConfig
};
const ruleFinder = await getRuleFinder(specifiedFile, finderOptions);
const errorOut = argv.error && !argv.n;
Expand Down
76 changes: 53 additions & 23 deletions src/lib/rule-finder.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
const path = require('path');

const { ESLint, Linter } = require('eslint');
const { ESLint, Linter, loadESLint } = require('eslint');
const glob = require('glob');
const difference = require('./array-diff');
const getSortedRules = require('./sort-rules');
const normalizePluginName = require('./normalize-plugin-name');

let builtinRules;
try {
builtinRules = require('eslint/use-at-your-own-risk').builtinRules;
} catch (e) {}

async function _loadEslint(options, useFlatConfig) {
if (!useFlatConfig) {
// Ignore any config applicable depending on the location on the filesystem
options.useEslintrc = false;
} else if (!loadESLint) {
throw 'This version of ESLint does not support flat config.';
}

return loadESLint
? new (await loadESLint({ useFlatConfig }))(options)
: new ESLint(options);
}

function _getConfigFile(specifiedFile) {
if (specifiedFile) {
if (path.isAbsolute(specifiedFile)) {
Expand All @@ -17,25 +35,27 @@ function _getConfigFile(specifiedFile) {
return require(path.join(process.cwd(), 'package.json')).main;
}

async function _getConfigs(overrideConfigFile, files) {
const esLint = new ESLint({
// Ignore any config applicable depending on the location on the filesystem
useEslintrc: false,
async function _getConfigs(overrideConfigFile, files, useFlatConfig) {
const esLint = await _loadEslint({
// Point to the particular config
overrideConfigFile
});
}, useFlatConfig);

const configs = files.map(async filePath => (
await esLint.isPathIgnored(filePath) ? false : esLint.calculateConfigForFile(filePath)
));
return new Set((await Promise.all(configs)).filter(Boolean));
}

async function _getConfig(configFile, files) {
return Array.from(await _getConfigs(configFile, files)).reduce((prev, item) => {
async function _getConfig(configFile, files, useFlatConfig) {
return Array.from(await _getConfigs(configFile, files, useFlatConfig)).reduce((prev, item) => {
const plugins = useFlatConfig
? Object.assign({}, prev.plugins, item.plugins)
: [...new Set([].concat(prev.plugins || [], item.plugins || []))]

return Object.assign(prev, item, {
rules: Object.assign({}, prev.rules, item.rules),
plugins: [...new Set([].concat(prev.plugins || [], item.plugins || []))]
plugins
});
}, {});
}
Expand All @@ -52,26 +72,36 @@ function _notDeprecated(rule) {
return !_isDeprecated(rule);
}

function _getPluginRules(config) {
function _getPluginRules(config, useFlatConfig) {
const pluginRules = new Map();
const plugins = config.plugins;
/* istanbul ignore else */
if (plugins) {
plugins.forEach(plugin => {
const normalized = normalizePluginName(plugin);
const pluginConfig = require(normalized.module);
const rules = pluginConfig.rules === undefined ? {} : pluginConfig.rules;

Object.keys(rules).forEach(ruleName =>
pluginRules.set(`${normalized.prefix}/${ruleName}`, rules[ruleName])
);
});
if (useFlatConfig) {
Object.entries(config.plugins)
.filter(([, { rules }]) => rules)
.forEach(([pluginName, { rules }]) => {
Object.keys(rules).forEach(ruleName =>
pluginRules.set(`${pluginName}/${ruleName}`, rules[ruleName])
);
});
} else {
plugins.forEach(plugin => {
const normalized = normalizePluginName(plugin);
const pluginConfig = require(normalized.module);
if (pluginConfig.rules) {
Object.keys(pluginConfig.rules).forEach(ruleName =>
pluginRules.set(`${normalized.prefix}/${ruleName}`, pluginConfig.rules[ruleName])
);
}
});
}
}
return pluginRules;
}

function _getCoreRules() {
return new Linter().getRules();
return builtinRules || new Linter().getRules();
}

function _filterRuleNames(ruleNames, rules, predicate) {
Expand All @@ -94,13 +124,13 @@ function _createExtensionRegExp(extensions) {
return new RegExp(`.\\.(?:${normalizedExts.join("|")})$`);
}

function RuleFinder(config, {omitCore, includeDeprecated}) {
function RuleFinder(config, {omitCore, includeDeprecated, useFlatConfig}) {
let currentRuleNames = _getCurrentNamesRules(config);
if (omitCore) {
currentRuleNames = currentRuleNames.filter(_isNotCore);
}

const pluginRules = _getPluginRules(config); // eslint-disable-line vars-on-top
const pluginRules = _getPluginRules(config, useFlatConfig); // eslint-disable-line vars-on-top
const coreRules = _getCoreRules();
const allRules = omitCore ? pluginRules : new Map([...coreRules, ...pluginRules]);

Expand Down Expand Up @@ -140,7 +170,7 @@ async function createRuleFinder(specifiedFile, options) {
const files = glob.sync(`**/*`, {dot: true, matchBase: true})
.filter(file => extensionRegExp.test(file));

const config = await _getConfig(configFile, files);
const config = await _getConfig(configFile, files, options.useFlatConfig);

return new RuleFinder(config, options);
}
Expand Down
29 changes: 29 additions & 0 deletions test/fixtures/post-v8/eslint-flat-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module.exports = [
{
files: ["**/*.js"],
plugins: {
plugin: {
rules: {
"foo-rule": {},
"old-plugin-rule": { meta: { deprecated: true } },
"bar-rule": {},
},
},
},
rules: {
"foo-rule": [2],
"plugin/foo-rule": [2],
},
},
{
files: ["**/*.json"],
plugins: {
jsonPlugin: {
rules: { "foo-rule": {} },
},
},
rules: {
"jsonPlugin/foo-rule": [2],
},
},
];
Loading

0 comments on commit dfd671e

Please sign in to comment.