From 6463dfe66adce16a876e09671d3fcaf2af483846 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 26 Sep 2019 15:25:37 +0700 Subject: [PATCH 01/13] Move `xo --init` to `npm init xo` --- cli-main.js | 12 +++-------- package.json | 3 +-- readme.md | 53 ++++++++++++++++++------------------------------ test/cli-main.js | 9 -------- 4 files changed, 24 insertions(+), 53 deletions(-) diff --git a/cli-main.js b/cli-main.js index 69b9fae3..5c1a18fc 100755 --- a/cli-main.js +++ b/cli-main.js @@ -13,7 +13,6 @@ const cli = meow(` $ xo [ ...] Options - --init Add XO to your project --fix Automagically fix issues --reporter Reporter to use --env Environment preset [Can be set multiple times] @@ -39,19 +38,16 @@ const cli = meow(` $ xo *.js !foo.js $ xo --space $ xo --env=node --env=mocha - $ xo --init --space $ xo --plugin=react $ xo --plugin=html --extension=html $ echo 'const x=true' | xo --stdin --fix Tips - Put options in package.json instead of using flags so other tools can read it. + - Add XO to your project with \`npm init xo\`. + - Put options in package.json instead of using flags so other tools can read it. `, { booleanDefault: undefined, flags: { - init: { - type: 'boolean' - }, fix: { type: 'boolean' }, @@ -155,9 +151,7 @@ if (options.nodeVersion) { } (async () => { - if (options.init) { - require('xo-init')(); - } else if (options.stdin) { + if (options.stdin) { const stdin = await getStdin(); if (options.fix) { diff --git a/package.json b/package.json index 653054ba..d193496f 100644 --- a/package.json +++ b/package.json @@ -78,8 +78,7 @@ "resolve-from": "^5.0.0", "semver": "^6.3.0", "slash": "^3.0.0", - "update-notifier": "^3.0.1", - "xo-init": "^0.7.0" + "update-notifier": "^3.0.1" }, "devDependencies": { "ava": "^1.1.0", diff --git a/readme.md b/readme.md index 7f4d58ca..afed8caf 100644 --- a/readme.md +++ b/readme.md @@ -31,7 +31,7 @@ Uses [ESLint](https://eslint.org) underneath, so issues regarding rules should b - Includes many useful ESLint plugins, like [`unicorn`](https://github.com/sindresorhus/eslint-plugin-unicorn), [`import`](https://github.com/benmosher/eslint-plugin-import), [`ava`](https://github.com/avajs/eslint-plugin-ava), [`node`](https://github.com/mysticatea/eslint-plugin-node) and more. - Automatically enables rules based on the [`engines`](https://docs.npmjs.com/files/package.json#engines) field in your `package.json`. - Caches results between runs for much better performance. -- Super simple to add XO to a project with `$ xo --init`. +- Super simple to add XO to a project with [`$ npm init xo`](https://github.com/xojs/create-xo). - Fix many issues automagically with `$ xo --fix`. - Open all files with errors at the correct line in your editor with `$ xo --open`. - Specify [indent](#space) and [semicolon](#semicolon) preferences easily without messing with the rule config. @@ -55,7 +55,6 @@ $ xo --help $ xo [ ...] Options - --init Add XO to your project --fix Automagically fix issues --reporter Reporter to use --env Environment preset [Can be set multiple times] @@ -81,13 +80,13 @@ $ xo --help $ xo *.js !foo.js $ xo --space $ xo --env=node --env=mocha - $ xo --init --space $ xo --plugin=react $ xo --plugin=html --extension=html $ echo 'const x=true' | xo --stdin --fix Tips - Put options in package.json instead of using flags so other tools can read it. + - Add XO to your project with `npm init xo`. + - Put options in package.json instead of using flags so other tools can read it. ``` *Note that the CLI will use your local install of XO when available, even when run globally.* @@ -111,35 +110,23 @@ Check out an [example](index.js) and the [ESLint rules](https://github.com/xojs/ The recommended workflow is to add XO locally to your project and run it with the tests. -Simply run `$ xo --init` (with any options) to add XO to your package.json or create one. - -### Before - -```json -{ - "name": "awesome-package", - "scripts": { - "test": "ava" - }, - "devDependencies": { - "ava": "^0.20.0" - } -} -``` - -### After - -```json -{ - "name": "awesome-package", - "scripts": { - "test": "xo && ava" - }, - "devDependencies": { - "ava": "^0.20.0", - "xo": "^0.18.0" - } -} +Simply run `$ npm init xo` (with any options) to add XO to your package.json or create one. + +### Before/after + +```diff + { + "name": "awesome-package", + "scripts": { +- "test": "ava", ++ "test": "xo && ava" + }, + "devDependencies": { +- "ava": "^2.0.0" ++ "ava": "^2.0.0", ++ "xo": "^0.25.0" + } + } ``` Then just run `$ npm test` and XO will be run before your tests. diff --git a/test/cli-main.js b/test/cli-main.js index 64df5bcf..56bf96c9 100644 --- a/test/cli-main.js +++ b/test/cli-main.js @@ -104,15 +104,6 @@ test('quiet option', async t => { t.is(report.warningCount, 0); }); -test('init option', async t => { - const filepath = await tempWrite('{}', 'package.json'); - await main(['--init'], { - cwd: path.dirname(filepath) - }); - const packageJson = fs.readFileSync(filepath, 'utf8'); - t.deepEqual(JSON.parse(packageJson).scripts, {test: 'xo'}); -}); - test('invalid node-engine option', async t => { const filepath = await tempWrite('console.log()\n', 'x.js'); const error = await t.throwsAsync(main(['--node-version', 'v', filepath])); From 1fdc2f98522b46502874cdd75f49c749290d7395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20Unneb=C3=A4ck?= Date: Tue, 19 Nov 2019 05:02:35 +0000 Subject: [PATCH 02/13] Fix Node.js range for `prefer-object-spread` rule (#408) --- index.js | 2 +- lib/options-manager.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 79142e5f..136fc15d 100644 --- a/index.js +++ b/index.js @@ -36,7 +36,7 @@ const runEslint = (paths, options) => { const engine = new eslint.CLIEngine(config); const report = engine.executeOnFiles( paths.filter(path => !engine.isPathIgnored(path)), - config, + config ); return processReport(report, options); }; diff --git a/lib/options-manager.js b/lib/options-manager.js index 0882d459..5804a409 100644 --- a/lib/options-manager.js +++ b/lib/options-manager.js @@ -70,7 +70,7 @@ const ENGINE_RULES = { '7.6.0': 'error' }, 'prefer-object-spread': { - '8.0.0': 'error' + '8.3.0': 'error' }, 'node/prefer-global/text-decoder': { '11.0.0': [ From 5cddaeccf7603e25cc943f09a69e0174ced021a9 Mon Sep 17 00:00:00 2001 From: Richie Bendall Date: Tue, 17 Dec 2019 00:59:21 +1300 Subject: [PATCH 03/13] Replace singular Lodash packages with the main one (#413) --- index.js | 2 +- lib/options-manager.js | 2 +- package.json | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 136fc15d..f4c43129 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,7 @@ const path = require('path'); const eslint = require('eslint'); const globby = require('globby'); -const isEqual = require('lodash.isequal'); +const {isEqual} = require('lodash'); const multimatch = require('multimatch'); const arrify = require('arrify'); const optionsManager = require('./lib/options-manager'); diff --git a/lib/options-manager.js b/lib/options-manager.js index 5804a409..2aa067b8 100644 --- a/lib/options-manager.js +++ b/lib/options-manager.js @@ -2,7 +2,7 @@ const os = require('os'); const path = require('path'); const arrify = require('arrify'); -const mergeWith = require('lodash.mergewith'); +const {mergeWith} = require('lodash'); const multimatch = require('multimatch'); const pathExists = require('path-exists'); const pkgConf = require('pkg-conf'); diff --git a/package.json b/package.json index d193496f..ae8ae1f6 100644 --- a/package.json +++ b/package.json @@ -66,8 +66,7 @@ "get-stdin": "^7.0.0", "globby": "^9.0.0", "has-flag": "^4.0.0", - "lodash.isequal": "^4.5.0", - "lodash.mergewith": "^4.6.2", + "lodash": "^4.17.15", "meow": "^5.0.0", "multimatch": "^4.0.0", "open-editor": "^2.0.1", From e8c43d2d0286fae6291248c249c17616b822c0d7 Mon Sep 17 00:00:00 2001 From: Ayyan Lewis Date: Sat, 25 Jan 2020 16:21:13 +0400 Subject: [PATCH 04/13] Replace destructing import with one-by-one import (#417) --- index.js | 2 +- lib/options-manager.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index f4c43129..c8ea896b 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,7 @@ const path = require('path'); const eslint = require('eslint'); const globby = require('globby'); -const {isEqual} = require('lodash'); +const isEqual = require('lodash/isEqual'); const multimatch = require('multimatch'); const arrify = require('arrify'); const optionsManager = require('./lib/options-manager'); diff --git a/lib/options-manager.js b/lib/options-manager.js index 2aa067b8..e298be84 100644 --- a/lib/options-manager.js +++ b/lib/options-manager.js @@ -2,7 +2,7 @@ const os = require('os'); const path = require('path'); const arrify = require('arrify'); -const {mergeWith} = require('lodash'); +const mergeWith = require('lodash/mergeWith'); const multimatch = require('multimatch'); const pathExists = require('path-exists'); const pkgConf = require('pkg-conf'); From c50eaf0f5f32386678a5fd807e73dfb89fa343ce Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Wed, 12 Feb 2020 18:53:04 +0700 Subject: [PATCH 05/13] Add some overrides to `unicorn/prevent-abbreviations` --- config/plugins.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/config/plugins.js b/config/plugins.js index 6968c709..16417cd7 100644 --- a/config/plugins.js +++ b/config/plugins.js @@ -41,7 +41,13 @@ module.exports = { // checkProperties: false, // checkFilenames: false, // replacements: { - // args: false + // args: false, + // application: { + // app: true + // }, + // applications: { + // apps: true + // } // } // } // ], From ab3e01ae762e727fc00963264463ddea89871937 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Wed, 12 Feb 2020 18:55:19 +0700 Subject: [PATCH 06/13] Temporarily disable the `import/order` rule --- config/plugins.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config/plugins.js b/config/plugins.js index 16417cd7..c9f072bc 100644 --- a/config/plugins.js +++ b/config/plugins.js @@ -148,7 +148,9 @@ module.exports = { // } // ], - 'import/order': 'error', + // Disabled because of https://github.com/benmosher/eslint-plugin-import/pull/1651 and other issues. + // 'import/order': 'error', + 'import/no-unassigned-import': [ 'error', { From d7fa8752dbd0b79d5429b89a718d73a79d59a4d8 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Wed, 12 Feb 2020 19:31:14 +0700 Subject: [PATCH 07/13] Require Node.js 10 --- .travis.yml | 1 - config/plugins.js | 49 +++++++++++++++++++---------------------- lib/options-manager.js | 24 -------------------- package.json | 37 ++++++++++++++++--------------- readme.md | 29 +++++------------------- test/cli-main.js | 2 +- test/lint-files.js | 3 ++- test/lint-text.js | 6 +++-- test/options-manager.js | 3 ++- 9 files changed, 57 insertions(+), 97 deletions(-) diff --git a/.travis.yml b/.travis.yml index c7b4a734..b7e5c87c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,5 @@ language: node_js node_js: - '12' - '10' - - '8' after_success: - './node_modules/.bin/nyc report --reporter=text-lcov | ./node_modules/.bin/coveralls' diff --git a/config/plugins.js b/config/plugins.js index c9f072bc..1838bf9a 100644 --- a/config/plugins.js +++ b/config/plugins.js @@ -32,13 +32,14 @@ module.exports = { rules: { 'no-use-extend-native/no-use-extend-native': 'error', - // TODO: Enable this again in the minor XO release. + // TODO: Enable this again in the next XO release. + // Start with just a manual list of some non-contreversial abbreviations. + // // TODO: Remove this override at some point. // It's just here to ease users into readable variable names. // 'unicorn/prevent-abbreviations': [ // 'error', // { - // checkProperties: false, // checkFilenames: false, // replacements: { // args: false, @@ -53,16 +54,14 @@ module.exports = { // ], 'unicorn/prevent-abbreviations': 'off', + // TODO: Enable this when targeting Node.js 12. + 'unicorn/prefer-flat-map': 'off', + // TODO: Remove this override when the rule is more stable. 'unicorn/consistent-function-scoping': 'off', - // TODO: Change the rule default for this. - 'unicorn/expiring-todo-comments': [ - 'error', - { - allowWarningComments: true - } - ], + // TODO: Enable it again when the bugs are fixed. + 'unicorn/regex-shorthand': 'off', // TODO: Temporarily disabled as the rule is buggy. 'function-call-argument-newline': 'off', @@ -77,10 +76,7 @@ module.exports = { 'promise/no-new-statics': 'error', 'promise/no-return-in-finally': 'error', 'promise/valid-params': 'error', - - // TODO: Enable this when XO targets Node.js 8 - // 'promise/prefer-await-to-then': 'error', - + 'promise/prefer-await-to-then': 'error', 'import/default': 'error', 'import/export': 'error', 'import/extensions': [ @@ -220,7 +216,8 @@ module.exports = { 'error', 'always' ], - // TODO: Enable these when XO targets Node.js 10 + + // Enable these when targeting Node.js 12. // 'node/prefer-global/text-decoder': [ // 'error', // 'always' @@ -229,14 +226,17 @@ module.exports = { // 'error', // 'always' // ], - // 'node/prefer-global/url-search-params': [ - // 'error', - // 'always' - // ], - // 'node/prefer-global/url': [ - // 'error', - // 'always' - // ] + + 'node/prefer-global/url-search-params': [ + 'error', + 'always' + ], + 'node/prefer-global/url': [ + 'error', + 'always' + ], + + // Enable these when targeting Node.js 12. // 'node/prefer-promises/dns': 'error', // 'node/prefer-promises/fs': 'error', @@ -253,9 +253,6 @@ module.exports = { // 'eslint-comments/no-unlimited-disable': 'error', 'eslint-comments/no-unused-disable': 'error', - 'eslint-comments/no-unused-enable': 'error', - - // Disabled by default, enabled only in Node.js >= 11 in option-manager.js - 'unicorn/prefer-flat-map': 'off' + 'eslint-comments/no-unused-enable': 'error' } }; diff --git a/lib/options-manager.js b/lib/options-manager.js index e298be84..a437b475 100644 --- a/lib/options-manager.js +++ b/lib/options-manager.js @@ -66,12 +66,6 @@ const DEFAULT_CONFIG = { * With `engines.node` set to `>=8` the rule `plugin/rule` will be used with the config `{prop: 'node-8-conf'}`. */ const ENGINE_RULES = { - 'promise/prefer-await-to-then': { - '7.6.0': 'error' - }, - 'prefer-object-spread': { - '8.3.0': 'error' - }, 'node/prefer-global/text-decoder': { '11.0.0': [ 'error', @@ -84,30 +78,12 @@ const ENGINE_RULES = { 'always' ] }, - 'node/prefer-global/url-search-params': { - '10.0.0': [ - 'error', - 'always' - ] - }, - 'node/prefer-global/url': { - '10.0.0': [ - 'error', - 'always' - ] - }, 'node/prefer-promises/dns': { '12.0.0': 'error' }, 'node/prefer-promises/fs': { '12.0.0': 'error' }, - 'no-useless-catch': { - '10.0.0': 'error' - }, - 'prefer-named-capture-group': { - '10.0.0': 'error' - }, 'unicorn/prefer-flat-map': { '11.0.0': 'error' } diff --git a/package.json b/package.json index ae8ae1f6..a6728f96 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "JavaScript linter with great defaults", "license": "MIT", "repository": "xojs/xo", + "funding": "https://github.com/sponsors/sindresorhus", "author": { "name": "Sindre Sorhus", "email": "sindresorhus@gmail.com", @@ -11,7 +12,7 @@ }, "bin": "cli.js", "engines": { - "node": ">=8" + "node": ">=10.18" }, "scripts": { "test": "eslint --quiet . && nyc ava" @@ -50,18 +51,18 @@ "dependencies": { "arrify": "^2.0.1", "debug": "^4.1.0", - "eslint": "^6.4.0", - "eslint-config-prettier": "^6.3.0", - "eslint-config-xo": "^0.27.1", - "eslint-formatter-pretty": "^2.0.0", - "eslint-plugin-ava": "^9.0.0", - "eslint-plugin-eslint-comments": "^3.0.1", - "eslint-plugin-import": "^2.18.2", - "eslint-plugin-no-use-extend-native": "^0.4.0", - "eslint-plugin-node": "^10.0.0", - "eslint-plugin-prettier": "^3.1.1", - "eslint-plugin-promise": "^4.0.0", - "eslint-plugin-unicorn": "^12.0.0", + "eslint": "^6.8.0", + "eslint-config-prettier": "^6.10.0", + "eslint-config-xo": "^0.28.0", + "eslint-formatter-pretty": "^3.0.1", + "eslint-plugin-ava": "^10.0.1", + "eslint-plugin-eslint-comments": "^3.1.2", + "eslint-plugin-import": "^2.20.1", + "eslint-plugin-no-use-extend-native": "^0.4.1", + "eslint-plugin-node": "^11.0.0", + "eslint-plugin-prettier": "^3.1.2", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-unicorn": "^16.1.1", "find-cache-dir": "^3.0.0", "get-stdin": "^7.0.0", "globby": "^9.0.0", @@ -75,18 +76,18 @@ "prettier": "^1.15.2", "resolve-cwd": "^3.0.0", "resolve-from": "^5.0.0", - "semver": "^6.3.0", + "semver": "^7.1.3", "slash": "^3.0.0", - "update-notifier": "^3.0.1" + "update-notifier": "^4.0.0" }, "devDependencies": { "ava": "^1.1.0", "coveralls": "^3.0.6", - "eslint-config-xo-react": "^0.20.0", + "eslint-config-xo-react": "^0.22.0", "eslint-plugin-react": "^7.14.3", "eslint-plugin-react-hooks": "^2.0.1", - "execa": "^1.0.0", - "nyc": "^14.1.1", + "execa": "^4.0.0", + "nyc": "^15.0.0", "pify": "^4.0.0", "proxyquire": "^2.1.3", "temp-write": "^4.0.0" diff --git a/readme.md b/readme.md index afed8caf..8f69502f 100644 --- a/readme.md +++ b/readme.md @@ -20,7 +20,6 @@ Uses [ESLint](https://eslint.org) underneath, so issues regarding rules should b ![](https://raw.githubusercontent.com/sindresorhus/eslint-formatter-pretty/master/screenshot.png) - ## Highlights - Beautiful output. @@ -38,14 +37,12 @@ Uses [ESLint](https://eslint.org) underneath, so issues regarding rules should b - Optionally use the [Prettier](https://github.com/prettier/prettier) code style. - Great [editor plugins](#editor-plugins). - ## Install ``` $ npm install --global xo ``` - ## Usage ``` @@ -91,7 +88,6 @@ $ xo --help *Note that the CLI will use your local install of XO when available, even when run globally.* - ## Default code style *Any of these can be [overridden](#rules) if necessary.* @@ -105,7 +101,6 @@ $ xo --help Check out an [example](index.js) and the [ESLint rules](https://github.com/xojs/eslint-config-xo/blob/master/index.js). - ## Workflow The recommended workflow is to add XO locally to your project and run it with the tests. @@ -131,7 +126,6 @@ Simply run `$ npm init xo` (with any options) to add XO to your package.json or Then just run `$ npm test` and XO will be run before your tests. - ## Config You can configure some options in XO by putting it in package.json: @@ -149,7 +143,7 @@ You can configure some options in XO by putting it in package.json: ### envs -Type: `string[]`
+Type: `string[]`\ Default: `['node']` 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. @@ -168,7 +162,7 @@ Some [paths](lib/options-manager.js) are ignored by default, including paths in ### space -Type: `boolean | number`
+Type: `boolean | number`\ Default: `false` *(tab indentation)* Set it to `true` to get 2-space indentation or specify the number of spaces. @@ -185,14 +179,14 @@ Please take a moment to consider if you really need to use this option. ### semicolon -Type: `boolean`
+Type: `boolean`\ Default: `true` *(Semicolons required)* Set it to `false` to enforce no-semicolon style. ### prettier -Type: `boolean`
+Type: `boolean`\ Default: `false` Format code with [Prettier](https://github.com/prettier/prettier). @@ -210,7 +204,7 @@ If contradicting options are set for both Prettier and XO an error will be throw ### nodeVersion -Type: `string | boolean`
+Type: `string | boolean`\ Default: Value of the `engines.node` key in the project `package.json` Enable rules specific to the Node.js versions within the configured range. @@ -249,14 +243,13 @@ ESLint parser. For example, [`babel-eslint`](https://github.com/babel/babel-esli ### esnext -Type: `boolean`
+Type: `boolean`\ Default: `true` Enforce ES2015+ rules. Disabling this will make it not *enforce* ES2015+ syntax and conventions. *ES2015+ is parsed even without this option. You can already use ES2017 features like [`async`/`await`](https://github.com/lukehoban/ecmascript-asyncawait). - ## TypeScript and Flow ### TypeScript @@ -267,7 +260,6 @@ See [eslint-config-xo-typescript#use-with-xo](https://github.com/xojs/eslint-con See [eslint-config-xo-flow#use-with-xo](https://github.com/xojs/eslint-config-xo-flow#use-with-xo) - ## Config Overrides XO makes it easy to override configs for specific files. The `overrides` property must be an array of override objects. Each override object must contain a `files` property which is a glob string, or an array of glob strings. The remaining properties are identical to those described above, and will override the settings of the base config. If multiple override configs match the same file, each matching override is applied in the order it appears in the array. This means the last override in the array takes precedence over earlier ones. Consider the following example: @@ -314,7 +306,6 @@ XO makes it easy to override configs for specific files. The `overrides` propert } ``` - ## Tips ### Using a parent's config @@ -344,7 +335,6 @@ For example, if your project targets Node.js 4 (your `package.json` is configure } ``` - ## FAQ #### What does XO mean? @@ -359,7 +349,6 @@ The [Standard style](https://standardjs.com) is a really cool idea. I too wish w XO is based on ESLint. This project started out as just a shareable ESLint config, but it quickly grew out of that. I wanted something even simpler. Just typing `xo` and be done. No decision-making. No config. I also have some exciting future plans for it. However, you can still get most of the XO benefits while using ESLint directly with the [ESLint shareable config](https://github.com/xojs/eslint-config-xo). - ## Editor plugins - [Sublime Text](https://github.com/xojs/SublimeLinter-contrib-xo) @@ -370,7 +359,6 @@ XO is based on ESLint. This project started out as just a shareable ESLint confi - [Emacs](https://github.com/j-em/xo-emacs) - [WebStorm](https://github.com/jamestalmage/xo-with-webstorm) - ## Build-system plugins - [Gulp](https://github.com/xojs/gulp-xo) @@ -380,7 +368,6 @@ XO is based on ESLint. This project started out as just a shareable ESLint confi - [Metalsmith](https://github.com/blainsmith/metalsmith-xo) - [Fly](https://github.com/lukeed/fly-xo) - ## Configs - [eslint-config-xo](https://github.com/xojs/eslint-config-xo) - ESLint shareable config for XO with tab indent @@ -393,18 +380,15 @@ XO is based on ESLint. This project started out as just a shareable ESLint confi - [eslint-config-xo-typescript](https://github.com/xojs/eslint-config-xo-typescript) - ESLint shareable config for TypeScript - [eslint-config-xo-flow](https://github.com/xojs/eslint-config-xo-flow) - ESLint shareable config for Flow - ## Support - [Twitter](https://twitter.com/sindresorhus) - ## Related - [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) - Various awesome ESLint rules *(Bundled in XO)* - [xo-summary](https://github.com/LitoMore/xo-summary) - Display output from `xo` as a list of style errors, ordered by count - ## Badge Show the world you're using XO → [![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/xojs/xo) @@ -415,7 +399,6 @@ Show the world you're using XO → [![XO code style](https://img.shields.io/badg You can also find some nice dynamic XO badges on [badgen.net](https://badgen.net/#xo). - ## Team [![Sindre Sorhus](https://github.com/sindresorhus.png?size=130)](https://sindresorhus.com) | [![Mario Nebl](https://github.com/marionebl.png?size=130)](https://github.com/marionebl) | [![Pierre Vanduynslager](https://github.com/pvdlg.png?size=130)](https://github.com/pvdlg) diff --git a/test/cli-main.js b/test/cli-main.js index 56bf96c9..b7116b04 100644 --- a/test/cli-main.js +++ b/test/cli-main.js @@ -107,7 +107,7 @@ test('quiet option', async t => { test('invalid node-engine option', async t => { const filepath = await tempWrite('console.log()\n', 'x.js'); const error = await t.throwsAsync(main(['--node-version', 'v', filepath])); - t.is(error.code, 1); + t.is(error.exitCode, 1); }); test('cli option takes precedence over config', async t => { diff --git a/test/lint-files.js b/test/lint-files.js index 67afbfc6..2217863b 100644 --- a/test/lint-files.js +++ b/test/lint-files.js @@ -100,7 +100,8 @@ test('multiple negative patterns should act as positive patterns', async t => { t.deepEqual(paths, ['!!unicorn.js', '!unicorn.js']); }); -test('enable rules based on nodeVersion', async t => { +// TODO: We need a new fixture. Help welcome. +test.failing('enable rules based on nodeVersion', async t => { const {results} = await fn.lintFiles('**/*', {cwd: 'fixtures/engines-overrides'}); // The transpiled file (as specified in `overrides`) should use `await` diff --git a/test/lint-text.js b/test/lint-text.js index d2e9c59f..1774acff 100644 --- a/test/lint-text.js +++ b/test/lint-text.js @@ -212,7 +212,8 @@ test('lint eslintignored files if filename is not given', async t => { t.true(results[0].errorCount > 0); }); -test('enable rules based on nodeVersion', async t => { +// TODO: We need a new fixture. Help welcome. +test.failing('enable rules based on nodeVersion', async t => { const cwd = path.join(__dirname, 'fixtures', 'engines-overrides'); const filename = path.join(cwd, 'promise-then.js'); const text = await readFile(filename, 'utf8'); @@ -224,7 +225,8 @@ test('enable rules based on nodeVersion', async t => { t.false(hasRule(results, 'promise/prefer-await-to-then')); }); -test('enable rules based on nodeVersion in override', async t => { +// TODO: We need a new fixture. Help welcome. +test.failing('enable rules based on nodeVersion in override', async t => { const cwd = path.join(__dirname, 'fixtures', 'engines-overrides'); const filename = path.join(cwd, 'promise-then.js'); const text = await readFile(filename, 'utf8'); diff --git a/test/options-manager.js b/test/options-manager.js index cf412b7c..653bd13b 100644 --- a/test/options-manager.js +++ b/test/options-manager.js @@ -228,7 +228,8 @@ test('buildConfig: nodeVersion: invalid range', t => { t.is(config.rules['promise/prefer-await-to-then'], undefined); }); -test('buildConfig: nodeVersion: >=8', t => { +// TODO: We need a new fixture. Help welcome. +test.failing('buildConfig: nodeVersion: >=8', t => { const config = manager.buildConfig({nodeVersion: '>=8'}); // Include rules for Node.js 8 and above From 08af7c13de9c62e74023780405f4f062944209e1 Mon Sep 17 00:00:00 2001 From: Alexis Tyler Date: Thu, 13 Feb 2020 09:06:30 +1030 Subject: [PATCH 08/13] Fix compatibility with being run in a child process (#405) --- cli-main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli-main.js b/cli-main.js index 5c1a18fc..04467880 100755 --- a/cli-main.js +++ b/cli-main.js @@ -132,7 +132,7 @@ if (typeof options.space === 'string') { const log = report => { const reporter = options.reporter ? xo.getFormatter(options.reporter) : formatterPretty; process.stdout.write(reporter(report.results)); - process.exit(report.errorCount === 0 ? 0 : 1); + process.exitCode = report.errorCount === 0 ? 0 : 1; }; // `xo -` => `xo --stdin` From eb5ea2c201f438b137fac89c14aa87cf19252228 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 13 Feb 2020 05:31:29 +0700 Subject: [PATCH 09/13] Set the `es2020` environment by default Fixes #415 Fixes #406 --- package.json | 2 +- readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a6728f96..0b0c3ebc 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "debug": "^4.1.0", "eslint": "^6.8.0", "eslint-config-prettier": "^6.10.0", - "eslint-config-xo": "^0.28.0", + "eslint-config-xo": "^0.29.0", "eslint-formatter-pretty": "^3.0.1", "eslint-plugin-ava": "^10.0.1", "eslint-plugin-eslint-comments": "^3.1.2", diff --git a/readme.md b/readme.md index 8f69502f..182867ef 100644 --- a/readme.md +++ b/readme.md @@ -144,7 +144,7 @@ You can configure some options in XO by putting it in package.json: ### envs Type: `string[]`\ -Default: `['node']` +Default: `['es2020', 'node']` 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. From c2bb1bf8be1d31c5e6784aa19f7a59b032f8a574 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Thu, 13 Feb 2020 05:48:33 +0700 Subject: [PATCH 10/13] 0.26.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0b0c3ebc..404e9b41 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xo", - "version": "0.25.3", + "version": "0.26.0", "description": "JavaScript linter with great defaults", "license": "MIT", "repository": "xojs/xo", From 9fbdb972b81ab6a7ca1e3cbf46fb1f8455a04f55 Mon Sep 17 00:00:00 2001 From: Pierre Vanduynslager Date: Thu, 13 Feb 2020 03:25:46 -0500 Subject: [PATCH 11/13] Use new eslint-config-prettier override (#422) --- lib/options-manager.js | 20 +++++++++++++------- test/options-manager.js | 3 ++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/options-manager.js b/lib/options-manager.js index a437b475..a09cf23d 100644 --- a/lib/options-manager.js +++ b/lib/options-manager.js @@ -89,6 +89,15 @@ const ENGINE_RULES = { } }; +const PRETTIER_CONFIG_OVERRIDE = { + '@typescript-eslint/eslint-plugin': 'prettier/@typescript-eslint', + 'eslint-plugin-babel': 'prettier/babel', + 'eslint-plugin-flowtype': 'prettier/flowtype', + 'eslint-plugin-react': 'prettier/react', + 'eslint-plugin-standard': 'prettier/standard', + 'eslint-plugin-vue': 'prettier/vue' +}; + // Keep the same behaviour in mergeWith as deepAssign const mergeFn = (previousValue, value) => { if (Array.isArray(previousValue) && Array.isArray(value)) { @@ -281,23 +290,20 @@ const buildConfig = options => { // If the user sets the `prettier` options then add the `prettier` plugin and config if (options.prettier) { - // Disable formatting rules conflicting with Prettier - config.rules['unicorn/number-literal-case'] = 'off'; - // Can be re-enabled when https://github.com/prettier/prettier/issues/4157 is fixed - config.rules['unicorn/no-nested-ternary'] = 'off'; // The prettier plugin uses Prettier to format the code with `--fix` config.plugins = config.plugins.concat('prettier'); // The prettier config overrides ESLint stylistic rules that are handled by Prettier config.baseConfig.extends = config.baseConfig.extends.concat('prettier'); + config.baseConfig.extends = config.baseConfig.extends.concat('prettier/unicorn'); // The `prettier/prettier` rule reports errors if the code is not formatted in accordance to Prettier config.rules['prettier/prettier'] = [ 'error', mergeWithPrettierConfig(options, prettier.resolveConfig.sync(options.cwd || process.cwd()) || {}) ]; // If the user has the React, Flowtype, or Standard plugin, add the corresponding Prettier rule overrides // See https://github.com/prettier/eslint-config-prettier for the list of plugins overrrides - for (const override of ['react', 'flowtype', 'standard']) { - if (options.cwd && resolveFrom.silent(options.cwd, `eslint-plugin-${override}`)) { - config.baseConfig.extends = config.baseConfig.extends.concat(`prettier/${override}`); + for (const [plugin, prettierConfig] of Object.entries(PRETTIER_CONFIG_OVERRIDE)) { + if (options.cwd && resolveFrom.silent(options.cwd, plugin)) { + config.baseConfig.extends = config.baseConfig.extends.concat(prettierConfig); } } } diff --git a/test/options-manager.js b/test/options-manager.js index 653bd13b..6043eff2 100644 --- a/test/options-manager.js +++ b/test/options-manager.js @@ -91,7 +91,8 @@ test('buildConfig: prettier: true', t => { trailingComma: 'none' }]); // eslint-prettier-config must always be last - t.deepEqual(config.baseConfig.extends.slice(-1), ['prettier']); + t.deepEqual(config.baseConfig.extends[config.baseConfig.extends.length - 1], 'prettier/unicorn'); + t.deepEqual(config.baseConfig.extends[config.baseConfig.extends.length - 2], 'prettier'); // Indent rule is not enabled t.is(config.rules.indent, undefined); // Semi rule is not enabled From 74b34bf6e08d81141af818885ef24c25263f87f7 Mon Sep 17 00:00:00 2001 From: Pierre Vanduynslager Date: Thu, 13 Feb 2020 03:42:54 -0500 Subject: [PATCH 12/13] Disable rules based on Node.js version (#423) --- config/plugins.js | 28 ++++++----------- lib/options-manager.js | 69 +++++++++++++++++++++++++++-------------- test/lint-files.js | 3 +- test/lint-text.js | 6 ++-- test/options-manager.js | 55 ++++++++++++++++++++------------ 5 files changed, 93 insertions(+), 68 deletions(-) diff --git a/config/plugins.js b/config/plugins.js index 1838bf9a..2a1d8d0d 100644 --- a/config/plugins.js +++ b/config/plugins.js @@ -54,9 +54,6 @@ module.exports = { // ], 'unicorn/prevent-abbreviations': 'off', - // TODO: Enable this when targeting Node.js 12. - 'unicorn/prefer-flat-map': 'off', - // TODO: Remove this override when the rule is more stable. 'unicorn/consistent-function-scoping': 'off', @@ -216,16 +213,14 @@ module.exports = { 'error', 'always' ], - - // Enable these when targeting Node.js 12. - // 'node/prefer-global/text-decoder': [ - // 'error', - // 'always' - // ], - // 'node/prefer-global/text-encoder': [ - // 'error', - // 'always' - // ], + 'node/prefer-global/text-decoder': [ + 'error', + 'always' + ], + 'node/prefer-global/text-encoder': [ + 'error', + 'always' + ], 'node/prefer-global/url-search-params': [ 'error', @@ -235,11 +230,8 @@ module.exports = { 'error', 'always' ], - - // Enable these when targeting Node.js 12. - // 'node/prefer-promises/dns': 'error', - // 'node/prefer-promises/fs': 'error', - + 'node/prefer-promises/dns': 'error', + 'node/prefer-promises/fs': 'error', 'eslint-comments/disable-enable-pair': [ 'error', { diff --git a/lib/options-manager.js b/lib/options-manager.js index a09cf23d..4121d02d 100644 --- a/lib/options-manager.js +++ b/lib/options-manager.js @@ -43,12 +43,12 @@ const DEFAULT_CONFIG = { }; /** - * Define the rules that are enabled only for specific version of Node.js based on `engines.node` in package.json or the `node-version` option. + * Define the rules config that are overwritten only for specific version of Node.js based on `engines.node` in package.json or the `node-version` option. * * The keys are rule names and the values are an Object with a valid semver (`4.0.0` is valid `4` is not) as keys and the rule configuration as values. * - * Each entry define the rule config and the minimum Node.js version for which to set it. - * The entry with the highest version that is compliant with the `engines.node`/`node-version` range will be used. + * Each entry define the rule config and the maximum Node.js version for which to set it. + * The entry with the lowest version that is compliant with the `engines.node`/`node-version` range will be used. * * @type {Object} * @@ -66,26 +66,50 @@ const DEFAULT_CONFIG = { * With `engines.node` set to `>=8` the rule `plugin/rule` will be used with the config `{prop: 'node-8-conf'}`. */ const ENGINE_RULES = { - 'node/prefer-global/text-decoder': { - '11.0.0': [ - 'error', - 'always' - ] + 'unicorn/prefer-spread': { + '5.0.0': 'off' + }, + 'unicorn/no-new-buffer': { + '5.10.0': 'off' + }, + 'prefer-rest-params': { + '6.0.0': 'off' + }, + 'prefer-destructuring': { + '6.0.0': 'off' + }, + 'promise/prefer-await-to-then': { + '7.6.0': 'off' + }, + 'prefer-object-spread': { + '8.3.0': 'off' + }, + 'node/prefer-global/url-search-params': { + '10.0.0': 'off' + }, + 'node/prefer-global/url': { + '10.0.0': 'off' + }, + 'no-useless-catch': { + '10.0.0': 'off' + }, + 'prefer-named-capture-group': { + '10.0.0': 'off' }, 'node/prefer-global/text-encoder': { - '11.0.0': [ - 'error', - 'always' - ] + '11.0.0': 'off' + }, + 'node/prefer-global/text-decoder': { + '11.0.0': 'off' + }, + 'unicorn/prefer-flat-map': { + '11.0.0': 'off' }, 'node/prefer-promises/dns': { - '12.0.0': 'error' + '11.14.0': 'off' }, 'node/prefer-promises/fs': { - '12.0.0': 'error' - }, - 'unicorn/prefer-flat-map': { - '11.0.0': 'error' + '11.14.0': 'off' } }; @@ -209,13 +233,10 @@ const buildConfig = options => { ); const spaces = normalizeSpaces(options); - if (options.nodeVersion) { - for (const rule of Object.keys(ENGINE_RULES)) { - // Use the rule value for the highest version that is lower or equal to the oldest version of Node.js supported - for (const minVersion of Object.keys(ENGINE_RULES[rule]).sort(semver.compare)) { - if (!semver.intersects(options.nodeVersion, `<${minVersion}`)) { - config.rules[rule] = ENGINE_RULES[rule][minVersion]; - } + for (const [rule, ruleConfig] of Object.entries(ENGINE_RULES)) { + for (const minVersion of Object.keys(ruleConfig).sort(semver.rcompare)) { + if (!options.nodeVersion || semver.intersects(options.nodeVersion, `<${minVersion}`)) { + config.rules[rule] = ruleConfig[minVersion]; } } } diff --git a/test/lint-files.js b/test/lint-files.js index 2217863b..67afbfc6 100644 --- a/test/lint-files.js +++ b/test/lint-files.js @@ -100,8 +100,7 @@ test('multiple negative patterns should act as positive patterns', async t => { t.deepEqual(paths, ['!!unicorn.js', '!unicorn.js']); }); -// TODO: We need a new fixture. Help welcome. -test.failing('enable rules based on nodeVersion', async t => { +test('enable rules based on nodeVersion', async t => { const {results} = await fn.lintFiles('**/*', {cwd: 'fixtures/engines-overrides'}); // The transpiled file (as specified in `overrides`) should use `await` diff --git a/test/lint-text.js b/test/lint-text.js index 1774acff..d2e9c59f 100644 --- a/test/lint-text.js +++ b/test/lint-text.js @@ -212,8 +212,7 @@ test('lint eslintignored files if filename is not given', async t => { t.true(results[0].errorCount > 0); }); -// TODO: We need a new fixture. Help welcome. -test.failing('enable rules based on nodeVersion', async t => { +test('enable rules based on nodeVersion', async t => { const cwd = path.join(__dirname, 'fixtures', 'engines-overrides'); const filename = path.join(cwd, 'promise-then.js'); const text = await readFile(filename, 'utf8'); @@ -225,8 +224,7 @@ test.failing('enable rules based on nodeVersion', async t => { t.false(hasRule(results, 'promise/prefer-await-to-then')); }); -// TODO: We need a new fixture. Help welcome. -test.failing('enable rules based on nodeVersion in override', async t => { +test('enable rules based on nodeVersion in override', async t => { const cwd = path.join(__dirname, 'fixtures', 'engines-overrides'); const filename = path.join(cwd, 'promise-then.js'); const text = await readFile(filename, 'utf8'); diff --git a/test/options-manager.js b/test/options-manager.js index 6043eff2..baa618a1 100644 --- a/test/options-manager.js +++ b/test/options-manager.js @@ -65,7 +65,7 @@ test('buildConfig: space: 4', t => { }); test('buildConfig: semicolon', t => { - const config = manager.buildConfig({semicolon: false}); + const config = manager.buildConfig({semicolon: false, nodeVersion: '12'}); t.deepEqual(config.rules, { semi: ['error', 'never'], 'semi-spacing': ['error', { @@ -203,38 +203,53 @@ test('buildConfig: engines: undefined', t => { const config = manager.buildConfig({}); // Do not include any Node.js version specific rules - t.is(config.rules['prefer-spread'], undefined); - t.is(config.rules['prefer-rest-params'], undefined); - t.is(config.rules['prefer-destructuring'], undefined); - t.is(config.rules['promise/prefer-await-to-then'], undefined); + t.is(config.rules['prefer-object-spread'], 'off'); + t.is(config.rules['prefer-rest-params'], 'off'); + t.is(config.rules['prefer-destructuring'], 'off'); + t.is(config.rules['promise/prefer-await-to-then'], 'off'); + t.is(config.rules['unicorn/prefer-flat-map'], 'off'); + t.is(config.rules['node/prefer-promises/dns'], 'off'); + t.is(config.rules['node/prefer-promises/fs'], 'off'); }); test('buildConfig: nodeVersion: false', t => { const config = manager.buildConfig({nodeVersion: false}); - // Do not include any Node.js version specific rules - t.is(config.rules['prefer-spread'], undefined); - t.is(config.rules['prefer-rest-params'], undefined); - t.is(config.rules['prefer-destructuring'], undefined); - t.is(config.rules['promise/prefer-await-to-then'], undefined); + // Override all the rules specific to Node.js version + t.is(config.rules['prefer-object-spread'], 'off'); + t.is(config.rules['prefer-rest-params'], 'off'); + t.is(config.rules['prefer-destructuring'], 'off'); + t.is(config.rules['promise/prefer-await-to-then'], 'off'); + t.is(config.rules['unicorn/prefer-flat-map'], 'off'); + t.is(config.rules['node/prefer-promises/dns'], 'off'); + t.is(config.rules['node/prefer-promises/fs'], 'off'); }); test('buildConfig: nodeVersion: invalid range', t => { const config = manager.buildConfig({nodeVersion: '4'}); - // Do not include any Node.js version specific rules - t.is(config.rules['prefer-spread'], undefined); - t.is(config.rules['prefer-rest-params'], undefined); - t.is(config.rules['prefer-destructuring'], undefined); - t.is(config.rules['promise/prefer-await-to-then'], undefined); + // Override all the rules specific to Node.js version + t.is(config.rules['prefer-object-spread'], 'off'); + t.is(config.rules['prefer-rest-params'], 'off'); + t.is(config.rules['prefer-destructuring'], 'off'); + t.is(config.rules['promise/prefer-await-to-then'], 'off'); + t.is(config.rules['unicorn/prefer-flat-map'], 'off'); + t.is(config.rules['node/prefer-promises/dns'], 'off'); + t.is(config.rules['node/prefer-promises/fs'], 'off'); }); -// TODO: We need a new fixture. Help welcome. -test.failing('buildConfig: nodeVersion: >=8', t => { +test('buildConfig: nodeVersion: >=6', t => { + const config = manager.buildConfig({nodeVersion: '>=6'}); + + // Turn off rule if we support Node.js below 7.6.0 + t.is(config.rules['promise/prefer-await-to-then'], 'off'); +}); + +test('buildConfig: nodeVersion: >=8', t => { const config = manager.buildConfig({nodeVersion: '>=8'}); - // Include rules for Node.js 8 and above - t.is(config.rules['promise/prefer-await-to-then'], 'error'); + // Do not turn off rule if we support only Node.js above 7.6.0 + t.is(config.rules['promise/prefer-await-to-then'], undefined); }); test('mergeWithPrettierConfig: use `singleQuote`, `trailingComma`, `bracketSpacing` and `jsxBracketSameLine` from `prettier` config if defined', t => { @@ -327,7 +342,7 @@ test('mergeWithPrettierConfig: throw error is `space`/`tabWidth` conflicts', t = test('buildConfig: rules', t => { const rules = {'object-curly-spacing': ['error', 'always']}; - const config = manager.buildConfig({rules}); + const config = manager.buildConfig({rules, nodeVersion: '12'}); t.deepEqual(config.rules, rules); }); From a8f9a345d1ae449490e450d1687eeaea6da425fc Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Fri, 14 Feb 2020 14:18:04 +0700 Subject: [PATCH 13/13] 0.26.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 404e9b41..c33be51c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xo", - "version": "0.26.0", + "version": "0.26.1", "description": "JavaScript linter with great defaults", "license": "MIT", "repository": "xojs/xo",