xo 0.44.0 → 0.46.2

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.
@@ -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: 2021,
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
- 'promise',
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
- 'promise/param-names': 'error',
177
- 'promise/no-return-wrap': [
178
- 'error',
179
- {
180
- allowReject: true,
181
- },
182
- ],
183
- 'promise/no-new-statics': 'error',
184
- 'promise/no-return-in-finally': 'error',
185
- 'promise/valid-params': 'error',
186
- 'promise/prefer-await-to-then': 'error',
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
- // Disabled as it doesn't work with TypeScript.
204
- // This issue and some others: https://github.com/benmosher/eslint-plugin-import/issues/1341
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',
package/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import {ESLint} from 'eslint';
3
3
  import {globby, isGitIgnoredSync} from 'globby';
4
- import {isEqual} from 'lodash-es';
4
+ import {isEqual, groupBy} from 'lodash-es';
5
5
  import micromatch from 'micromatch';
6
6
  import arrify from 'arrify';
7
7
  import slash from 'slash';
@@ -12,30 +12,6 @@ import {
12
12
  } from './lib/options-manager.js';
13
13
  import {mergeReports, processReport, getIgnoredReport} from './lib/report.js';
14
14
 
15
- const runEslint = async (lint, options) => {
16
- const {filePath, eslintOptions, isQuiet} = options;
17
- const {cwd, baseConfig: {ignorePatterns}} = eslintOptions;
18
-
19
- if (
20
- filePath
21
- && (
22
- micromatch.isMatch(path.relative(cwd, filePath), ignorePatterns)
23
- || isGitIgnoredSync({cwd, ignore: ignorePatterns})(filePath)
24
- )
25
- ) {
26
- return getIgnoredReport(filePath);
27
- }
28
-
29
- const eslint = new ESLint(eslintOptions);
30
-
31
- if (filePath && await eslint.isPathIgnored(filePath)) {
32
- return getIgnoredReport(filePath);
33
- }
34
-
35
- const report = await lint(eslint);
36
- return processReport(report, {isQuiet});
37
- };
38
-
39
15
  const globFiles = async (patterns, options) => {
40
16
  const {ignores, extensions, cwd} = (await mergeWithFileConfig(options)).options;
41
17
 
@@ -59,32 +35,76 @@ const getConfig = async options => {
59
35
 
60
36
  const lintText = async (string, options) => {
61
37
  options = await parseOptions(options);
62
- const {filePath, warnIgnored, eslintOptions} = options;
63
- const {ignorePatterns} = eslintOptions.baseConfig;
38
+ const {filePath, warnIgnored, eslintOptions, isQuiet} = options;
39
+ const {cwd, baseConfig: {ignorePatterns}} = eslintOptions;
64
40
 
65
41
  if (typeof filePath !== 'string' && !isEqual(getIgnores({}), ignorePatterns)) {
66
42
  throw new Error('The `ignores` option requires the `filePath` option to be defined.');
67
43
  }
68
44
 
69
- return runEslint(
70
- eslint => eslint.lintText(string, {filePath, warnIgnored}),
71
- options,
72
- );
73
- };
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);
53
+ }
74
54
 
75
- const lintFile = async (filePath, options) => runEslint(
76
- eslint => eslint.lintFiles([filePath]),
77
- await parseOptions({...options, filePath}),
78
- );
55
+ const eslint = new ESLint(eslintOptions);
56
+
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});
63
+ };
79
64
 
80
65
  const lintFiles = async (patterns, options) => {
81
66
  const files = await globFiles(patterns, options);
82
67
 
83
- const reports = await Promise.all(
84
- files.map(filePath => lintFile(filePath, options)),
68
+ const allOptions = await Promise.all(
69
+ files.map(filePath => parseOptions({...options, filePath})),
85
70
  );
86
71
 
87
- const report = mergeReports(reports.filter(({isIgnored}) => !isIgnored));
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);
88
108
 
89
109
  return report;
90
110
  };
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,
@@ -1,11 +1,10 @@
1
+ import {existsSync, promises as fs} from 'node:fs';
1
2
  import process from 'node:process';
2
3
  import os from 'node:os';
3
4
  import path from 'node:path';
4
- import fsExtra from 'fs-extra';
5
5
  import arrify from 'arrify';
6
6
  import {mergeWith, flow, pick} from 'lodash-es';
