xo 0.42.0 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +3 -2
- package/config/plugins.cjs +20 -18
- package/index.js +80 -136
- package/lib/constants.js +0 -8
- package/lib/options-manager.js +101 -116
- package/lib/report.js +84 -0
- package/package.json +34 -41
- package/readme.md +20 -11
package/cli.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import process from 'node:process';
|
|
2
3
|
import getStdin from 'get-stdin';
|
|
3
4
|
import meow from 'meow';
|
|
4
5
|
import formatterPretty from 'eslint-formatter-pretty';
|
|
@@ -172,8 +173,8 @@ if (options.nodeVersion) {
|
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
(async () => {
|
|
175
|
-
if (options.printConfig) {
|
|
176
|
-
if (input.length > 0) {
|
|
176
|
+
if (typeof options.printConfig === 'string') {
|
|
177
|
+
if (input.length > 0 || options.printConfig === '') {
|
|
177
178
|
console.error('The `--print-config` flag must be used with exactly one filename');
|
|
178
179
|
process.exit(1);
|
|
179
180
|
}
|
package/config/plugins.cjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
// Repeated here from eslint-config-xo in case some plugins set something different
|
|
5
5
|
parserOptions: {
|
|
6
|
-
ecmaVersion:
|
|
6
|
+
ecmaVersion: 'latest',
|
|
7
7
|
sourceType: 'module',
|
|
8
8
|
ecmaFeatures: {
|
|
9
9
|
jsx: true,
|
|
@@ -14,7 +14,8 @@ module.exports = {
|
|
|
14
14
|
'no-use-extend-native',
|
|
15
15
|
'ava',
|
|
16
16
|
'unicorn',
|
|
17
|
-
'
|
|
17
|
+
// Disabled as the plugin doesn't support ESLint 8 yet.
|
|
18
|
+
// 'promise',
|
|
18
19
|
'import',
|
|
19
20
|
'node',
|
|
20
21
|
'eslint-comments',
|
|
@@ -173,17 +174,19 @@ module.exports = {
|
|
|
173
174
|
// TODO: Temporarily disabled as the rule is buggy.
|
|
174
175
|
'function-call-argument-newline': 'off',
|
|
175
176
|
|
|
176
|
-
'
|
|
177
|
-
'promise/
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
'promise/no-
|
|
185
|
-
'promise/
|
|
186
|
-
'promise/
|
|
177
|
+
// Disabled as the plugin doesn't support ESLint 8 yet.
|
|
178
|
+
// 'promise/param-names': 'error',
|
|
179
|
+
// 'promise/no-return-wrap': [
|
|
180
|
+
// 'error',
|
|
181
|
+
// {
|
|
182
|
+
// allowReject: true,
|
|
183
|
+
// },
|
|
184
|
+
// ],
|
|
185
|
+
// 'promise/no-new-statics': 'error',
|
|
186
|
+
// 'promise/no-return-in-finally': 'error',
|
|
187
|
+
// 'promise/valid-params': 'error',
|
|
188
|
+
// 'promise/prefer-await-to-then': 'error',
|
|
189
|
+
|
|
187
190
|
'import/default': 'error',
|
|
188
191
|
'import/export': 'error',
|
|
189
192
|
'import/extensions': [
|
|
@@ -200,9 +203,8 @@ module.exports = {
|
|
|
200
203
|
],
|
|
201
204
|
'import/first': 'error',
|
|
202
205
|
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
// 'import/named': 'error',
|
|
206
|
+
// Enabled, but disabled on TypeScript (https://github.com/xojs/xo/issues/576)
|
|
207
|
+
'import/named': 'error',
|
|
206
208
|
|
|
207
209
|
'import/namespace': [
|
|
208
210
|
'error',
|
|
@@ -322,7 +324,7 @@ module.exports = {
|
|
|
322
324
|
'node/no-deprecated-api': 'error',
|
|
323
325
|
'node/prefer-global/buffer': [
|
|
324
326
|
'error',
|
|
325
|
-
'
|
|
327
|
+
'never',
|
|
326
328
|
],
|
|
327
329
|
'node/prefer-global/console': [
|
|
328
330
|
'error',
|
|
@@ -330,7 +332,7 @@ module.exports = {
|
|
|
330
332
|
],
|
|
331
333
|
'node/prefer-global/process': [
|
|
332
334
|
'error',
|
|
333
|
-
'
|
|
335
|
+
'never',
|
|
334
336
|
],
|
|
335
337
|
'node/prefer-global/text-decoder': [
|
|
336
338
|
'error',
|
package/index.js
CHANGED
|
@@ -1,168 +1,112 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import {ESLint} from 'eslint';
|
|
3
|
-
import globby from 'globby';
|
|
4
|
-
import {isEqual} from 'lodash-es';
|
|
3
|
+
import {globby, isGitIgnoredSync} from 'globby';
|
|
4
|
+
import {isEqual, groupBy} from 'lodash-es';
|
|
5
5
|
import micromatch from 'micromatch';
|
|
6
6
|
import arrify from 'arrify';
|
|
7
|
-
import pReduce from 'p-reduce';
|
|
8
|
-
import pMap from 'p-map';
|
|
9
|
-
import {cosmiconfig, defaultLoaders} from 'cosmiconfig';
|
|
10
|
-
import defineLazyProperty from 'define-lazy-prop';
|
|
11
|
-
import pFilter from 'p-filter';
|
|
12
7
|
import slash from 'slash';
|
|
13
|
-
import {CONFIG_FILES, MODULE_NAME, DEFAULT_IGNORES} from './lib/constants.js';
|
|
14
8
|
import {
|
|
15
|
-
|
|
9
|
+
parseOptions,
|
|
16
10
|
getIgnores,
|
|
17
11
|
mergeWithFileConfig,
|
|
18
|
-
mergeWithFileConfigs,
|
|
19
|
-
buildConfig,
|
|
20
|
-
mergeOptions,
|
|
21
12
|
} from './lib/options-manager.js';
|
|
13
|
+
import {mergeReports, processReport, getIgnoredReport} from './lib/report.js';
|
|
22
14
|
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
const report = {
|
|
26
|
-
results: [],
|
|
27
|
-
errorCount: 0,
|
|
28
|
-
warningCount: 0,
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
for (const currentReport of reports) {
|
|
32
|
-
report.results.push(...currentReport.results);
|
|
33
|
-
report.errorCount += currentReport.errorCount;
|
|
34
|
-
report.warningCount += currentReport.warningCount;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return report;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const getReportStatistics = results => {
|
|
41
|
-
const statistics = {
|
|
42
|
-
errorCount: 0,
|
|
43
|
-
warningCount: 0,
|
|
44
|
-
fixableErrorCount: 0,
|
|
45
|
-
fixableWarningCount: 0,
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
for (const result of results) {
|
|
49
|
-
statistics.errorCount += result.errorCount;
|
|
50
|
-
statistics.warningCount += result.warningCount;
|
|
51
|
-
statistics.fixableErrorCount += result.fixableErrorCount;
|
|
52
|
-
statistics.fixableWarningCount += result.fixableWarningCount;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return statistics;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const processReport = (report, {isQuiet = false} = {}) => {
|
|
59
|
-
if (isQuiet) {
|
|
60
|
-
report = ESLint.getErrorResults(report);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const result = {
|
|
64
|
-
results: report,
|
|
65
|
-
...getReportStatistics(report),
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
defineLazyProperty(result, 'usedDeprecatedRules', () => {
|
|
69
|
-
const seenRules = new Set();
|
|
70
|
-
const rules = [];
|
|
71
|
-
|
|
72
|
-
for (const {usedDeprecatedRules} of report) {
|
|
73
|
-
for (const rule of usedDeprecatedRules) {
|
|
74
|
-
if (seenRules.has(rule.ruleId)) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
seenRules.add(rule.ruleId);
|
|
79
|
-
rules.push(rule);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
15
|
+
const globFiles = async (patterns, options) => {
|
|
16
|
+
const {ignores, extensions, cwd} = (await mergeWithFileConfig(options)).options;
|
|
82
17
|
|
|
83
|
-
|
|
84
|
-
|
|
18
|
+
patterns = patterns.length === 0
|
|
19
|
+
? [`**/*.{${extensions.join(',')}}`]
|
|
20
|
+
: arrify(patterns).map(pattern => slash(pattern));
|
|
85
21
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const engine = new ESLint(options);
|
|
22
|
+
const files = await globby(
|
|
23
|
+
patterns,
|
|
24
|
+
{ignore: ignores, gitignore: true, absolute: true, cwd},
|
|
25
|
+
);
|
|
91
26
|
|
|
92
|
-
|
|
93
|
-
return processReport(report, processorOptions);
|
|
27
|
+
return files.filter(file => extensions.includes(path.extname(file).slice(1)));
|
|
94
28
|
};
|
|
95
29
|
|
|
96
|
-
const globFiles = async (patterns, {ignores, extensions, cwd}) => (
|
|
97
|
-
await globby(
|
|
98
|
-
patterns.length === 0 ? [`**/*.{${extensions.join(',')}}`] : arrify(patterns).map(pattern => slash(pattern)),
|
|
99
|
-
{ignore: ignores, gitignore: true, absolute: true, cwd},
|
|
100
|
-
)).filter(file => extensions.includes(path.extname(file).slice(1)));
|
|
101
|
-
|
|
102
30
|
const getConfig = async options => {
|
|
103
|
-
const {
|
|
104
|
-
const {filePath, warnIgnored, ...eslintOptions} = buildConfig(foundOptions, prettierOptions);
|
|
31
|
+
const {filePath, eslintOptions} = await parseOptions(options);
|
|
105
32
|
const engine = new ESLint(eslintOptions);
|
|
106
33
|
return engine.calculateConfigForFile(filePath);
|
|
107
34
|
};
|
|
108
35
|
|
|
109
|
-
const lintText = async (string,
|
|
110
|
-
|
|
111
|
-
const
|
|
36
|
+
const lintText = async (string, options) => {
|
|
37
|
+
options = await parseOptions(options);
|
|
38
|
+
const {filePath, warnIgnored, eslintOptions, isQuiet} = options;
|
|
39
|
+
const {cwd, baseConfig: {ignorePatterns}} = eslintOptions;
|
|
112
40
|
|
|
113
|
-
if (
|
|
41
|
+
if (typeof filePath !== 'string' && !isEqual(getIgnores({}), ignorePatterns)) {
|
|
114
42
|
throw new Error('The `ignores` option requires the `filePath` option to be defined.');
|
|
115
43
|
}
|
|
116
44
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|| globby.gitignore.sync({cwd: options.cwd, ignore: options.baseConfig.ignorePatterns})(filePath)
|
|
126
|
-
|| await engine.isPathIgnored(filePath)
|
|
127
|
-
) {
|
|
128
|
-
return {
|
|
129
|
-
errorCount: 0,
|
|
130
|
-
warningCount: 0,
|
|
131
|
-
results: [{
|
|
132
|
-
errorCount: 0,
|
|
133
|
-
filePath: filename,
|
|
134
|
-
messages: [],
|
|
135
|
-
warningCount: 0,
|
|
136
|
-
}],
|
|
137
|
-
};
|
|
138
|
-
}
|
|
45
|
+
if (
|
|
46
|
+
filePath
|
|
47
|
+
&& (
|
|
48
|
+
micromatch.isMatch(path.relative(cwd, filePath), ignorePatterns)
|
|
49
|
+
|| isGitIgnoredSync({cwd, ignore: ignorePatterns})(filePath)
|
|
50
|
+
)
|
|
51
|
+
) {
|
|
52
|
+
return getIgnoredReport(filePath);
|
|
139
53
|
}
|
|
140
54
|
|
|
141
|
-
const
|
|
55
|
+
const eslint = new ESLint(eslintOptions);
|
|
142
56
|
|
|
143
|
-
|
|
57
|
+
if (filePath && await eslint.isPathIgnored(filePath)) {
|
|
58
|
+
return getIgnoredReport(filePath);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const report = await eslint.lintText(string, {filePath, warnIgnored});
|
|
62
|
+
return processReport(report, {isQuiet});
|
|
144
63
|
};
|
|
145
64
|
|
|
146
|
-
const lintFiles = async (patterns,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
65
|
+
const lintFiles = async (patterns, options) => {
|
|
66
|
+
const files = await globFiles(patterns, options);
|
|
67
|
+
|
|
68
|
+
const allOptions = await Promise.all(
|
|
69
|
+
files.map(filePath => parseOptions({...options, filePath})),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Files with same `xoConfigPath` can lint together
|
|
73
|
+
// https://github.com/xojs/xo/issues/599
|
|
74
|
+
const groups = groupBy(allOptions, 'eslintConfigId');
|
|
75
|
+
|
|
76
|
+
const reports = await Promise.all(
|
|
77
|
+
Object.values(groups)
|
|
78
|
+
.map(async filesWithOptions => {
|
|
79
|
+
const options = filesWithOptions[0];
|
|
80
|
+
const eslint = new ESLint(options.eslintOptions);
|
|
81
|
+
const files = [];
|
|
82
|
+
|
|
83
|
+
for (const options of filesWithOptions) {
|
|
84
|
+
const {filePath, eslintOptions} = options;
|
|
85
|
+
const {cwd, baseConfig: {ignorePatterns}} = eslintOptions;
|
|
86
|
+
if (filePath
|
|
87
|
+
&& (
|
|
88
|
+
micromatch.isMatch(path.relative(cwd, filePath), ignorePatterns)
|
|
89
|
+
|| isGitIgnoredSync({cwd, ignore: ignorePatterns})(filePath)
|
|
90
|
+
)) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// eslint-disable-next-line no-await-in-loop
|
|
95
|
+
if ((await eslint.isPathIgnored(filePath))) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
files.push(filePath);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const report = await eslint.lintFiles(files);
|
|
103
|
+
|
|
104
|
+
return processReport(report, {isQuiet: options.isQuiet});
|
|
105
|
+
}));
|
|
106
|
+
|
|
107
|
+
const report = mergeReports(reports);
|
|
108
|
+
|
|
109
|
+
return report;
|
|
166
110
|
};
|
|
167
111
|
|
|
168
112
|
const getFormatter = async name => {
|
package/lib/constants.js
CHANGED
|
@@ -106,13 +106,6 @@ const ENGINE_RULES = {
|
|
|
106
106
|
},
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
-
const PRETTIER_CONFIG_OVERRIDE = {
|
|
110
|
-
'eslint-plugin-babel': 'prettier/babel',
|
|
111
|
-
'eslint-plugin-flowtype': 'prettier/flowtype',
|
|
112
|
-
'eslint-plugin-standard': 'prettier/standard',
|
|
113
|
-
'eslint-plugin-vue': 'prettier/vue',
|
|
114
|
-
};
|
|
115
|
-
|
|
116
109
|
const MODULE_NAME = 'xo';
|
|
117
110
|
|
|
118
111
|
const CONFIG_FILES = [
|
|
@@ -144,7 +137,6 @@ export {
|
|
|
144
137
|
DEFAULT_EXTENSION,
|
|
145
138
|
TYPESCRIPT_EXTENSION,
|
|
146
139
|
ENGINE_RULES,
|
|
147
|
-
PRETTIER_CONFIG_OVERRIDE,
|
|
148
140
|
MODULE_NAME,
|
|
149
141
|
CONFIG_FILES,
|
|
150
142
|
MERGE_OPTIONS_CONCAT,
|
package/lib/options-manager.js
CHANGED
|
@@ -1,29 +1,26 @@
|
|
|
1
|
+
import {existsSync, promises as fs} from 'node:fs';
|
|
2
|
+
import process from 'node:process';
|
|
1
3
|
import os from 'node:os';
|
|
2
4
|
import path from 'node:path';
|
|
3
|
-
import fsExtra from 'fs-extra';
|
|
4
5
|
import arrify from 'arrify';
|
|
5
|
-
import {mergeWith,
|
|
6
|
-
import
|
|
7
|
-
import findUp from 'find-up';
|
|
6
|
+
import {mergeWith, flow, pick} from 'lodash-es';
|
|
7
|
+
import {findUpSync} from 'find-up';
|
|
8
8
|
import findCacheDir from 'find-cache-dir';
|
|
9
9
|
import prettier from 'prettier';
|
|
10
10
|
import semver from 'semver';
|
|
11
|
-
import {cosmiconfig,
|
|
12
|
-
import pReduce from 'p-reduce';
|
|
11
|
+
import {cosmiconfig, defaultLoaders} from 'cosmiconfig';
|
|
13
12
|
import micromatch from 'micromatch';
|
|
14
13
|
import JSON5 from 'json5';
|
|
15
14
|
import toAbsoluteGlob from 'to-absolute-glob';
|
|
16
15
|
import stringify from 'json-stable-stringify-without-jsonify';
|
|
17
16
|
import murmur from 'imurmurhash';
|
|
18
|
-
import
|
|
19
|
-
import eslintrc from '@eslint/eslintrc';
|
|
17
|
+
import {Legacy} from '@eslint/eslintrc';
|
|
20
18
|
import createEsmUtils from 'esm-utils';
|
|
21
19
|
import {
|
|
22
20
|
DEFAULT_IGNORES,
|
|
23
21
|
DEFAULT_EXTENSION,
|
|
24
22
|
TYPESCRIPT_EXTENSION,
|
|
25
23
|
ENGINE_RULES,
|
|
26
|
-
PRETTIER_CONFIG_OVERRIDE,
|
|
27
24
|
MODULE_NAME,
|
|
28
25
|
CONFIG_FILES,
|
|
29
26
|
MERGE_OPTIONS_CONCAT,
|
|
@@ -32,10 +29,8 @@ import {
|
|
|
32
29
|
} from './constants.js';
|
|
33
30
|
|
|
34
31
|
const {__dirname, json, require} = createEsmUtils(import.meta);
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const {normalizePackageName} = eslintrc.Legacy.naming;
|
|
38
|
-
const resolveModule = eslintrc.Legacy.ModuleResolver.resolve;
|
|
32
|
+
const {normalizePackageName} = Legacy.naming;
|
|
33
|
+
const resolveModule = Legacy.ModuleResolver.resolve;
|
|
39
34
|
|
|
40
35
|
const resolveFrom = (moduleId, fromDirectory = process.cwd()) => resolveModule(moduleId, path.join(fromDirectory, '__placeholder__.js'));
|
|
41
36
|
|
|
@@ -48,13 +43,14 @@ resolveFrom.silent = (moduleId, fromDirectory) => {
|
|
|
48
43
|
const resolveLocalConfig = name => resolveModule(normalizePackageName(name, 'eslint-config'), import.meta.url);
|
|
49
44
|
|
|
50
45
|
const nodeVersion = process && process.version;
|
|
51
|
-
const cacheLocation = findCacheDir({name: CACHE_DIR_NAME}) || path.join(os.homedir() || os.tmpdir(), '.xo-cache/');
|
|
46
|
+
const cacheLocation = cwd => findCacheDir({name: CACHE_DIR_NAME, cwd}) || path.join(os.homedir() || os.tmpdir(), '.xo-cache/');
|
|
52
47
|
|
|
53
48
|
const DEFAULT_CONFIG = {
|
|
54
49
|
useEslintrc: false,
|
|
55
50
|
cache: true,
|
|
56
|
-
cacheLocation: path.join(cacheLocation, 'xo-cache.json'),
|
|
51
|
+
cacheLocation: path.join(cacheLocation(), 'xo-cache.json'),
|
|
57
52
|
globInputPaths: false,
|
|
53
|
+
resolvePluginsRelativeTo: __dirname,
|
|
58
54
|
baseConfig: {
|
|
59
55
|
extends: [
|
|
60
56
|
resolveLocalConfig('xo'),
|
|
@@ -104,111 +100,90 @@ const isTypescript = file => TYPESCRIPT_EXTENSION.includes(path.extname(file).sl
|
|
|
104
100
|
Find config for `lintText`.
|
|
105
101
|
The config files are searched starting from `options.filePath` if defined or `options.cwd` otherwise.
|
|
106
102
|
*/
|
|
107
|
-
const mergeWithFileConfig = options => {
|
|
103
|
+
const mergeWithFileConfig = async options => {
|
|
108
104
|
options.cwd = path.resolve(options.cwd || process.cwd());
|
|
109
|
-
const configExplorer =
|
|
110
|
-
const pkgConfigExplorer =
|
|
105
|
+
const configExplorer = cosmiconfig(MODULE_NAME, {searchPlaces: CONFIG_FILES, loaders: {noExt: defaultLoaders['.json']}, stopDir: options.cwd});
|
|
106
|
+
const pkgConfigExplorer = cosmiconfig('engines', {searchPlaces: ['package.json'], stopDir: options.cwd});
|
|
111
107
|
if (options.filePath) {
|
|
112
108
|
options.filePath = path.resolve(options.cwd, options.filePath);
|
|
113
109
|
}
|
|
114
110
|
|
|
115
111
|
const searchPath = options.filePath || options.cwd;
|
|
116
112
|
|
|
117
|
-
const {config: xoOptions, filepath: xoConfigPath} = configExplorer.search(searchPath) || {};
|
|
118
|
-
const {config: enginesOptions} = pkgConfigExplorer.search(searchPath) || {};
|
|
113
|
+
const {config: xoOptions, filepath: xoConfigPath} = (await configExplorer.search(searchPath)) || {};
|
|
114
|
+
const {config: enginesOptions} = (await pkgConfigExplorer.search(searchPath)) || {};
|
|
119
115
|
|
|
120
116
|
options = mergeOptions(options, xoOptions, enginesOptions);
|
|
121
117
|
options.cwd = xoConfigPath && path.dirname(xoConfigPath) !== options.cwd ? path.resolve(options.cwd, path.dirname(xoConfigPath)) : options.cwd;
|
|
122
118
|
|
|
119
|
+
// Very simple way to ensure eslint is ran minimal times across
|
|
120
|
+
// all linted files, once for each unique configuration - xo config path + override hash + tsconfig path
|
|
121
|
+
let eslintConfigId = xoConfigPath;
|
|
123
122
|
if (options.filePath) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const prettierOptions = options.prettier ? prettier.resolveConfig.sync(searchPath, {editorconfig: true}) || {} : {};
|
|
128
|
-
|
|
129
|
-
if (options.filePath && isTypescript(options.filePath)) {
|
|
130
|
-
const tsConfigExplorer = cosmiconfigSync([], {searchPlaces: ['tsconfig.json'], loaders: {'.json': (_, content) => JSON5.parse(content)}});
|
|
131
|
-
const {config: tsConfig, filepath: tsConfigPath} = tsConfigExplorer.search(options.filePath) || {};
|
|
123
|
+
const overrides = applyOverrides(options.filePath, options);
|
|
124
|
+
options = overrides.options;
|
|
132
125
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
126
|
+
if (overrides.hash) {
|
|
127
|
+
eslintConfigId += overrides.hash;
|
|
128
|
+
}
|
|
136
129
|
}
|
|
137
130
|
|
|
138
|
-
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
Find config for each files found by `lintFiles`.
|
|
143
|
-
The config files are searched starting from each files.
|
|
144
|
-
*/
|
|
145
|
-
const mergeWithFileConfigs = async (files, options, configFiles) => {
|
|
146
|
-
configFiles = configFiles.sort((a, b) => b.filepath.split(path.sep).length - a.filepath.split(path.sep).length);
|
|
147
|
-
const tsConfigs = {};
|
|
148
|
-
|
|
149
|
-
const groups = [...(await pReduce(files, async (configs, file) => {
|
|
150
|
-
const pkgConfigExplorer = cosmiconfig('engines', {searchPlaces: ['package.json'], stopDir: options.cwd});
|
|
151
|
-
|
|
152
|
-
const {config: xoOptions, filepath: xoConfigPath} = findApplicableConfig(file, configFiles) || {};
|
|
153
|
-
const {config: enginesOptions, filepath: enginesConfigPath} = await pkgConfigExplorer.search(file) || {};
|
|
131
|
+
const prettierOptions = options.prettier ? await prettier.resolveConfig(searchPath, {editorconfig: true}) || {} : {};
|
|
154
132
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const {
|
|
159
|
-
fileOptions = optionsWithOverrides;
|
|
160
|
-
|
|
161
|
-
const prettierOptions = fileOptions.prettier ? await prettier.resolveConfig(file, {editorconfig: true}) || {} : {};
|
|
133
|
+
if (options.filePath && isTypescript(options.filePath)) {
|
|
134
|
+
// We can skip looking up the tsconfig if we have it defined
|
|
135
|
+
// in our parser options already. Otherwise we can look it up and create it as normal
|
|
136
|
+
const {project: tsConfigProjectPath, tsconfigRootDir} = options.parserOptions || {};
|
|
162
137
|
|
|
138
|
+
let tsConfig;
|
|
163
139
|
let tsConfigPath;
|
|
164
|
-
if (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
140
|
+
if (tsConfigProjectPath) {
|
|
141
|
+
tsConfigPath = path.resolve(options.cwd, tsConfigProjectPath);
|
|
142
|
+
tsConfig = await json.load(tsConfigPath);
|
|
143
|
+
} else {
|
|
144
|
+
const tsConfigExplorer = cosmiconfig([], {
|
|
145
|
+
searchPlaces: ['tsconfig.json'],
|
|
146
|
+
loaders: {'.json': (_, content) => JSON5.parse(content)},
|
|
147
|
+
stopDir: tsconfigRootDir,
|
|
148
|
+
});
|
|
149
|
+
const searchResults = (await tsConfigExplorer.search(options.filePath)) || {};
|
|
150
|
+
tsConfigPath = searchResults.filepath;
|
|
151
|
+
tsConfig = searchResults.config;
|
|
172
152
|
}
|
|
173
153
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
await Promise.all(Object.entries(groupBy(groups.filter(({options}) => Boolean(options.ts)), group => group.options.tsConfigPath || '')).map(
|
|
187
|
-
([tsConfigPath, groups]) => {
|
|
188
|
-
const files = groups.flatMap(group => group.files);
|
|
189
|
-
const cachePath = getTsConfigCachePath(files, tsConfigPath);
|
|
190
|
-
|
|
191
|
-
for (const group of groups) {
|
|
192
|
-
group.options.tsConfigPath = cachePath;
|
|
193
|
-
}
|
|
154
|
+
if (tsConfigPath) {
|
|
155
|
+
options.tsConfigPath = tsConfigPath;
|
|
156
|
+
eslintConfigId += tsConfigPath;
|
|
157
|
+
} else {
|
|
158
|
+
const {path: tsConfigCachePath, hash: tsConfigHash} = await getTsConfigCachePath([eslintConfigId], tsConfigPath, options.cwd);
|
|
159
|
+
eslintConfigId += tsConfigHash;
|
|
160
|
+
options.tsConfigPath = tsConfigCachePath;
|
|
161
|
+
const config = makeTSConfig(tsConfig, tsConfigPath, [options.filePath]);
|
|
162
|
+
await fs.mkdir(path.dirname(options.tsConfigPath), {recursive: true});
|
|
163
|
+
await fs.writeFile(options.tsConfigPath, JSON.stringify(config));
|
|
164
|
+
}
|
|
194
165
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
));
|
|
166
|
+
options.ts = true;
|
|
167
|
+
}
|
|
198
168
|
|
|
199
|
-
return
|
|
169
|
+
return {options, prettierOptions, eslintConfigId};
|
|
200
170
|
};
|
|
201
171
|
|
|
202
|
-
const findApplicableConfig = (file, configFiles) => configFiles.find(({filepath}) => isPathInside(file, path.dirname(filepath)));
|
|
203
|
-
|
|
204
172
|
/**
|
|
205
173
|
Generate a unique and consistent path for the temporary `tsconfig.json`.
|
|
206
174
|
Hashing based on https://github.com/eslint/eslint/blob/cf38d0d939b62f3670cdd59f0143fd896fccd771/lib/cli-engine/lint-result-cache.js#L30
|
|
207
175
|
*/
|
|
208
|
-
const getTsConfigCachePath = (files, tsConfigPath) =>
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
176
|
+
const getTsConfigCachePath = async (files, tsConfigPath, cwd) => {
|
|
177
|
+
const {version} = await json.load('../package.json');
|
|
178
|
+
const tsConfigHash = murmur(`${version}_${nodeVersion}_${stringify({files: files.sort(), tsConfigPath})}`).result().toString(36);
|
|
179
|
+
return {
|
|
180
|
+
path: path.join(
|
|
181
|
+
cacheLocation(cwd),
|
|
182
|
+
`tsconfig.${tsConfigHash}.json`,
|
|
183
|
+
),
|
|
184
|
+
hash: tsConfigHash,
|
|
185
|
+
};
|
|
186
|
+
};
|
|
212
187
|
|
|
213
188
|
const makeTSConfig = (tsConfig, tsConfigPath, files) => {
|
|
214
189
|
const config = {files: files.filter(file => isTypescript(file))};
|
|
@@ -270,7 +245,7 @@ const mergeOptions = (options, xoOptions = {}, enginesOptions = {}) => {
|
|
|
270
245
|
...options,
|
|
271
246
|
});
|
|
272
247
|
|
|
273
|
-
mergedOptions.extensions = DEFAULT_EXTENSION
|
|
248
|
+
mergedOptions.extensions = [...DEFAULT_EXTENSION, ...(mergedOptions.extensions || [])];
|
|
274
249
|
mergedOptions.ignores = getIgnores(mergedOptions);
|
|
275
250
|
|
|
276
251
|
return mergedOptions;
|
|
@@ -413,6 +388,10 @@ const buildXOConfig = options => config => {
|
|
|
413
388
|
|
|
414
389
|
// Does not work when the TS definition exports a default const.
|
|
415
390
|
config.baseConfig.rules['import/default'] = 'off';
|
|
391
|
+
|
|
392
|
+
// Disabled as it doesn't work with TypeScript.
|
|
393
|
+
// This issue and some others: https://github.com/benmosher/eslint-plugin-import/issues/1341
|
|
394
|
+
config.baseConfig.rules['import/named'] = 'off';
|
|
416
395
|
}
|
|
417
396
|
|
|
418
397
|
config.baseConfig.settings['import/resolver'] = gatherImportResolvers(options);
|
|
@@ -424,7 +403,7 @@ const buildExtendsConfig = options => config => {
|
|
|
424
403
|
if (options.extends && options.extends.length > 0) {
|
|
425
404
|
const configs = options.extends.map(name => {
|
|
426
405
|
// Don't do anything if it's a filepath
|
|
427
|
-
if (
|
|
406
|
+
if (existsSync(name)) {
|
|
428
407
|
return name;
|
|
429
408
|
}
|
|
430
409
|
|
|
@@ -453,19 +432,11 @@ const buildPrettierConfig = (options, prettierConfig) => config => {
|
|
|
453
432
|
// The prettier plugin uses Prettier to format the code with `--fix`
|
|
454
433
|
config.baseConfig.plugins.push('prettier');
|
|
455
434
|
|
|
456
|
-
// The prettier
|
|
457
|
-
config.baseConfig.extends.push('prettier');
|
|
435
|
+
// The prettier plugin overrides ESLint stylistic rules that are handled by Prettier
|
|
436
|
+
config.baseConfig.extends.push('plugin:prettier/recommended');
|
|
458
437
|
|
|
459
438
|
// The `prettier/prettier` rule reports errors if the code is not formatted in accordance to Prettier
|
|
460
439
|
config.baseConfig.rules['prettier/prettier'] = ['error', mergeWithPrettierConfig(options, prettierConfig)];
|
|
461
|
-
|
|
462
|
-
// If the user has the React, Flowtype, or Standard plugin, add the corresponding Prettier rule overrides
|
|
463
|
-
// See https://github.com/prettier/eslint-config-prettier for the list of plugins overrrides
|
|
464
|
-
for (const [plugin, prettierConfig] of Object.entries(PRETTIER_CONFIG_OVERRIDE)) {
|
|
465
|
-
if (options.cwd && resolveFrom.silent(plugin, options.cwd)) {
|
|
466
|
-
config.baseConfig.extends.push(prettierConfig);
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
440
|
}
|
|
470
441
|
|
|
471
442
|
return config;
|
|
@@ -491,8 +462,8 @@ const mergeWithPrettierConfig = (options, prettierOptions) => {
|
|
|
491
462
|
{
|
|
492
463
|
singleQuote: true,
|
|
493
464
|
bracketSpacing: false,
|
|
494
|
-
|
|
495
|
-
trailingComma: '
|
|
465
|
+
bracketSameLine: false,
|
|
466
|
+
trailingComma: 'all',
|
|
496
467
|
tabWidth: normalizeSpaces(options),
|
|
497
468
|
useTabs: !options.space,
|
|
498
469
|
semi: options.semicolon !== false,
|
|
@@ -504,7 +475,7 @@ const mergeWithPrettierConfig = (options, prettierOptions) => {
|
|
|
504
475
|
|
|
505
476
|
const buildTSConfig = options => config => {
|
|
506
477
|
if (options.ts) {
|
|
507
|
-
config.baseConfig.extends.push('xo-typescript');
|
|
478
|
+
config.baseConfig.extends.push(require.resolve('eslint-config-xo-typescript'));
|
|
508
479
|
config.baseConfig.parser = require.resolve('@typescript-eslint/parser');
|
|
509
480
|
config.baseConfig.parserOptions = {
|
|
510
481
|
...config.baseConfig.parserOptions,
|
|
@@ -550,11 +521,11 @@ const findApplicableOverrides = (path, overrides) => {
|
|
|
550
521
|
const applicable = [];
|
|
551
522
|
|
|
552
523
|
for (const override of overrides) {
|
|
553
|
-
hash <<= 1;
|
|
524
|
+
hash <<= 1; // eslint-disable-line no-bitwise
|
|
554
525
|
|
|
555
526
|
if (micromatch.isMatch(path, override.files)) {
|
|
556
527
|
applicable.push(override);
|
|
557
|
-
hash |= 1;
|
|
528
|
+
hash |= 1; // eslint-disable-line no-bitwise
|
|
558
529
|
}
|
|
559
530
|
}
|
|
560
531
|
|
|
@@ -564,7 +535,7 @@ const findApplicableOverrides = (path, overrides) => {
|
|
|
564
535
|
};
|
|
565
536
|
};
|
|
566
537
|
|
|
567
|
-
const getIgnores = ({ignores}) => DEFAULT_IGNORES
|
|
538
|
+
const getIgnores = ({ignores}) => [...DEFAULT_IGNORES, ...(ignores || [])];
|
|
568
539
|
|
|
569
540
|
const gatherImportResolvers = options => {
|
|
570
541
|
let resolvers = {};
|
|
@@ -584,7 +555,7 @@ const gatherImportResolvers = options => {
|
|
|
584
555
|
webpackResolverSettings = options.webpack === true ? {} : options.webpack;
|
|
585
556
|
} else if (!(options.webpack === false || resolvers.webpack)) {
|
|
586
557
|
// If a webpack config file exists, add the import resolver automatically
|
|
587
|
-
const webpackConfigPath =
|
|
558
|
+
const webpackConfigPath = findUpSync('webpack.config.js', {cwd: options.cwd});
|
|
588
559
|
if (webpackConfigPath) {
|
|
589
560
|
webpackResolverSettings = {config: webpackConfigPath};
|
|
590
561
|
}
|
|
@@ -603,14 +574,28 @@ const gatherImportResolvers = options => {
|
|
|
603
574
|
return resolvers;
|
|
604
575
|
};
|
|
605
576
|
|
|
577
|
+
const parseOptions = async options => {
|
|
578
|
+
options = normalizeOptions(options);
|
|
579
|
+
const {options: foundOptions, prettierOptions, eslintConfigId} = await mergeWithFileConfig(options);
|
|
580
|
+
const {filePath, warnIgnored, ...eslintOptions} = buildConfig(foundOptions, prettierOptions);
|
|
581
|
+
return {
|
|
582
|
+
filePath,
|
|
583
|
+
warnIgnored,
|
|
584
|
+
isQuiet: options.quiet,
|
|
585
|
+
eslintOptions,
|
|
586
|
+
eslintConfigId,
|
|
587
|
+
};
|
|
588
|
+
};
|
|
589
|
+
|
|
606
590
|
export {
|
|
591
|
+
parseOptions,
|
|
592
|
+
getIgnores,
|
|
593
|
+
mergeWithFileConfig,
|
|
594
|
+
|
|
595
|
+
// For tests
|
|
596
|
+
applyOverrides,
|
|
607
597
|
findApplicableOverrides,
|
|
608
598
|
mergeWithPrettierConfig,
|
|
609
599
|
normalizeOptions,
|
|
610
|
-
getIgnores,
|
|
611
|
-
mergeWithFileConfigs,
|
|
612
|
-
mergeWithFileConfig,
|
|
613
600
|
buildConfig,
|
|
614
|
-
applyOverrides,
|
|
615
|
-
mergeOptions,
|
|
616
601
|
};
|
package/lib/report.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import defineLazyProperty from 'define-lazy-prop';
|
|
2
|
+
import {ESLint} from 'eslint';
|
|
3
|
+
|
|
4
|
+
/** Merge multiple reports into a single report */
|
|
5
|
+
const mergeReports = reports => {
|
|
6
|
+
const report = {
|
|
7
|
+
results: [],
|
|
8
|
+
errorCount: 0,
|
|
9
|
+
warningCount: 0,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
for (const currentReport of reports) {
|
|
13
|
+
report.results.push(...currentReport.results);
|
|
14
|
+
report.errorCount += currentReport.errorCount;
|
|
15
|
+
report.warningCount += currentReport.warningCount;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return report;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const processReport = (report, {isQuiet = false} = {}) => {
|
|
22
|
+
if (isQuiet) {
|
|
23
|
+
report = ESLint.getErrorResults(report);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const result = {
|
|
27
|
+
results: report,
|
|
28
|
+
...getReportStatistics(report),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
defineLazyProperty(result, 'usedDeprecatedRules', () => {
|
|
32
|
+
const seenRules = new Set();
|
|
33
|
+
const rules = [];
|
|
34
|
+
|
|
35
|
+
for (const {usedDeprecatedRules} of report) {
|
|
36
|
+
for (const rule of usedDeprecatedRules) {
|
|
37
|
+
if (seenRules.has(rule.ruleId)) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
seenRules.add(rule.ruleId);
|
|
42
|
+
rules.push(rule);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return rules;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const getReportStatistics = results => {
|
|
53
|
+
const statistics = {
|
|
54
|
+
errorCount: 0,
|
|
55
|
+
warningCount: 0,
|
|
56
|
+
fixableErrorCount: 0,
|
|
57
|
+
fixableWarningCount: 0,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
for (const result of results) {
|
|
61
|
+
statistics.errorCount += result.errorCount;
|
|
62
|
+
statistics.warningCount += result.warningCount;
|
|
63
|
+
statistics.fixableErrorCount += result.fixableErrorCount;
|
|
64
|
+
statistics.fixableWarningCount += result.fixableWarningCount;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return statistics;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const getIgnoredReport = filePath => ({
|
|
71
|
+
errorCount: 0,
|
|
72
|
+
warningCount: 0,
|
|
73
|
+
results: [
|
|
74
|
+
{
|
|
75
|
+
errorCount: 0,
|
|
76
|
+
warningCount: 0,
|
|
77
|
+
filePath,
|
|
78
|
+
messages: [],
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
isIgnored: true,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
export {mergeReports, processReport, getIgnoredReport};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.0",
|
|
4
4
|
"description": "JavaScript/TypeScript linter (ESLint wrapper) with great defaults",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "xojs/xo",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"node": ">=12.20"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
|
-
"test": "
|
|
19
|
+
"test:clean": "find ./test -type d -name 'node_modules' -prune -not -path ./test/fixtures/project/node_modules -exec rm -rf '{}' +",
|
|
20
|
+
"test": "node cli.js && nyc ava"
|
|
20
21
|
},
|
|
21
22
|
"files": [
|
|
22
23
|
"config",
|
|
@@ -51,75 +52,67 @@
|
|
|
51
52
|
"javascript",
|
|
52
53
|
"typescript"
|
|
53
54
|
],
|
|
55
|
+
"bundledDependencies": [
|
|
56
|
+
"@typescript-eslint/eslint-plugin",
|
|
57
|
+
"@typescript-eslint/parser",
|
|
58
|
+
"eslint-config-xo-typescript"
|
|
59
|
+
],
|
|
54
60
|
"dependencies": {
|
|
55
|
-
"@eslint/eslintrc": "^0.
|
|
56
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
57
|
-
"@typescript-eslint/parser": "^
|
|
61
|
+
"@eslint/eslintrc": "^1.0.3",
|
|
62
|
+
"@typescript-eslint/eslint-plugin": "^5.2.0",
|
|
63
|
+
"@typescript-eslint/parser": "^5.2.0",
|
|
58
64
|
"arrify": "^3.0.0",
|
|
59
|
-
"cosmiconfig": "^7.0.
|
|
60
|
-
"debug": "^4.3.2",
|
|
65
|
+
"cosmiconfig": "^7.0.1",
|
|
61
66
|
"define-lazy-prop": "^3.0.0",
|
|
62
|
-
"eslint": "^
|
|
67
|
+
"eslint": "^8.1.0",
|
|
63
68
|
"eslint-config-prettier": "^8.3.0",
|
|
64
|
-
"eslint-config-xo": "^0.
|
|
65
|
-
"eslint-config-xo-typescript": "^0.
|
|
69
|
+
"eslint-config-xo": "^0.39.0",
|
|
70
|
+
"eslint-config-xo-typescript": "^0.47.0",
|
|
66
71
|
"eslint-formatter-pretty": "^4.1.0",
|
|
67
|
-
"eslint-import-resolver-webpack": "^0.13.
|
|
68
|
-
"eslint-plugin-ava": "^
|
|
72
|
+
"eslint-import-resolver-webpack": "^0.13.2",
|
|
73
|
+
"eslint-plugin-ava": "^13.1.0",
|
|
69
74
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
70
|
-
"eslint-plugin-import": "^2.
|
|
75
|
+
"eslint-plugin-import": "^2.25.2",
|
|
71
76
|
"eslint-plugin-no-use-extend-native": "^0.5.0",
|
|
72
77
|
"eslint-plugin-node": "^11.1.0",
|
|
73
|
-
"eslint-plugin-prettier": "^
|
|
74
|
-
"eslint-plugin-
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"find-
|
|
78
|
-
"find-up": "^5.0.0",
|
|
79
|
-
"fs-extra": "^10.0.0",
|
|
78
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
79
|
+
"eslint-plugin-unicorn": "^37.0.1",
|
|
80
|
+
"esm-utils": "^2.0.0",
|
|
81
|
+
"find-cache-dir": "^3.3.2",
|
|
82
|
+
"find-up": "^6.2.0",
|
|
80
83
|
"get-stdin": "^9.0.0",
|
|
81
|
-
"globby": "^
|
|
84
|
+
"globby": "^12.0.2",
|
|
82
85
|
"imurmurhash": "^0.1.4",
|
|
83
|
-
"is-path-inside": "^4.0.0",
|
|
84
86
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
|
85
87
|
"json5": "^2.2.0",
|
|
86
88
|
"lodash-es": "^4.17.21",
|
|
87
|
-
"meow": "^10.1.
|
|
89
|
+
"meow": "^10.1.1",
|
|
88
90
|
"micromatch": "^4.0.4",
|
|
89
91
|
"open-editor": "^3.0.0",
|
|
90
|
-
"
|
|
91
|
-
"p-map": "^5.0.0",
|
|
92
|
-
"p-reduce": "^3.0.0",
|
|
93
|
-
"path-exists": "^4.0.0",
|
|
94
|
-
"prettier": "^2.3.2",
|
|
92
|
+
"prettier": "^2.4.1",
|
|
95
93
|
"semver": "^7.3.5",
|
|
96
94
|
"slash": "^4.0.0",
|
|
97
95
|
"to-absolute-glob": "^2.0.2",
|
|
98
|
-
"typescript": "^4.
|
|
96
|
+
"typescript": "^4.4.4"
|
|
99
97
|
},
|
|
100
98
|
"devDependencies": {
|
|
101
99
|
"ava": "^3.15.0",
|
|
102
100
|
"eslint-config-xo-react": "^0.25.0",
|
|
103
|
-
"eslint-plugin-react": "^7.
|
|
101
|
+
"eslint-plugin-react": "^7.26.1",
|
|
104
102
|
"eslint-plugin-react-hooks": "^4.2.0",
|
|
105
103
|
"execa": "^5.1.1",
|
|
106
104
|
"nyc": "^15.1.0",
|
|
107
105
|
"proxyquire": "^2.1.3",
|
|
108
106
|
"temp-write": "^5.0.0",
|
|
109
|
-
"webpack": "^5.
|
|
107
|
+
"webpack": "^5.60.0"
|
|
110
108
|
},
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"
|
|
109
|
+
"xo": {
|
|
110
|
+
"ignores": [
|
|
111
|
+
"test/fixtures",
|
|
112
|
+
"test/temp",
|
|
113
|
+
"coverage"
|
|
116
114
|
]
|
|
117
115
|
},
|
|
118
|
-
"eslintIgnore": [
|
|
119
|
-
"test/fixtures",
|
|
120
|
-
"test/temp",
|
|
121
|
-
"coverage"
|
|
122
|
-
],
|
|
123
116
|
"ava": {
|
|
124
117
|
"files": [
|
|
125
118
|
"!test/temp"
|
package/readme.md
CHANGED
|
@@ -40,7 +40,7 @@ It uses [ESLint](https://eslint.org) underneath, so issues regarding built-in ru
|
|
|
40
40
|
## Install
|
|
41
41
|
|
|
42
42
|
```
|
|
43
|
-
$ npm install xo
|
|
43
|
+
$ npm install xo --save-dev
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
*You must install XO locally. You can run it directly with `$ npx xo`.*
|
|
@@ -169,14 +169,14 @@ module.exports = {
|
|
|
169
169
|
};
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
[Globals](https://eslint.org/docs/user-guide/configuring#specifying-globals) and [rules](https://eslint.org/docs/user-guide/configuring#configuring-rules) can be configured inline in files.
|
|
172
|
+
[Globals](https://eslint.org/docs/user-guide/configuring/language-options#specifying-globals) and [rules](https://eslint.org/docs/user-guide/configuring/rules#configuring-rules) can be configured inline in files.
|
|
173
173
|
|
|
174
174
|
### envs
|
|
175
175
|
|
|
176
176
|
Type: `string[]`\
|
|
177
177
|
Default: `['es2021', 'node']`
|
|
178
178
|
|
|
179
|
-
Which [environments](https://eslint.org/docs/user-guide/configuring#specifying-environments) your code is designed to run in. Each environment brings with it a certain set of predefined global variables.
|
|
179
|
+
Which [environments](https://eslint.org/docs/user-guide/configuring/language-options#specifying-environments) your code is designed to run in. Each environment brings with it a certain set of predefined global variables.
|
|
180
180
|
|
|
181
181
|
### globals
|
|
182
182
|
|
|
@@ -188,7 +188,7 @@ Additional global variables your code accesses during execution.
|
|
|
188
188
|
|
|
189
189
|
Type: `string[]`
|
|
190
190
|
|
|
191
|
-
Some [paths](lib/options-manager.js) are ignored by default, including paths in `.gitignore` and [.eslintignore](https://eslint.org/docs/user-guide/configuring#eslintignore). Additional ignores can be added here.
|
|
191
|
+
Some [paths](lib/options-manager.js) are ignored by default, including paths in `.gitignore` and [.eslintignore](https://eslint.org/docs/user-guide/configuring/ignoring-code#the-eslintignore-file). Additional ignores can be added here.
|
|
192
192
|
|
|
193
193
|
### space
|
|
194
194
|
|
|
@@ -221,14 +221,23 @@ Default: `false`
|
|
|
221
221
|
|
|
222
222
|
Format code with [Prettier](https://github.com/prettier/prettier).
|
|
223
223
|
|
|
224
|
-
|
|
224
|
+
[Prettier options](https://prettier.io/docs/en/options.html) will be based on your [Prettier config](https://prettier.io/docs/en/configuration.html). XO will then **merge** your options with its own defaults:
|
|
225
225
|
- [semi](https://prettier.io/docs/en/options.html#semicolons): based on [semicolon](#semicolon) option
|
|
226
226
|
- [useTabs](https://prettier.io/docs/en/options.html#tabs): based on [space](#space) option
|
|
227
227
|
- [tabWidth](https://prettier.io/docs/en/options.html#tab-width): based on [space](#space) option
|
|
228
|
-
- [trailingComma](https://prettier.io/docs/en/options.html#trailing-commas): `
|
|
228
|
+
- [trailingComma](https://prettier.io/docs/en/options.html#trailing-commas): `all`
|
|
229
229
|
- [singleQuote](https://prettier.io/docs/en/options.html#quotes): `true`
|
|
230
230
|
- [bracketSpacing](https://prettier.io/docs/en/options.html#bracket-spacing): `false`
|
|
231
|
-
|
|
231
|
+
|
|
232
|
+
To stick with Prettier's defaults, add this to your Prettier config:
|
|
233
|
+
|
|
234
|
+
```js
|
|
235
|
+
module.exports = {
|
|
236
|
+
trailingComma: 'es5',
|
|
237
|
+
singleQuote: false,
|
|
238
|
+
bracketSpacing: true,
|
|
239
|
+
};
|
|
240
|
+
```
|
|
232
241
|
|
|
233
242
|
If contradicting options are set for both Prettier and XO an error will be thrown.
|
|
234
243
|
|
|
@@ -245,13 +254,13 @@ If set to `false`, no rules specific to a Node.js version will be enabled.
|
|
|
245
254
|
|
|
246
255
|
Type: `string[]`
|
|
247
256
|
|
|
248
|
-
Include third-party [plugins](https://eslint.org/docs/user-guide/configuring
|
|
257
|
+
Include third-party [plugins](https://eslint.org/docs/user-guide/configuring/plugins#configuring-plugins).
|
|
249
258
|
|
|
250
259
|
### extends
|
|
251
260
|
|
|
252
261
|
Type: `string | string[]`
|
|
253
262
|
|
|
254
|
-
Use one or more [shareable configs](https://eslint.org/docs/developer-guide/shareable-configs
|
|
263
|
+
Use one or more [shareable configs](https://eslint.org/docs/developer-guide/shareable-configs) or [plugin configs](https://eslint.org/docs/user-guide/configuring/configuration-files#using-a-configuration-from-a-plugin) to override any of the default rules (like `rules` above).
|
|
255
264
|
|
|
256
265
|
### extensions
|
|
257
266
|
|
|
@@ -263,7 +272,7 @@ Allow more extensions to be linted besides `.js`, `.jsx`, `.mjs`, and `.cjs`. Ma
|
|
|
263
272
|
|
|
264
273
|
Type: `object`
|
|
265
274
|
|
|
266
|
-
[Shared ESLint settings](https://eslint.org/docs/user-guide/configuring#adding-shared-settings) exposed to rules.
|
|
275
|
+
[Shared ESLint settings](https://eslint.org/docs/user-guide/configuring/configuration-files#adding-shared-settings) exposed to rules.
|
|
267
276
|
|
|
268
277
|
### parser
|
|
269
278
|
|
|
@@ -275,7 +284,7 @@ ESLint parser. For example, [`@babel/eslint-parser`](https://github.com/babel/ba
|
|
|
275
284
|
|
|
276
285
|
Type: `string`
|
|
277
286
|
|
|
278
|
-
[ESLint processor.](https://eslint.org/docs/user-guide/configuring#specifying-processor)
|
|
287
|
+
[ESLint processor.](https://eslint.org/docs/user-guide/configuring/plugins#specifying-processor)
|
|
279
288
|
|
|
280
289
|
### webpack
|
|
281
290
|
|