7
- import pathExists from 'path-exists';
8
- import findUp from 'find-up';
7
+ import {findUpSync} from 'find-up';
9
8
  import findCacheDir from 'find-cache-dir';
10
9
  import prettier from 'prettier';
11
10
  import semver from 'semver';
@@ -22,7 +21,6 @@ import {
22
21
  DEFAULT_EXTENSION,
23
22
  TYPESCRIPT_EXTENSION,
24
23
  ENGINE_RULES,
25
- PRETTIER_CONFIG_OVERRIDE,
26
24
  MODULE_NAME,
27
25
  CONFIG_FILES,
28
26
  MERGE_OPTIONS_CONCAT,
@@ -52,6 +50,7 @@ const DEFAULT_CONFIG = {
52
50
  cache: true,
53
51
  cacheLocation: path.join(cacheLocation(), 'xo-cache.json'),
54
52
  globInputPaths: false,
53
+ resolvePluginsRelativeTo: __dirname,
55
54
  baseConfig: {
56
55
  extends: [
57
56
  resolveLocalConfig('xo'),
@@ -117,22 +116,57 @@ const mergeWithFileConfig = async options => {
117
116
  options = mergeOptions(options, xoOptions, enginesOptions);
118
117
  options.cwd = xoConfigPath && path.dirname(xoConfigPath) !== options.cwd ? path.resolve(options.cwd, path.dirname(xoConfigPath)) : options.cwd;
119
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;
120
122
  if (options.filePath) {
121
- ({options} = applyOverrides(options.filePath, options));
123
+ const overrides = applyOverrides(options.filePath, options);
124
+ options = overrides.options;
125
+
126
+ if (overrides.hash) {
127
+ eslintConfigId += overrides.hash;
128
+ }
122
129
  }
123
130
 
124
131
  const prettierOptions = options.prettier ? await prettier.resolveConfig(searchPath, {editorconfig: true}) || {} : {};
125
132
 
126
133
  if (options.filePath && isTypescript(options.filePath)) {
127
- const tsConfigExplorer = cosmiconfig([], {searchPlaces: ['tsconfig.json'], loaders: {'.json': (_, content) => JSON5.parse(content)}});
128
- const {config: tsConfig, filepath: tsConfigPath} = (await tsConfigExplorer.search(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 || {};
137
+
138
+ let tsConfig;
139
+ let tsConfigPath;
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;
152
+ }
153
+
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
+ }
129
165
 
130
- options.tsConfigPath = await getTsConfigCachePath([options.filePath], options.tsConfigPath, options.cwd);
131
166
  options.ts = true;
132
- await fsExtra.outputJson(options.tsConfigPath, makeTSConfig(tsConfig, tsConfigPath, [options.filePath]));
133
167
  }
134
168
 
135
- return {options, prettierOptions};
169
+ return {options, prettierOptions, eslintConfigId};
136
170
  };
137
171
 
138
172
  /**
@@ -141,10 +175,14 @@ Hashing based on https://github.com/eslint/eslint/blob/cf38d0d939b62f3670cdd59f0
141
175
  */
142
176
  const getTsConfigCachePath = async (files, tsConfigPath, cwd) => {
143
177
  const {version} = await json.load('../package.json');
144
- return path.join(
145
- cacheLocation(cwd),
146
- `tsconfig.${murmur(`${version}_${nodeVersion}_${stringify({files: files.sort(), tsConfigPath})}`).result().toString(36)}.json`,
147
- );
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
+ };
148
186
  };
149
187
 
150
188
  const makeTSConfig = (tsConfig, tsConfigPath, files) => {
@@ -207,7 +245,7 @@ const mergeOptions = (options, xoOptions = {}, enginesOptions = {}) => {
207
245
  ...options,
208
246
  });
209
247
 
210
- mergedOptions.extensions = DEFAULT_EXTENSION.concat(mergedOptions.extensions || []);
248
+ mergedOptions.extensions = [...DEFAULT_EXTENSION, ...(mergedOptions.extensions || [])];
211
249
  mergedOptions.ignores = getIgnores(mergedOptions);
212
250
 
213
251
  return mergedOptions;
@@ -350,6 +388,10 @@ const buildXOConfig = options => config => {
350
388
 
351
389
  // Does not work when the TS definition exports a default const.
352
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';
353
395
  }
354
396
 
355
397
  config.baseConfig.settings['import/resolver'] = gatherImportResolvers(options);
@@ -361,7 +403,7 @@ const buildExtendsConfig = options => config => {
361
403
  if (options.extends && options.extends.length > 0) {
362
404
  const configs = options.extends.map(name => {
363
405
  // Don't do anything if it's a filepath
364
- if (pathExists.sync(name)) {
406
+ if (existsSync(name)) {
365
407
  return name;
366
408
  }
367
409
 
@@ -390,19 +432,11 @@ const buildPrettierConfig = (options, prettierConfig) => config => {
390
432
  // The prettier plugin uses Prettier to format the code with `--fix`
391
433
  config.baseConfig.plugins.push('prettier');
392
434
 
393
- // The prettier config overrides ESLint stylistic rules that are handled by Prettier
394
- 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');
395
437
 
396
438
  // The `prettier/prettier` rule reports errors if the code is not formatted in accordance to Prettier
397
439
  config.baseConfig.rules['prettier/prettier'] = ['error', mergeWithPrettierConfig(options, prettierConfig)];
398
-
399
- // If the user has the React, Flowtype, or Standard plugin, add the corresponding Prettier rule overrides
400
- // See https://github.com/prettier/eslint-config-prettier for the list of plugins overrrides
401
- for (const [plugin, prettierConfig] of Object.entries(PRETTIER_CONFIG_OVERRIDE)) {
402
- if (options.cwd && resolveFrom.silent(plugin, options.cwd)) {
403
- config.baseConfig.extends.push(prettierConfig);
404
- }
405
- }
406
440
  }
407
441
 
408
442
  return config;
@@ -428,7 +462,7 @@ const mergeWithPrettierConfig = (options, prettierOptions) => {
428
462
  {
429
463
  singleQuote: true,
430
464
  bracketSpacing: false,
431
- jsxBracketSameLine: false,
465
+ bracketSameLine: false,
432
466
  trailingComma: 'all',
433
467
  tabWidth: normalizeSpaces(options),
434
468
  useTabs: !options.space,
@@ -441,7 +475,7 @@ const mergeWithPrettierConfig = (options, prettierOptions) => {
441
475
 
442
476
  const buildTSConfig = options => config => {
443
477
  if (options.ts) {
444
- config.baseConfig.extends.push('xo-typescript');
478
+ config.baseConfig.extends.push(require.resolve('eslint-config-xo-typescript'));
445
479
  config.baseConfig.parser = require.resolve('@typescript-eslint/parser');
446
480
  config.baseConfig.parserOptions = {
447
481
  ...config.baseConfig.parserOptions,
@@ -487,11 +521,11 @@ const findApplicableOverrides = (path, overrides) => {
487
521
  const applicable = [];
488
522
 
489
523
  for (const override of overrides) {
490
- hash <<= 1;
524
+ hash <<= 1; // eslint-disable-line no-bitwise
491
525
 
492
526
  if (micromatch.isMatch(path, override.files)) {
493
527
  applicable.push(override);
494
- hash |= 1;
528
+ hash |= 1; // eslint-disable-line no-bitwise
495
529
  }
496
530
  }
497
531
 
@@ -501,7 +535,7 @@ const findApplicableOverrides = (path, overrides) => {
501
535
  };
502
536
  };
503
537
 
504
- const getIgnores = ({ignores}) => DEFAULT_IGNORES.concat(ignores || []);
538
+ const getIgnores = ({ignores}) => [...DEFAULT_IGNORES, ...(ignores || [])];
505
539
 
506
540
  const gatherImportResolvers = options => {
507
541
  let resolvers = {};
@@ -521,7 +555,7 @@ const gatherImportResolvers = options => {
521
555
  webpackResolverSettings = options.webpack === true ? {} : options.webpack;
522
556
  } else if (!(options.webpack === false || resolvers.webpack)) {
523
557
  // If a webpack config file exists, add the import resolver automatically
524
- const webpackConfigPath = findUp.sync('webpack.config.js', {cwd: options.cwd});
558
+ const webpackConfigPath = findUpSync('webpack.config.js', {cwd: options.cwd});
525
559
  if (webpackConfigPath) {
526
560
  webpackResolverSettings = {config: webpackConfigPath};
527
561
  }
@@ -542,13 +576,14 @@ const gatherImportResolvers = options => {
542
576
 
543
577
  const parseOptions = async options => {
544
578
  options = normalizeOptions(options);
545
- const {options: foundOptions, prettierOptions} = await mergeWithFileConfig(options);
579
+ const {options: foundOptions, prettierOptions, eslintConfigId} = await mergeWithFileConfig(options);
546
580
  const {filePath, warnIgnored, ...eslintOptions} = buildConfig(foundOptions, prettierOptions);
547
581
  return {
548
582
  filePath,
549
583
  warnIgnored,
550
584
  isQuiet: options.quiet,
551
585
  eslintOptions,
586
+ eslintConfigId,
552
587
  };
553
588
  };
554
589
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xo",
3
- "version": "0.44.0",
3
+ "version": "0.46.2",
4
4
  "description": "JavaScript/TypeScript linter (ESLint wrapper) with great defaults",
5
5
  "license": "MIT",
6
6
  "repository": "xojs/xo",
@@ -16,6 +16,7 @@
16
16
  "node": ">=12.20"
17
17
  },
18
18
  "scripts": {
19
+ "test:clean": "find ./test -type d -name 'node_modules' -prune -not -path ./test/fixtures/project/node_modules -exec rm -rf '{}' +",
19
20
  "test": "node cli.js && nyc ava"
20
21
  },
21
22
  "files": [
@@ -51,59 +52,59 @@
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": "^1.0.0",
56
- "@typescript-eslint/eslint-plugin": "^4.29.0",
57
- "@typescript-eslint/parser": "^4.29.0",
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.0",
60
- "debug": "^4.3.2",
65
+ "cosmiconfig": "^7.0.1",
61
66
  "define-lazy-prop": "^3.0.0",
62
- "eslint": "^7.32.0",
67
+ "eslint": "^8.1.0",
63
68
  "eslint-config-prettier": "^8.3.0",
64
- "eslint-config-xo": "^0.38.0",
65
- "eslint-config-xo-typescript": "^0.44.0",
69
+ "eslint-config-xo": "^0.39.0",
70
+ "eslint-config-xo-typescript": "^0.47.1",
66
71
  "eslint-formatter-pretty": "^4.1.0",
67
- "eslint-import-resolver-webpack": "^0.13.1",
68
- "eslint-plugin-ava": "^12.0.0",
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.23.4",
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": "^3.4.0",
74
- "eslint-plugin-promise": "^5.1.0",
75
- "eslint-plugin-unicorn": "^35.0.0",
76
- "esm-utils": "^1.1.0",
77
- "find-cache-dir": "^3.3.1",
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": "^12.0.0",
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
89
  "meow": "^10.1.1",
88
90
  "micromatch": "^4.0.4",
89
91
  "open-editor": "^3.0.0",
90
- "path-exists": "^4.0.0",
91
- "prettier": "^2.3.2",
92
+ "prettier": "^2.4.1",
92
93
  "semver": "^7.3.5",
93
94
  "slash": "^4.0.0",
94
95
  "to-absolute-glob": "^2.0.2",
95
- "typescript": "^4.3.5"
96
+ "typescript": "^4.4.4"
96
97
  },
97
98
  "devDependencies": {
98
99
  "ava": "^3.15.0",
99
100
  "eslint-config-xo-react": "^0.25.0",
100
- "eslint-plugin-react": "^7.24.0",
101
+ "eslint-plugin-react": "^7.26.1",
101
102
  "eslint-plugin-react-hooks": "^4.2.0",
102
103
  "execa": "^5.1.1",
103
104
  "nyc": "^15.1.0",
104
105
  "proxyquire": "^2.1.3",
105
106
  "temp-write": "^5.0.0",
106
- "webpack": "^5.49.0"
107
+ "webpack": "^5.60.0"
107
108
  },
108
109
  "xo": {
109
110
  "ignores": [
package/readme.md CHANGED
@@ -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
- The [Prettier options](https://prettier.io/docs/en/options.html) will be read from the [Prettier config](https://prettier.io/docs/en/configuration.html) and if **not set** will be determined as follow:
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
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
- - [jsxBracketSameLine](https://prettier.io/docs/en/options.html#jsx-brackets): `false`
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.html#configuring-plugins).
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.html) or [plugin configs](https://eslint.org/docs/user-guide/configuring#using-the-configuration-from-a-plugin) to override any of the default rules (like `rules` above).
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