From 56c4a2cb437e3d09fd6bb555267908984cf440ac Mon Sep 17 00:00:00 2001 From: Dominik Csapak Date: Fri, 22 May 2020 13:37:30 +0200 Subject: [PATCH] upgrade to v7.0.0 Signed-off-by: Dominik Csapak --- Makefile | 2 +- eslint/.eslintrc.js | 183 +- eslint/.github/workflows/ci.yml | 18 +- eslint/CHANGELOG.md | 255 + eslint/Makefile.js | 16 +- eslint/README.md | 14 +- eslint/bin/eslint.js | 192 +- eslint/conf/category-list.json | 1 - eslint/docs/developer-guide/nodejs-api.md | 409 +- eslint/docs/rules/array-callback-return.md | 2 +- eslint/docs/rules/callback-return.md | 2 + eslint/docs/rules/curly.md | 16 +- eslint/docs/rules/global-require.md | 2 + eslint/docs/rules/handle-callback-err.md | 2 + eslint/docs/rules/no-buffer-constructor.md | 2 + eslint/docs/rules/no-inner-declarations.md | 11 +- eslint/docs/rules/no-mixed-requires.md | 2 + eslint/docs/rules/no-new-object.md | 5 +- eslint/docs/rules/no-new-require.md | 2 + eslint/docs/rules/no-path-concat.md | 2 + eslint/docs/rules/no-process-env.md | 2 + eslint/docs/rules/no-process-exit.md | 2 + eslint/docs/rules/no-restricted-modules.md | 2 + eslint/docs/rules/no-return-assign.md | 16 + eslint/docs/rules/no-sync.md | 2 + eslint/docs/rules/object-curly-newline.md | 3 + eslint/docs/rules/prefer-rest-params.md | 4 + eslint/docs/rules/spaced-comment.md | 18 +- eslint/docs/user-guide/README.md | 1 + eslint/docs/user-guide/migrating-to-7.0.0.md | 232 + eslint/lib/api.js | 2 + .../cascading-config-array-factory.js | 12 + eslint/lib/cli-engine/cli-engine.js | 97 +- eslint/lib/cli-engine/config-array-factory.js | 4 +- .../cli-engine/config-array/config-array.js | 2 +- .../cli-engine/config-array/ignore-pattern.js | 8 +- eslint/lib/cli.js | 276 +- eslint/lib/eslint/eslint.js | 656 ++ eslint/lib/eslint/index.js | 7 + eslint/lib/init/autoconfig.js | 8 +- eslint/lib/init/config-initializer.js | 6 +- eslint/lib/init/source-code-utils.js | 4 +- eslint/lib/options.js | 1 - eslint/lib/rule-tester/rule-tester.js | 7 +- eslint/lib/rules/array-callback-return.js | 21 +- eslint/lib/rules/callback-return.js | 4 + eslint/lib/rules/comma-style.js | 11 +- eslint/lib/rules/func-call-spacing.js | 7 +- eslint/lib/rules/getter-return.js | 14 +- eslint/lib/rules/global-require.js | 4 + eslint/lib/rules/handle-callback-err.js | 4 + eslint/lib/rules/key-spacing.js | 2 +- eslint/lib/rules/new-cap.js | 2 +- eslint/lib/rules/newline-per-chained-call.js | 9 +- eslint/lib/rules/no-buffer-constructor.js | 4 + eslint/lib/rules/no-empty-function.js | 2 +- eslint/lib/rules/no-extra-parens.js | 6 +- eslint/lib/rules/no-inner-declarations.js | 70 +- eslint/lib/rules/no-lone-blocks.js | 2 +- eslint/lib/rules/no-mixed-requires.js | 4 + eslint/lib/rules/no-new-object.js | 18 +- eslint/lib/rules/no-new-require.js | 4 + eslint/lib/rules/no-path-concat.js | 4 + eslint/lib/rules/no-process-env.js | 4 + eslint/lib/rules/no-process-exit.js | 4 + eslint/lib/rules/no-restricted-modules.js | 4 + eslint/lib/rules/no-sync.js | 4 + eslint/lib/rules/no-unexpected-multiline.js | 34 +- eslint/lib/rules/no-useless-concat.js | 2 +- .../lib/rules/space-before-function-paren.js | 7 +- eslint/lib/rules/yoda.js | 152 +- eslint/lib/shared/relative-module-resolver.js | 1 + eslint/lib/shared/types.js | 7 + eslint/package.json | 47 +- .../_utils.js => _utils/in-memory-fs.js} | 117 +- eslint/tests/_utils/index.js | 39 + eslint/tests/bench/large.js | 10 +- eslint/tests/bench/small.js | 2 +- eslint/tests/bin/eslint.js | 17 +- .../fix-types/ignore-missing-meta.expected.js | 2 +- .../fixtures/fix-types/ignore-missing-meta.js | 2 +- eslint/tests/lib/_utils.js | 76 - .../cascading-config-array-factory.js | 2 +- eslint/tests/lib/cli-engine/cli-engine.js | 32 +- .../lib/cli-engine/config-array-factory.js | 4 +- .../cli-engine/config-array/ignore-pattern.js | 178 +- .../tests/lib/cli-engine/file-enumerator.js | 2 +- eslint/tests/lib/cli.js | 708 +- eslint/tests/lib/eslint/eslint.js | 6089 +++++++++++++++++ eslint/tests/lib/init/autoconfig.js | 42 +- eslint/tests/lib/init/config-initializer.js | 7 +- eslint/tests/lib/init/npm-utils.js | 2 +- .../linter/code-path-analysis/code-path.js | 2 +- eslint/tests/lib/linter/linter.js | 34 +- eslint/tests/lib/options.js | 5 +- eslint/tests/lib/rule-tester/rule-tester.js | 2 +- .../tests/lib/rules/array-bracket-newline.js | 547 +- .../tests/lib/rules/array-callback-return.js | 249 +- .../tests/lib/rules/array-element-newline.js | 88 + eslint/tests/lib/rules/comma-style.js | 12 +- eslint/tests/lib/rules/func-call-spacing.js | 100 +- eslint/tests/lib/rules/getter-return.js | 121 +- eslint/tests/lib/rules/id-blacklist.js | 2 +- .../lib/rules/implicit-arrow-linebreak.js | 2 +- eslint/tests/lib/rules/indent.js | 24 +- eslint/tests/lib/rules/keyword-spacing.js | 2 +- eslint/tests/lib/rules/new-cap.js | 170 +- .../lib/rules/newline-per-chained-call.js | 188 +- eslint/tests/lib/rules/no-empty-function.js | 63 +- eslint/tests/lib/rules/no-extra-parens.js | 57 +- eslint/tests/lib/rules/no-implied-eval.js | 2 +- .../tests/lib/rules/no-inner-declarations.js | 289 +- eslint/tests/lib/rules/no-new-object.js | 45 +- eslint/tests/lib/rules/no-return-assign.js | 82 +- .../lib/rules/no-unexpected-multiline.js | 71 +- eslint/tests/lib/rules/no-useless-concat.js | 28 +- eslint/tests/lib/rules/object-shorthand.js | 2 +- .../lib/rules/space-before-function-paren.js | 30 +- eslint/tests/lib/rules/yoda.js | 401 +- eslint/tests/lib/shared/runtime-info.js | 2 +- eslint/tests/performance/jshint.js | 8 +- src/eslint.js | 2866 ++++---- 122 files changed, 13062 insertions(+), 2731 deletions(-) create mode 100644 eslint/docs/user-guide/migrating-to-7.0.0.md create mode 100644 eslint/lib/eslint/eslint.js create mode 100644 eslint/lib/eslint/index.js rename eslint/tests/{lib/cli-engine/_utils.js => _utils/in-memory-fs.js} (77%) create mode 100644 eslint/tests/_utils/index.js delete mode 100644 eslint/tests/lib/_utils.js create mode 100644 eslint/tests/lib/eslint/eslint.js diff --git a/Makefile b/Makefile index 147b4d4..05bc8e0 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ DSC=${PACKAGE}_${DEB_VERSION_UPSTREAM_REVISION}.dsc SRCDIR=src UPSTREAM=eslint -UPSTREAMTAG=v7.0.0-alpha.3 +UPSTREAMTAG=v7.0.0 BUILDSRC=${UPSTREAM}-${UPSTREAMTAG} all: ${DEB} diff --git a/eslint/.eslintrc.js b/eslint/.eslintrc.js index 6ab4510..a53fedb 100644 --- a/eslint/.eslintrc.js +++ b/eslint/.eslintrc.js @@ -1,13 +1,44 @@ "use strict"; -const internalFiles = [ - "**/cli-engine/**/*", - "**/init/**/*", - "**/linter/**/*", - "**/rule-tester/**/*", - "**/rules/**/*", - "**/source-code/**/*" -]; +const path = require("path"); + +const INTERNAL_FILES = { + CLI_ENGINE_PATTERN: "lib/cli-engine/**/*", + INIT_PATTERN: "lib/init/**/*", + LINTER_PATTERN: "lib/linter/**/*", + RULE_TESTER_PATTERN: "lib/rule-tester/**/*", + RULES_PATTERN: "lib/rules/**/*", + SOURCE_CODE_PATTERN: "lib/source-code/**/*" +}; + +/** + * Resolve an absolute path or glob pattern. + * @param {string} pathOrPattern the path or glob pattern. + * @returns {string} The resolved path or glob pattern. + */ +function resolveAbsolutePath(pathOrPattern) { + return path.resolve(__dirname, pathOrPattern); +} + +/** + * Create an array of `no-restricted-require` entries for ESLint's core files. + * @param {string} [pattern] The glob pattern to create the entries for. + * @returns {Object[]} The array of `no-restricted-require` entries. + */ +function createInternalFilesPatterns(pattern = null) { + return Object.values(INTERNAL_FILES) + .filter(p => p !== pattern) + .map(p => ({ + name: [ + + // Disallow all children modules. + resolveAbsolutePath(p), + + // Allow the main `index.js` module. + `!${resolveAbsolutePath(p.replace(/\*\*\/\*$/u, "index.js"))}` + ] + })); +} module.exports = { root: true, @@ -22,6 +53,16 @@ module.exports = { parserOptions: { ecmaVersion: 2020 }, + + /* + * it fixes eslint-plugin-jsdoc's reports: "Invalid JSDoc tag name "template" jsdoc/check-tag-names" + * refs: https://github.com/gajus/eslint-plugin-jsdoc#check-tag-names + */ + settings: { + jsdoc: { + mode: "typescript" + } + }, rules: { "eslint-plugin/consistent-output": "error", "eslint-plugin/no-deprecated-context-methods": "error", @@ -84,106 +125,90 @@ module.exports = { { files: ["lib/*"], rules: { - "no-restricted-modules": ["error", { - patterns: [ - ...internalFiles - ] - }] + "node/no-restricted-require": ["error", [ + ...createInternalFilesPatterns() + ]] } }, { - files: ["lib/cli-engine/**/*"], + files: [INTERNAL_FILES.CLI_ENGINE_PATTERN], rules: { - "no-restricted-modules": ["error", { - patterns: [ - ...internalFiles, - "**/init" - ] - }] + "node/no-restricted-require": ["error", [ + ...createInternalFilesPatterns(INTERNAL_FILES.CLI_ENGINE_PATTERN), + resolveAbsolutePath("lib/init/index.js") + ]] } }, { - files: ["lib/init/**/*"], + files: [INTERNAL_FILES.INIT_PATTERN], rules: { - "no-restricted-modules": ["error", { - patterns: [ - ...internalFiles, - "**/rule-tester" - ] - }] + "node/no-restricted-require": ["error", [ + ...createInternalFilesPatterns(INTERNAL_FILES.INIT_PATTERN), + resolveAbsolutePath("lib/rule-tester/index.js") + ]] } }, { - files: ["lib/linter/**/*"], + files: [INTERNAL_FILES.LINTER_PATTERN], rules: { - "no-restricted-modules": ["error", { - patterns: [ - ...internalFiles, - "fs", - "**/cli-engine", - "**/init", - "**/rule-tester" - ] - }] + "node/no-restricted-require": ["error", [ + ...createInternalFilesPatterns(INTERNAL_FILES.LINTER_PATTERN), + "fs", + resolveAbsolutePath("lib/cli-engine/index.js"), + resolveAbsolutePath("lib/init/index.js"), + resolveAbsolutePath("lib/rule-tester/index.js") + ]] } }, { - files: ["lib/rules/**/*"], + files: [INTERNAL_FILES.RULES_PATTERN], rules: { - "no-restricted-modules": ["error", { - patterns: [ - ...internalFiles, - "fs", - "**/cli-engine", - "**/init", - "**/linter", - "**/rule-tester", - "**/source-code" - ] - }] + "node/no-restricted-require": ["error", [ + ...createInternalFilesPatterns(INTERNAL_FILES.RULES_PATTERN), + "fs", + resolveAbsolutePath("lib/cli-engine/index.js"), + resolveAbsolutePath("lib/init/index.js"), + resolveAbsolutePath("lib/linter/index.js"), + resolveAbsolutePath("lib/rule-tester/index.js"), + resolveAbsolutePath("lib/source-code/index.js") + ]] } }, { files: ["lib/shared/**/*"], rules: { - "no-restricted-modules": ["error", { - patterns: [ - ...internalFiles, - "**/cli-engine", - "**/init", - "**/linter", - "**/rule-tester", - "**/source-code" - ] - }] + "node/no-restricted-require": ["error", [ + ...createInternalFilesPatterns(), + resolveAbsolutePath("lib/cli-engine/index.js"), + resolveAbsolutePath("lib/init/index.js"), + resolveAbsolutePath("lib/linter/index.js"), + resolveAbsolutePath("lib/rule-tester/index.js"), + resolveAbsolutePath("lib/source-code/index.js") + ]] } }, { - files: ["lib/source-code/**/*"], + files: [INTERNAL_FILES.SOURCE_CODE_PATTERN], rules: { - "no-restricted-modules": ["error", { - patterns: [ - ...internalFiles, - "fs", - "**/cli-engine", - "**/init", - "**/linter", - "**/rule-tester", - "**/rules" - ] - }] + "node/no-restricted-require": ["error", [ + ...createInternalFilesPatterns(INTERNAL_FILES.SOURCE_CODE_PATTERN), + "fs", + resolveAbsolutePath("lib/cli-engine/index.js"), + resolveAbsolutePath("lib/init/index.js"), + resolveAbsolutePath("lib/linter/index.js"), + resolveAbsolutePath("lib/rule-tester/index.js"), + resolveAbsolutePath("lib/rules/index.js") + ]] } }, { - files: ["lib/rule-tester/**/*"], + files: [INTERNAL_FILES.RULE_TESTER_PATTERN], rules: { - "no-restricted-modules": ["error", { - patterns: [ - ...internalFiles, - "**/cli-engine", - "**/init" - ] - }] + "node/no-restricted-require": ["error", [ + ...createInternalFilesPatterns(INTERNAL_FILES.RULE_TESTER_PATTERN), + resolveAbsolutePath("lib/cli-engine/index.js"), + resolveAbsolutePath("lib/init/index.js") + ]] } } ] diff --git a/eslint/.github/workflows/ci.yml b/eslint/.github/workflows/ci.yml index f1da882..2674b57 100644 --- a/eslint/.github/workflows/ci.yml +++ b/eslint/.github/workflows/ci.yml @@ -25,21 +25,13 @@ jobs: name: Test strategy: matrix: - os: [ubuntu-latest, windows-latest, macOS-latest] - node: [13.x, 12.x, 10.x, "10.12.0"] - exclude: + os: [ubuntu-latest] + node: [14.x, 13.x, 12.x, 10.x, "10.12.0"] + include: - os: windows-latest - node: "10.12.0" - - os: windows-latest - node: 10.x - - os: windows-latest - node: 13.x - - os: macOS-latest - node: "10.12.0" - - os: macOS-latest - node: 10.x + node: "12.x" - os: macOS-latest - node: 13.x + node: "12.x" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 diff --git a/eslint/CHANGELOG.md b/eslint/CHANGELOG.md index feaeee4..3ab86f0 100644 --- a/eslint/CHANGELOG.md +++ b/eslint/CHANGELOG.md @@ -1,3 +1,258 @@ +v7.0.0 - May 8, 2020 + +* [`b98d8bd`](https://github.com/eslint/eslint/commit/b98d8bda4630fe8278c5aa2b6650630770568fe5) Upgrade: eslint-release@2.0.0 (#13271) (Kai Cataldo) +* [`4c0b028`](https://github.com/eslint/eslint/commit/4c0b028c55fc1674b374efe0bc6dd22c02b4ac88) Fix: remove Node.js and CommonJS category from build process (#13242) (Kai Cataldo) +* [`401a687`](https://github.com/eslint/eslint/commit/401a68799d9d15145e1c7d92ee04644abec2f15a) Chore: fix rules list for prereleases (#13230) (Kai Cataldo) +* [`4ef6158`](https://github.com/eslint/eslint/commit/4ef61580736353f700ab9e4e29f005b5ac552c78) Breaking: espree@7.0.0 (#13270) (Kai Cataldo) +* [`b5c8d73`](https://github.com/eslint/eslint/commit/b5c8d7389bb2c5d4eae850b866832d099187818b) Docs: update 7.0.0 migration guide for consistency (#13267) (Kai Cataldo) +* [`356fdb4`](https://github.com/eslint/eslint/commit/356fdb46aa118ba3d81cee93e8c058a7c98acaf9) Docs: add migration guide (#12692) (Toru Nagashima) +* [`015edf6`](https://github.com/eslint/eslint/commit/015edf6467e33c67b904db037a674d71957a6865) Sponsors: Sync README with website (ESLint Jenkins) +* [`fdfa364`](https://github.com/eslint/eslint/commit/fdfa364c1b0d88689d02cbe8ae848a3e323209aa) 7.0.0-rc.0 (ESLint Jenkins) +* [`8d1b4db`](https://github.com/eslint/eslint/commit/8d1b4db9932cf7c3289187afbb3866a83f9b3f8c) Build: changelog update for 7.0.0-rc.0 (ESLint Jenkins) +* [`0b1d65a`](https://github.com/eslint/eslint/commit/0b1d65a45aa5dfe08cd596c420490e81b546317e) Update: Improve report location for array-callback-return (refs #12334) (#13109) (Milos Djermanovic) +* [`d85e291`](https://github.com/eslint/eslint/commit/d85e291d1b56960373031f2562547df7285444f7) Fix: yoda left string fix for exceptRange (fixes #12883) (#13052) (Anix) +* [`2ce6bed`](https://github.com/eslint/eslint/commit/2ce6bed04cad376003f70447ece4b6578c142bfd) Chore: added tests for nested arrays (#13145) (Anix) +* [`d3aac53`](https://github.com/eslint/eslint/commit/d3aac532f6a24c633f85dedf0e552eabd22d0956) Update: report backtick loc in no-unexpected-multiline (refs #12334) (#13142) (Milos Djermanovic) +* [`8e7a2d9`](https://github.com/eslint/eslint/commit/8e7a2d93595cfe0c1597af0e7873853369251c0b) Fix: func-call-spacing "never" reports wrong message (fixes #13190) (#13193) (Milos Djermanovic) +* [`bcafd0f`](https://github.com/eslint/eslint/commit/bcafd0f8508e19ab8087a35fac7b97fc4295df3e) Update: Add ESLint API (refs eslint/rfcs#40) (#12939) (Kai Cataldo) +* [`3eeae56`](https://github.com/eslint/eslint/commit/3eeae565bfb0834a31e5d3a253a17bbf4027cf88) Upgrade: some (dev) deps (#13155) (薛定谔的猫) +* [`6b7030b`](https://github.com/eslint/eslint/commit/6b7030b1a1e1e3d1a3953cfa9722074d6a6fc1a9) Chore: Run tests on Node.js v14 (#13210) (fisker Cheung) +* [`ebc28d7`](https://github.com/eslint/eslint/commit/ebc28d76658f1f3e4e8d56e70a25752b5d4a6686) Fix: Remove default .js from --ext CLI option (#13176) (Brandon Mills) +* [`5c1bdeb`](https://github.com/eslint/eslint/commit/5c1bdebcf728062fd41583886830c89b65485df9) Update: Improve report location for getter-return (refs #12334) (#13164) (Milos Djermanovic) +* [`56d2bee`](https://github.com/eslint/eslint/commit/56d2beea0ea0b6395a6d4a3e116ea6a964ff92f3) Docs: fix typos (#13204) (Nitin Kumar) +* [`e13256e`](https://github.com/eslint/eslint/commit/e13256e395cc413ce45a66c8562621d48440d8f4) Chore: use espree.latestEcmaVersion in config-initializer (#13157) (Kai Cataldo) +* [`e4f57b7`](https://github.com/eslint/eslint/commit/e4f57b7d7b8b7441a2217a217dcda1e7bfff516a) Chore: add nested array tests for array-element-newline (#13161) (Anix) +* [`63ac918`](https://github.com/eslint/eslint/commit/63ac91877668205aaa50495a9615806967e6e4cf) Sponsors: Sync README with website (ESLint Jenkins) +* [`516f253`](https://github.com/eslint/eslint/commit/516f253729daeeb9da5de5e9b38606ff9c1aae71) Docs: Adds import example for object-curly-newline rule (refs #12018) (#13177) (Luke Lewis) +* [`5a0e84e`](https://github.com/eslint/eslint/commit/5a0e84e9498a946a3e8491a370e012354e087fe0) Sponsors: Sync README with website (ESLint Jenkins) +* [`b398e0b`](https://github.com/eslint/eslint/commit/b398e0b9ff455e4e7d70f19d5ccea5819c21eb86) Docs: add rule comment in prefer-rest-params doc (#13191) (YeonJuan) +* [`ffaa4ba`](https://github.com/eslint/eslint/commit/ffaa4ba5e2bb1a51a78a2b2c028cbe2efc9e5165) Sponsors: Sync README with website (ESLint Jenkins) +* [`932869b`](https://github.com/eslint/eslint/commit/932869b3251b04c4ad5fa72279cb2a56a3e67b55) Sponsors: Sync README with website (ESLint Jenkins) +* [`54630f0`](https://github.com/eslint/eslint/commit/54630f0063fa66318476473757b5912465d4caf2) Sponsors: Sync README with website (ESLint Jenkins) +* [`c9a5035`](https://github.com/eslint/eslint/commit/c9a503571a4662f6c2d31cabc7fd7819ec388150) Fix: newBasePath should be an absolute path (fixes #12850) (#13078) (Nick Harris) +* [`e7c1d4b`](https://github.com/eslint/eslint/commit/e7c1d4b2ac56149a517d4b0000230348a641f1d3) Update: deprecate Node.js & CommonJS rules (#12898) (Kai Cataldo) +* [`95e1c70`](https://github.com/eslint/eslint/commit/95e1c70cebde210a990ee786ec7ab1c8e522edb8) Upgrade: levn@0.4.1 (fixes #9366) (#13140) (Kai Cataldo) +* [`c41de38`](https://github.com/eslint/eslint/commit/c41de382a330d4789488fd2dcf6db5a3163bb5d2) Docs: fix curly multi-or-nest examples with comments (refs #12972) (#13151) (Milos Djermanovic) +* [`57221d5`](https://github.com/eslint/eslint/commit/57221d58c60e757062242f30ed574e0502fc7c31) Docs: fix eslint comment in no-inner-declarations examples (#13152) (Milos Djermanovic) +* [`301b450`](https://github.com/eslint/eslint/commit/301b450ecd1985ae84d1915124f4638ab2a2e6de) Fix: no-extra-parens export default sequence expression false positive (#13096) (Milos Djermanovic) +* [`af4472f`](https://github.com/eslint/eslint/commit/af4472fed794b86e63730702e4b27294a4118d09) Update: Improve report location for new-cap (refs #12334) (#13136) (Milos Djermanovic) +* [`bfa811c`](https://github.com/eslint/eslint/commit/bfa811ceee801fe8ba212a5c879e13743146e909) Fix: init error in extending recommended config (fixes #12707) (#12738) (YeonJuan) +* [`5dfd4eb`](https://github.com/eslint/eslint/commit/5dfd4eb50d84077a57950f119e5de8976070e49a) Docs: examples with arrow functions in no-return-assign (fixes #13135) (#13138) (Anix) +* [`adc8fa8`](https://github.com/eslint/eslint/commit/adc8fa88c9f223b984a3519ed159a055bf933b18) Docs: clarify exceptions in spaced-comment (fixes #13032) (#13126) (Anix) +* [`a784dac`](https://github.com/eslint/eslint/commit/a784dac8bc26f31edbca869b16d6ad91bd4e5cc4) Update: Improve report location newline-per-chained-call (refs #12334) (#13116) (Milos Djermanovic) +* [`5e07574`](https://github.com/eslint/eslint/commit/5e07574a91861fdcab6888b4c6829868030c3a39) Update: Improve report location for space-before-function-paren (#13128) (Chiawen Chen) +* [`d0d32a8`](https://github.com/eslint/eslint/commit/d0d32a8c2a6c60c7e6e2d32a054e5987efd4c263) Sponsors: Sync README with website (ESLint Jenkins) +* [`17e2fe4`](https://github.com/eslint/eslint/commit/17e2fe425168e675fe7607182615e50527eedf53) Update: Improve error message for fatal fixer errors (#13120) (Brad Zacher) +* [`7551f0c`](https://github.com/eslint/eslint/commit/7551f0c6fd12f0295cc7d6377bf1899c092e79d8) Fix: no check for shadowed Object (fixes #12809) (#13115) (Anix) +* [`988d842`](https://github.com/eslint/eslint/commit/988d8428811934943ce13b2d8b833d795ac2bb4a) Fix: add end location to report in no-useless-concat (refs #12334) (#13110) (Milos Djermanovic) +* [`0518ebb`](https://github.com/eslint/eslint/commit/0518ebb840b5bf2d646dbd8a9b3e246e7069eb79) Sponsors: Sync README with website (ESLint Jenkins) +* [`252fd41`](https://github.com/eslint/eslint/commit/252fd4133805f003151e0220b5d89bbb70cfacb1) Sponsors: Sync README with website (ESLint Jenkins) +* [`438dcbb`](https://github.com/eslint/eslint/commit/438dcbb981214d67e8848006ce45afc0fbfa1705) Update: Improve report location for comma-style (refs #12334) (#13111) (Chiawen Chen) +* [`85b7254`](https://github.com/eslint/eslint/commit/85b72548c0c893499c787b82f404348e1b50354d) Update: no-inner-declarations false negative in non-block (fixes #12222) (#13062) (Anix) +* [`6631ef1`](https://github.com/eslint/eslint/commit/6631ef1678bbdd48680fb3025c3692abd0aeec4a) Update: Improve report location for no-empty-function (refs #12334) (#13121) (Milos Djermanovic) +* [`b228f95`](https://github.com/eslint/eslint/commit/b228f958afaf507d6f6f99c90b2075b395733839) Docs: Update README team and sponsors (ESLint Jenkins) +* [`119b7ce`](https://github.com/eslint/eslint/commit/119b7ce97993b84df5af4e4f82e5102e430dfff1) 7.0.0-alpha.3 (ESLint Jenkins) +* [`2531b80`](https://github.com/eslint/eslint/commit/2531b802a1527ea1084905641a17befcb50350c7) Build: changelog update for 7.0.0-alpha.3 (ESLint Jenkins) +* [`78c8cda`](https://github.com/eslint/eslint/commit/78c8cda5a5d82f5f8548c4528a6438d29756bb71) Breaking: RuleTester Improvements (refs eslint/rfcs#25) (#12955) (Milos Djermanovic) +* [`e0f1b6c`](https://github.com/eslint/eslint/commit/e0f1b6c3d62f725b99b8c07654603b559ba43ba9) Update: stricter array index check in no-magic-numbers (fixes #12845) (#12851) (Milos Djermanovic) +* [`362713c`](https://github.com/eslint/eslint/commit/362713c04aa89092b2b98a77fa94a75b3c933fc6) Update: Improve report location for template-curly-spacing (#12813) (Milos Djermanovic) +* [`29f32db`](https://github.com/eslint/eslint/commit/29f32db68c921a857e17ae627923d87b9c8708de) Fix: Change error message logic for implicit file ignore (fixes #12873) (#12878) (Scott Hardin) +* [`eb1a43c`](https://github.com/eslint/eslint/commit/eb1a43ce3113c906800192c3ef766d2ff188776f) Fix: require-await ignore async generators (fixes #12459) (#13048) (Anix) +* [`920465b`](https://github.com/eslint/eslint/commit/920465b5d8d291df8bce7eef8a066b1dd43d8cae) Fix: getNameLocationInGlobalDirectiveComment end location (refs #12334) (#13086) (Milos Djermanovic) +* [`ae14a02`](https://github.com/eslint/eslint/commit/ae14a021bbea5117fe366ae4ed235e8f08dc65a8) Fix: add end location to report in no-extra-bind (refs #12334) (#13083) (Milos Djermanovic) +* [`105384c`](https://github.com/eslint/eslint/commit/105384ccc11dcd7303104fb5a347eda1d4d48357) Update: report operator location in operator-linebreak (refs #12334) (#13102) (Milos Djermanovic) +* [`081e240`](https://github.com/eslint/eslint/commit/081e24022a40d9a026ddd2a85c68cb8c3f18dc53) Update: support globalThis in no-implied-eval (fixes #12670) (#13105) (YeonJuan) +* [`185982d`](https://github.com/eslint/eslint/commit/185982d5615d325ae8b45c2360d5847df4098bda) Breaking: improve plugin resolving (refs eslint/rfcs#47) (#12922) (Toru Nagashima) +* [`0c20bc0`](https://github.com/eslint/eslint/commit/0c20bc068e608869981a10711bba88ffde1539d8) Fix: check assignment property target in camelcase (fixes #13025) (#13027) (YeonJuan) +* [`8d50a7d`](https://github.com/eslint/eslint/commit/8d50a7d82244d4912f3eab62a66c81c76c44a9da) Fix: add end location to report in no-prototype-builtins (refs #12334) (#13087) (Milos Djermanovic) +* [`3e4e7f8`](https://github.com/eslint/eslint/commit/3e4e7f8d429dc70b78c0aefaa37f9c22a1e5fc0f) Fix: incorrect logic for required parens in no-extra-boolean-cast fixer (#13061) (Milos Djermanovic) +* [`6c069f9`](https://github.com/eslint/eslint/commit/6c069f907a04268b671c7f949c04a508df9d42a3) Docs: Add comments to code block in example (#13089) (Kibeom Kwon) +* [`ee1f053`](https://github.com/eslint/eslint/commit/ee1f0531aa534ef9182cf8586f55ad82aaa55e75) Docs: Fix typo (#13092) (Max Coplan) +* [`76324ac`](https://github.com/eslint/eslint/commit/76324ace67893c3d7e38a369114d6128df9ffb65) Docs: Add further reading to rule (#13084) (Max Coplan) +* [`a1370ab`](https://github.com/eslint/eslint/commit/a1370abed72e1fb93e601816d981fa6e46204afb) Update: Report constructor calls in no-obj-calls (#12909) (Milos Djermanovic) +* [`2111c52`](https://github.com/eslint/eslint/commit/2111c52443e7641caad291e0daaea8e2fe6c4562) Upgrade: esquery@1.2.0 (#13076) (Milos Djermanovic) +* [`3f7c9bf`](https://github.com/eslint/eslint/commit/3f7c9bf19615122fb776cdd13da532d860bd945a) Docs: clarify variables option in no-use-before-define (fixes #12986) (#13017) (Anix) +* [`aef9488`](https://github.com/eslint/eslint/commit/aef9488c07d3da4becff6e8d6918824b53086d86) Fix: allow references to external globals in id-blacklist (fixes #12567) (#12987) (Milos Djermanovic) +* [`4955c50`](https://github.com/eslint/eslint/commit/4955c50dc9e89b4077b28e35f065d45e89bdccd7) Fix: remove type arguments in prefer-object-spread (fixes #13058) (#13063) (Milos Djermanovic) +* [`48b122f`](https://github.com/eslint/eslint/commit/48b122f450b14dd27afef4c8115c69fca4f02be1) Breaking: change relative paths with --config (refs eslint/rfcs#37) (#12887) (Toru Nagashima) +* [`085979f`](https://github.com/eslint/eslint/commit/085979fed9a5e24a87e4d92ee79272b59211d03f) Update: consider env in no-implied-eval (fixes #12733) (#12757) (YeonJuan) +* [`9ac5b9e`](https://github.com/eslint/eslint/commit/9ac5b9edf06d16a9216c2c9b02bb20b6aa8ed0ab) Docs: Clarify node_modules is ignored by default (fixes #13006) (#13054) (Mika Kuijpers) +* [`0de91f3`](https://github.com/eslint/eslint/commit/0de91f39a97cdf530cb64edbadde57a2bb41ca86) Docs: removed correct code from incorrect eg (#13060) (Anix) +* [`dbe357d`](https://github.com/eslint/eslint/commit/dbe357de199620675446464f6fd0e35064c4d247) Fix: check template literal in prefer-numeric-literals (fixes #13045) (#13046) (YeonJuan) +* [`2260611`](https://github.com/eslint/eslint/commit/2260611e616bdc2a0bf16d508b60a50772ce7fbb) Fix: added async in allow method in no-empty-function (fixes #12768) (#13036) (Anix) +* [`f3788af`](https://github.com/eslint/eslint/commit/f3788aff615edfbfb7afc4c491bb07d20737531b) Sponsors: Sync README with website (ESLint Jenkins) +* [`e90b29b`](https://github.com/eslint/eslint/commit/e90b29bb1f41d4e5767e33d03db5984f036586ed) Update: Allow testing Suggestions with data in RuleTester (fixes #12606) (#12635) (Milos Djermanovic) +* [`7224eee`](https://github.com/eslint/eslint/commit/7224eee3ff4b4378d3439deb038bf34b116fa48b) Fix: no-plusplus allow comma operands in for afterthought (fixes #13005) (#13024) (Milos Djermanovic) +* [`7598cf8`](https://github.com/eslint/eslint/commit/7598cf816bd854de1dd7d96cf00dec6ecc4564ac) Fix: Newline before eof when creating config via --init (#12952) (Andreas Lind) +* [`183e300`](https://github.com/eslint/eslint/commit/183e3006841c29efdd245c45a72e6cefac86ae35) Update: support globalThis (refs #12670) (#12774) (YeonJuan) +* [`af7af9d`](https://github.com/eslint/eslint/commit/af7af9d32ea8073d2d0d726cc8551351261a170f) Docs: Update governance (#13055) (Nicholas C. Zakas) +* [`31d5eb3`](https://github.com/eslint/eslint/commit/31d5eb3e60b6c2ee26976721f07cc89d60867659) Sponsors: Sync README with website (ESLint Jenkins) +* [`95613d4`](https://github.com/eslint/eslint/commit/95613d46b7900b3d9757a7f6959d5dfb262f29fc) Upgrade: espree@6.2.1 (#13026) (Kai Cataldo) +* [`f1525dc`](https://github.com/eslint/eslint/commit/f1525dc45dfdbbe31e724671270785b41cffc6bd) Sponsors: Sync README with website (ESLint Jenkins) +* [`0243549`](https://github.com/eslint/eslint/commit/0243549db4d237cb78e720d55a9cae89b91f6830) Fix: camelcase false positive with computed property (fixes #13022) (#13023) (Milos Djermanovic) +* [`bc0c02c`](https://github.com/eslint/eslint/commit/bc0c02cd0368559c7a7b1510eb4620022a4cc31c) Chore: added lock files to gitignore (#13015) (Anix) +* [`79ac6cd`](https://github.com/eslint/eslint/commit/79ac6cd2d8e4c32e03dfea10a957806845058573) Docs: added less confusing explanation for func-style (fixes #12900) (#13004) (Anix) +* [`26267ed`](https://github.com/eslint/eslint/commit/26267ed70270ef746b785c09e267f815bf7c596a) Chore: update GitHub Actions (#12984) (Pig Fang) +* [`1299705`](https://github.com/eslint/eslint/commit/12997058626b5167ba4b9d2ae0d0ea965a01c4be) Update: acorn version (#13016) (Idan Avrahami) +* [`6cef0d5`](https://github.com/eslint/eslint/commit/6cef0d50a0d131bc8897799a54e1af1d38606db4) Fix: Check division operator in astUtils.canTokensBeAdjacent (#12879) (Milos Djermanovic) +* [`fd8e1f5`](https://github.com/eslint/eslint/commit/fd8e1f52110cada542a120750236fd1ec8779336) Sponsors: Sync README with website (ESLint Jenkins) +* [`472025f`](https://github.com/eslint/eslint/commit/472025f2814d0360fe8d4cddbcba049982e1cd43) Chore: update space-before-function-paren in eslint-config-eslint (#12966) (Kai Cataldo) +* [`fd8c42a`](https://github.com/eslint/eslint/commit/fd8c42ada52f0ae2488ad96ee8fee675f63134ce) Sponsors: Sync README with website (ESLint Jenkins) +* [`c615eae`](https://github.com/eslint/eslint/commit/c615eae0be3c6c167c6f77ec7a73e7adca0ecef0) 7.0.0-alpha.2 (ESLint Jenkins) +* [`2e8f193`](https://github.com/eslint/eslint/commit/2e8f193ebc970f07ea37d267c8bce02d74c285a8) Build: changelog update for 7.0.0-alpha.2 (ESLint Jenkins) +* [`a5b41a7`](https://github.com/eslint/eslint/commit/a5b41a75b57572e97476b06ad39b768e15b9d844) Update: no-restricted-modules handle TemplateLiteral (fixes #12926) (#12927) (Michal Piechowiak) +* [`051567a`](https://github.com/eslint/eslint/commit/051567adca7ca56d691bcda76f54ed72e3eae367) Update: check identifier in array pattern in id-length (fixes #12832) (#12839) (YeonJuan) +* [`4af06fc`](https://github.com/eslint/eslint/commit/4af06fc49029dac5c9acfd53f01fd9527bfbb4aa) Breaking: Test with an unknown error property should fail in RuleTester (#12096) (Milos Djermanovic) +* [`9038a29`](https://github.com/eslint/eslint/commit/9038a29569548c0563c29dbe9f7dae280ff3addd) Update: func-names `as-needed` false negative with AssignmentPattern (#12932) (Milos Djermanovic) +* [`afde78b`](https://github.com/eslint/eslint/commit/afde78b125747ce5ad9e5f871122a0d370dd0152) Fix: curly removes necessary braces between if and else (fixes #12928) (#12943) (Milos Djermanovic) +* [`4797fb2`](https://github.com/eslint/eslint/commit/4797fb2c29db97bc5cd23b40e5a9235fef1ea06a) Fix: arrow-body-style crash with object literal body (fixes #12884) (#12886) (Milos Djermanovic) +* [`afa9aac`](https://github.com/eslint/eslint/commit/afa9aac6de9444e935a55b46311e5b5a58f86063) Breaking: class default `true` computed-property-spacing (fixes #12812) (#12915) (Milos Djermanovic) +* [`b8e20d3`](https://github.com/eslint/eslint/commit/b8e20d33b7d6645266beef09cd231afaf5054328) Docs: Mention TypeScript's compiler check (#12903) (Benny Neugebauer) +* [`de14d1c`](https://github.com/eslint/eslint/commit/de14d1ce0cf422b4100a686abb906f53fbf905b3) Fix: wrap-iife autofix removes mandatory parentheses (#12905) (Milos Djermanovic) +* [`5775b06`](https://github.com/eslint/eslint/commit/5775b06a74573cbe068bea56b1d2376421f5e831) Fix: Optionally allow underscores in member names (#11972) (Edgardo Avilés) +* [`e997f32`](https://github.com/eslint/eslint/commit/e997f32b936463ac38e8b0034f764c47502e56a8) Docs: Updated arrow-parens for minor grammar issue (#12962) (Tom) +* [`7d52151`](https://github.com/eslint/eslint/commit/7d52151bcd5d5524f240588436a8808162be187f) Breaking: classes default `true` in accessor-pairs (fixes #12811) (#12919) (Milos Djermanovic) +* [`cf14355`](https://github.com/eslint/eslint/commit/cf14355e34a6757e15806f8e493553bd7110fb36) Docs: Fix links to custom parsers doc (#12965) (Brandon Mills) +* [`0dfc3ff`](https://github.com/eslint/eslint/commit/0dfc3ff9fb228e1d9b1df99de50033ce9140ac24) Fix: add end location to report in no-eval (#12960) (Milos Djermanovic) +* [`f479f6f`](https://github.com/eslint/eslint/commit/f479f6fe2eb95156e22bebfccb39a7fc1f19e9c0) Docs: References correct config file name (#12885) (Patrick Kilgore) +* [`78182e4`](https://github.com/eslint/eslint/commit/78182e45e0178d9eac2591944ef4daee21d2cb44) Breaking: Add new rules to eslint:recommended (fixes #12911) (#12920) (Milos Djermanovic) +* [`8d5c434`](https://github.com/eslint/eslint/commit/8d5c434f721142be74c7515aaa935668a15b79b1) Docs: fix category descriptions for site generation (fixes #12894) (#12930) (Kai Cataldo) +* [`05380e6`](https://github.com/eslint/eslint/commit/05380e6e7e19a79d26ea6d6b44a8d5ee7cde51c8) Docs: Remove claim about semicolons from docs (#12944) (Luke Sikina) +* [`aa9d725`](https://github.com/eslint/eslint/commit/aa9d72525054e641231a2960a2e37b3716228056) Sponsors: Sync README with website (ESLint Jenkins) +* [`7747177`](https://github.com/eslint/eslint/commit/7747177f8504961059b7c56bdb70a820bd1114c1) Update: report rename id destructuring in id-blacklist (fixes #12807) (#12923) (YeonJuan) +* [`6423e11`](https://github.com/eslint/eslint/commit/6423e11c0bedd3b4e661ab554316bdeb1fc1ee3c) Breaking: check unnamed default export in func-names (fixes #12194) (#12195) (Chiawen Chen) +* [`77df505`](https://github.com/eslint/eslint/commit/77df505d9a08496a8eaefeca4f885f54a21d5c5e) Update: check renaming identifier in object destructuring (fixes 12827) (#12881) (YeonJuan) +* [`41de9df`](https://github.com/eslint/eslint/commit/41de9df41a30a4300243bfe4ca26f716a787b2fc) Update: enforceForLogicalOperands no-extra-boolean-cast (fixes #12137) (#12734) (jmoore914) +* [`98a9b01`](https://github.com/eslint/eslint/commit/98a9b019e52f344c1a6bc2f704c227f89692afe3) 7.0.0-alpha.1 (ESLint Jenkins) +* [`e9f1073`](https://github.com/eslint/eslint/commit/e9f1073f748f8c22f754d145b1ba193e7ce82215) Build: changelog update for 7.0.0-alpha.1 (ESLint Jenkins) +* [`f702b1a`](https://github.com/eslint/eslint/commit/f702b1a54820d2b4e4993dcded99f551a98b490f) Add missing plugin reference (#12796) (Eduard Bardají Puig) +* [`1f1424c`](https://github.com/eslint/eslint/commit/1f1424cb200e609d58645f6c54739e11469e6265) Fix: fix inconsistently works option in no-extra-parens (fixes #12717) (#12843) (YeonJuan) +* [`b5adcaa`](https://github.com/eslint/eslint/commit/b5adcaab93f388f1d8e9d35d6f5e8c2994240850) Fix: make YieldExpression throwable (fixes #12880) (#12897) (YeonJuan) +* [`4293229`](https://github.com/eslint/eslint/commit/4293229709dde105692347241513766e953664dd) Breaking: use-isnan enforceForSwitchCase default `true` (fixes #12810) (#12913) (Milos Djermanovic) +* [`cf38d0d`](https://github.com/eslint/eslint/commit/cf38d0d939b62f3670cdd59f0143fd896fccd771) Breaking: change default ignore pattern (refs eslint/rfcs#51) (#12888) (Toru Nagashima) +* [`bfe1dc4`](https://github.com/eslint/eslint/commit/bfe1dc4e614640cb69032afbb5851c1493f537e3) Breaking: no-dupe-class-members checks some computed keys (fixes #12808) (#12837) (Milos Djermanovic) +* [`1ee6b63`](https://github.com/eslint/eslint/commit/1ee6b6388305a8671c8d4c3cf30c2dbf18a1ff7e) Update: check template literal in yoda (fixes #12863) (#12876) (YeonJuan) +* [`0ae7041`](https://github.com/eslint/eslint/commit/0ae70417af70ad565450d9e779ae78c05f6a51e2) Sponsors: Sync README with website (ESLint Jenkins) +* [`1907e57`](https://github.com/eslint/eslint/commit/1907e57362f7d5f7a02a5a78f24ac3347f868e93) Chore: add Twitter and Open Collective badge (#12877) (Kai Cataldo) +* [`95e0586`](https://github.com/eslint/eslint/commit/95e0586c95e6953d11983d1d11891ed30318109a) Fix: id-blacklist false positives on renamed imports (#12831) (Milos Djermanovic) +* [`b7f0d20`](https://github.com/eslint/eslint/commit/b7f0d200c125b3d233ccafaabdaa61c66dc60e3c) Chore: Use consistent badge style (#12825) (fisker Cheung) +* [`3734a66`](https://github.com/eslint/eslint/commit/3734a669983de7d5107ba8f39b291c6e3116489f) Chore: use ids for messages (#12859) (Gareth Jones) +* [`824d235`](https://github.com/eslint/eslint/commit/824d23585c205f2993716585cb6f55dfbe4a33f0) Docs: add errorOnUnmatchedPattern option to CLIEngine (#12834) (Arthur Denner) +* [`439c833`](https://github.com/eslint/eslint/commit/439c83342c364ba3ce5168d54e165b1fe3e35630) Update: array-callback-return checks Array.forEach (fixes #12551) (#12646) (Gabriel R Sezefredo) +* [`33efd71`](https://github.com/eslint/eslint/commit/33efd71d7c3496b4b9cbfe006280527064940826) Docs: Fix spelling mistakes (#12861) (Bryan Mishkin) +* [`a5b3c5f`](https://github.com/eslint/eslint/commit/a5b3c5fa4edc2312534af0d9f0911f68144f8baf) Docs: Update README team and sponsors (ESLint Jenkins) +* [`0cae920`](https://github.com/eslint/eslint/commit/0cae9203a8077184ad6beb00028fd376cc806f34) Chore: rename shadowed global (#12862) (Tony Brix) +* [`055b80d`](https://github.com/eslint/eslint/commit/055b80dc89bba2a5ab22f7a27deb40135b5cacfa) Chore: Fix typo in complexity.js (#12864) (Kyle Shevlin) +* [`d6c313d`](https://github.com/eslint/eslint/commit/d6c313de794ea0671d35b5027288cd2ea456c0b5) Docs: add missing eslint comments in prefer-regex-literals examples (#12858) (Milos Djermanovic) +* [`7d551ab`](https://github.com/eslint/eslint/commit/7d551ab8cbf2d3a802b0d0685379aa075fe9d7c0) Sponsors: Sync README with website (ESLint Jenkins) +* [`540de8e`](https://github.com/eslint/eslint/commit/540de8e34d08f4b17b66b06d13927acb7552357a) Sponsors: Sync README with website (ESLint Jenkins) +* [`ac5d515`](https://github.com/eslint/eslint/commit/ac5d515252c226f030fa646bf7635a12a3b856fe) Sponsors: Sync README with website (ESLint Jenkins) +* [`dadc892`](https://github.com/eslint/eslint/commit/dadc8927820576c60b48bcbc7d5a9056a6279d30) Fix: operator-assignment crash on adjacent division assignment (#12844) (Milos Djermanovic) +* [`9f39ef0`](https://github.com/eslint/eslint/commit/9f39ef0d4b398c7c09ceef89128da448680d587c) Chore: typo in PULL_REQUEST_TEMPLATE.md (#12848) (Balázs Orbán) +* [`a60d5cd`](https://github.com/eslint/eslint/commit/a60d5cd2325ca72fa1b272b0b90ccd7904b92062) Chore: typo in no-irregular-whitespace.js (#12847) (Balázs Orbán) +* [`691d19a`](https://github.com/eslint/eslint/commit/691d19a2872bffab50c0024d488b8cb33504cc83) Chore: add missing `ecmaVersion` 2020/11 type value (#12833) (Piotr Błażejewicz (Peter Blazejewicz)) +* [`516ddb3`](https://github.com/eslint/eslint/commit/516ddb37d39502e5a8c88a017ae3bad05046f41d) Sponsors: Sync README with website (ESLint Jenkins) +* [`a9d92f9`](https://github.com/eslint/eslint/commit/a9d92f991d69902a9150db373590e2ed54dec988) Fix: radix rule crash on disabled globals (#12824) (Milos Djermanovic) +* [`03a69db`](https://github.com/eslint/eslint/commit/03a69dbe86d5b5768a310105416ae726822e3c1c) Update: check template literal in no-proto, no-iterator (fixes #12801) (#12806) (YeonJuan) +* [`562e784`](https://github.com/eslint/eslint/commit/562e7845946a490f2e173a0bcd1af631070a4eef) Update: fix no-magic-numbers false negative with ignoreArrayIndexes (#12805) (Milos Djermanovic) +* [`f5b9656`](https://github.com/eslint/eslint/commit/f5b96564f732962f46755adbb33c49fae9af6a92) Chore: add test for no-constant-condition (#12836) (Milos Djermanovic) +* [`533c114`](https://github.com/eslint/eslint/commit/533c1140dc98bebdc3ae8334ab2e6c27c7df0c21) Fix: multiline-comment-style rule add extra space after * (fixes #12785) (#12823) (Karthik Priyadarshan) +* [`0460748`](https://github.com/eslint/eslint/commit/0460748cda67ddc4a4cb0db3cdf187a742d09bf8) Update: check template literal in no-constant-condition (fixes #12815) (#12816) (YeonJuan) +* [`80309c3`](https://github.com/eslint/eslint/commit/80309c3791188ac5d1c4eebc99ede323a55336e6) Fix: no-constant-condition doesn't introspect arrays (fixes #12225) (#12307) (Sean Gray) +* [`10a79a6`](https://github.com/eslint/eslint/commit/10a79a672b42d51539bcd6ace482be7afa5f34f8) Chore: Adopt `eslint-plugin/require-meta-docs-description` internally (#12762) (Bryan Mishkin) +* [`aea1729`](https://github.com/eslint/eslint/commit/aea172998ec4e2af1d9186b6767c3f34428945f4) Docs: Fix anchor links in Node.js API TOC (#12821) (Matija Marohnić) +* [`1b8a3ce`](https://github.com/eslint/eslint/commit/1b8a3ce15237b9085f2761dcf73655207e6169a6) Sponsors: Sync README with website (ESLint Jenkins) +* [`c2217c0`](https://github.com/eslint/eslint/commit/c2217c04d6c82b160a21b00fca39c8acec543403) Breaking: make `radix` rule stricter (#12608) (fisker Cheung) +* [`e03a7b3`](https://github.com/eslint/eslint/commit/e03a7b311cb9ddf55914b1496683609bd564de2f) 7.0.0-alpha.0 (ESLint Jenkins) +* [`c8c1b2b`](https://github.com/eslint/eslint/commit/c8c1b2b2efadfcd3c04aaf15bd793c5b4dd84cb6) Build: changelog update for 7.0.0-alpha.0 (ESLint Jenkins) +* [`1aa021d`](https://github.com/eslint/eslint/commit/1aa021d77fdd2c68d7b7d2f4603252110c414b32) Breaking: lint `overrides` files (fixes #10828, refs eslint/rfcs#20) (#12677) (Toru Nagashima) +* [`e59d775`](https://github.com/eslint/eslint/commit/e59d77536bd8db57e8a75cd5245f6f320aa699f8) Update: Separate pattern/expression options for array-element-newline (#11796) (jacobparish) +* [`f8f115a`](https://github.com/eslint/eslint/commit/f8f115af6e10539e6cad485588187cb11917f8c9) Update: treat comment tokens in template-curly-spacing (fixes #12744) (#12775) (YeonJuan) +* [`b50179d`](https://github.com/eslint/eslint/commit/b50179def3fedbd95fdeab25e32c2511867eb760) Breaking: Check assignment targets in no-extra-parens (#12490) (Milos Djermanovic) +* [`de4fa7c`](https://github.com/eslint/eslint/commit/de4fa7c65c7befefa64d1605550267071ee56a5d) Fix: wrong indent at tagged template in indent (fixes #12122) (#12596) (YeonJuan) +* [`d86a5bb`](https://github.com/eslint/eslint/commit/d86a5bbb1987d858d4963f647b0af5c1fd924b4f) Breaking: Check flatMap in array-callback-return (fixes #12235) (#12765) (Milos Djermanovic) +* [`cf46df7`](https://github.com/eslint/eslint/commit/cf46df70158a4ed4c09d5c9d655c07dc6df3ff5e) Breaking: description in directive comments (refs eslint/rfcs#33) (#12699) (Toru Nagashima) +* [`7350589`](https://github.com/eslint/eslint/commit/7350589a5bdfc9d75d1ff19364f476eec44c3911) Breaking: some rules recognize bigint literals (fixes #11803) (#12701) (Toru Nagashima) +* [`16a1c1f`](https://github.com/eslint/eslint/commit/16a1c1f79bc0a4cc1c3a87e98d220041de88bb93) Fix: prefer-object-spread false positives with accessors (fixes #12086) (#12784) (Milos Djermanovic) +* [`f9774ec`](https://github.com/eslint/eslint/commit/f9774ec11b0ebe63fb16a97b97890efb84699548) New: Add default-case-last rule (fixes #12665) (#12668) (Milos Djermanovic) +* [`9a93d9e`](https://github.com/eslint/eslint/commit/9a93d9ef389c49a133c4df4f9843927f5f806423) Update: fix no-restricted-imports export * false negative (fixes #12737) (#12798) (Milos Djermanovic) +* [`0d8c0af`](https://github.com/eslint/eslint/commit/0d8c0affe1ae7ecf228cdf91b490921f7e9d1fdb) Fix: improve report location for computed-property-spacing (#12795) (Milos Djermanovic) +* [`756b95d`](https://github.com/eslint/eslint/commit/756b95d59fb97cd9b3f3adf98cebf529fe4842a2) Fix: id-blacklist should ignore ObjectPatterns (fixes #12787) (#12792) (JP Ramassini) +* [`561b6d4`](https://github.com/eslint/eslint/commit/561b6d4726f3e77dd40ba0d340ca7f08429cd2eb) Chore: add prerequisites checklist to PR template (#12790) (Kai Cataldo) +* [`01ff791`](https://github.com/eslint/eslint/commit/01ff7910af86fc45b76e883bc9ab00c9be3b50ac) Fix: Display pipe character correctly in test output (#12771) (Brad Zacher) +* [`68becbd`](https://github.com/eslint/eslint/commit/68becbd84e8a0693409d36f2be10679c483e233a) Update: fix no-restricted-imports importNames reporting (fixes #12282) (#12711) (Andrey Alexandrov) +* [`ae959b6`](https://github.com/eslint/eslint/commit/ae959b691fb148ac8b474c924c8cb01ef61c436d) Update: report double extra parens in no-extra-parens (fixes #12127) (#12697) (YeonJuan) +* [`02fcc05`](https://github.com/eslint/eslint/commit/02fcc055710e8d69d986f1e682cae2014ad881e2) Docs: Improve sort-keys (#12791) (Steven Vachon) +* [`35cd958`](https://github.com/eslint/eslint/commit/35cd95893be0afd8c954cbcf9268c2aa045b7d5b) Sponsors: Sync README with website (ESLint Jenkins) +* [`a1d999c`](https://github.com/eslint/eslint/commit/a1d999c6b4e51c317ad409110be7be214ff9f7c6) New: Add no-useless-backreference rule (fixes #12673) (#12690) (Milos Djermanovic) +* [`b2c6209`](https://github.com/eslint/eslint/commit/b2c62096a8b318765d618cab222240f87d73063b) Update: fix no-extra-parens CallExpression#callee false negatives (#12743) (Milos Djermanovic) +* [`14b42c3`](https://github.com/eslint/eslint/commit/14b42c386be3387c415267b789f277e1294d4567) Update: fix counting jsx comment len in max-len (fixes #12213) (#12661) (YeonJuan) +* [`e632c31`](https://github.com/eslint/eslint/commit/e632c31d7e5363f1347b787702ecd4a85f5c11a2) Upgrade: several dependencies (#12753) (Toru Nagashima) +* [`25eb703`](https://github.com/eslint/eslint/commit/25eb703c8758563988ffb497a53f89a3ed345399) Docs: fix dead link in max-lines rule docs (#12766) (Christian Bundy) +* [`9dfc850`](https://github.com/eslint/eslint/commit/9dfc8501fb1956c90dc11e6377b4cb38a6bea65d) Chore: Refactor to use messageId in a number of rules (#12715) (Brad Zacher) +* [`1118fce`](https://github.com/eslint/eslint/commit/1118fceb49af3436b8dcd0c6089f913cedf9a329) Breaking: runtime-deprecation on '~/.eslintrc' (refs eslint/rfcs#32) (#12678) (Toru Nagashima) +* [`2c28fbb`](https://github.com/eslint/eslint/commit/2c28fbbb563a44282bef0c9fcc9be29d611cc83b) Breaking: drop Node.js 8 support (refs eslint/rfcs#44) (#12700) (Toru Nagashima) +* [`098b67d`](https://github.com/eslint/eslint/commit/098b67d04a4b4dc8ef4faa6434c6ef5abbde3ed3) Docs: fix minor typo in brace-style.md (#12749) (Marko Kaznovac) +* [`313f70a`](https://github.com/eslint/eslint/commit/313f70ac9a3cf5d1558d2427b00dd75666e18cf4) Update: add outerIIFEBody: "off" to indent rule (fixes #11377) (#12706) (Kai Cataldo) +* [`b77b858`](https://github.com/eslint/eslint/commit/b77b8585e33fc4bb438a0e11ca8177c7eb91dbd8) Chore: fix separateRequires tests for one-var rule (#12709) (Milos Djermanovic) +* [`e4df7df`](https://github.com/eslint/eslint/commit/e4df7dfb0199badb61d2c03ff4f7e4be735279d9) Chore: add JSDoc types for RuleTester test cases (#12325) (Chiawen Chen) +* [`b23ad0d`](https://github.com/eslint/eslint/commit/b23ad0d789a909baf8d7c41a35bc53df932eaf30) Docs: change a broken link in working-with-rules.md (#12732) (Damien Cassou) +* [`3fa39a6`](https://github.com/eslint/eslint/commit/3fa39a633b37544fec7cedfc1f2b0e62e9312a72) Update: Handle locally unsupported regex in computed property keys (#12056) (Milos Djermanovic) +* [`4744397`](https://github.com/eslint/eslint/commit/474439720258b1a64b305c31588f803104fa4aaf) Chore: remove unused code in max-lines-per-function (#12659) (YeonJuan) +* [`4e16957`](https://github.com/eslint/eslint/commit/4e169576a526023ee297d5bc8b37eedba229f63d) Build: update browser build (#12693) (Toru Nagashima) +* [`00ddfff`](https://github.com/eslint/eslint/commit/00ddfffe6b4b4244e4680b0f92f2c6697fad136f) Fix: Windows path parsing for JUnit (fixes #12507) (#12509) (Michael Wall) +* [`985dac3`](https://github.com/eslint/eslint/commit/985dac35e3c367f0f99d1f0e766e06a1d9818dd4) New: Add no-restricted-exports rule (fixes #10428) (#12546) (Milos Djermanovic) +* [`1aff21b`](https://github.com/eslint/eslint/commit/1aff21bb54da44cef0b6e378a34a74265863b930) Fix: no-mixed-spaces-and-tabs reports multiline strings (#12566) (Milos Djermanovic) +* [`8f1020f`](https://github.com/eslint/eslint/commit/8f1020ff711b0c57d590bf666e2841f64186d083) Update: no-void add an option to allow void as a statement (#12613) (Brad Zacher) +* [`bb6cf50`](https://github.com/eslint/eslint/commit/bb6cf5082623ffb67bb1495fee52c0610ee5f421) Update: Add offsetTernaryExpressions option for indent rule (#12556) (Adam Stankiewicz) +* [`39f5a45`](https://github.com/eslint/eslint/commit/39f5a453579b2ad732212edeb71f84ecb0991f97) Chore: add test cases for for-direction (#12698) (YeonJuan) +* [`b340304`](https://github.com/eslint/eslint/commit/b3403045e535921df6d34785a4ce053e14ba27fd) Chore: Add extra test, improve docs (#12492) (Kevin Partington) +* [`827259e`](https://github.com/eslint/eslint/commit/827259ea009f98a0fdf3f7ebf1bfb6cd661ce28d) Build: package.json update for eslint-config-eslint release (ESLint Jenkins) +v7.0.0-rc.0 - April 24, 2020 + +* [`0b1d65a`](https://github.com/eslint/eslint/commit/0b1d65a45aa5dfe08cd596c420490e81b546317e) Update: Improve report location for array-callback-return (refs #12334) (#13109) (Milos Djermanovic) +* [`d85e291`](https://github.com/eslint/eslint/commit/d85e291d1b56960373031f2562547df7285444f7) Fix: yoda left string fix for exceptRange (fixes #12883) (#13052) (Anix) +* [`2ce6bed`](https://github.com/eslint/eslint/commit/2ce6bed04cad376003f70447ece4b6578c142bfd) Chore: added tests for nested arrays (#13145) (Anix) +* [`d3aac53`](https://github.com/eslint/eslint/commit/d3aac532f6a24c633f85dedf0e552eabd22d0956) Update: report backtick loc in no-unexpected-multiline (refs #12334) (#13142) (Milos Djermanovic) +* [`8e7a2d9`](https://github.com/eslint/eslint/commit/8e7a2d93595cfe0c1597af0e7873853369251c0b) Fix: func-call-spacing "never" reports wrong message (fixes #13190) (#13193) (Milos Djermanovic) +* [`bcafd0f`](https://github.com/eslint/eslint/commit/bcafd0f8508e19ab8087a35fac7b97fc4295df3e) Update: Add ESLint API (refs eslint/rfcs#40) (#12939) (Kai Cataldo) +* [`3eeae56`](https://github.com/eslint/eslint/commit/3eeae565bfb0834a31e5d3a253a17bbf4027cf88) Upgrade: some (dev) deps (#13155) (薛定谔的猫) +* [`6b7030b`](https://github.com/eslint/eslint/commit/6b7030b1a1e1e3d1a3953cfa9722074d6a6fc1a9) Chore: Run tests on Node.js v14 (#13210) (fisker Cheung) +* [`ebc28d7`](https://github.com/eslint/eslint/commit/ebc28d76658f1f3e4e8d56e70a25752b5d4a6686) Fix: Remove default .js from --ext CLI option (#13176) (Brandon Mills) +* [`5c1bdeb`](https://github.com/eslint/eslint/commit/5c1bdebcf728062fd41583886830c89b65485df9) Update: Improve report location for getter-return (refs #12334) (#13164) (Milos Djermanovic) +* [`56d2bee`](https://github.com/eslint/eslint/commit/56d2beea0ea0b6395a6d4a3e116ea6a964ff92f3) Docs: fix typos (#13204) (Nitin Kumar) +* [`e13256e`](https://github.com/eslint/eslint/commit/e13256e395cc413ce45a66c8562621d48440d8f4) Chore: use espree.latestEcmaVersion in config-initializer (#13157) (Kai Cataldo) +* [`e4f57b7`](https://github.com/eslint/eslint/commit/e4f57b7d7b8b7441a2217a217dcda1e7bfff516a) Chore: add nested array tests for array-element-newline (#13161) (Anix) +* [`63ac918`](https://github.com/eslint/eslint/commit/63ac91877668205aaa50495a9615806967e6e4cf) Sponsors: Sync README with website (ESLint Jenkins) +* [`516f253`](https://github.com/eslint/eslint/commit/516f253729daeeb9da5de5e9b38606ff9c1aae71) Docs: Adds import example for object-curly-newline rule (refs #12018) (#13177) (Luke Lewis) +* [`5a0e84e`](https://github.com/eslint/eslint/commit/5a0e84e9498a946a3e8491a370e012354e087fe0) Sponsors: Sync README with website (ESLint Jenkins) +* [`b398e0b`](https://github.com/eslint/eslint/commit/b398e0b9ff455e4e7d70f19d5ccea5819c21eb86) Docs: add rule comment in prefer-rest-params doc (#13191) (YeonJuan) +* [`ffaa4ba`](https://github.com/eslint/eslint/commit/ffaa4ba5e2bb1a51a78a2b2c028cbe2efc9e5165) Sponsors: Sync README with website (ESLint Jenkins) +* [`932869b`](https://github.com/eslint/eslint/commit/932869b3251b04c4ad5fa72279cb2a56a3e67b55) Sponsors: Sync README with website (ESLint Jenkins) +* [`54630f0`](https://github.com/eslint/eslint/commit/54630f0063fa66318476473757b5912465d4caf2) Sponsors: Sync README with website (ESLint Jenkins) +* [`c9a5035`](https://github.com/eslint/eslint/commit/c9a503571a4662f6c2d31cabc7fd7819ec388150) Fix: newBasePath should be an absolute path (fixes #12850) (#13078) (Nick Harris) +* [`e7c1d4b`](https://github.com/eslint/eslint/commit/e7c1d4b2ac56149a517d4b0000230348a641f1d3) Update: deprecate Node.js & CommonJS rules (#12898) (Kai Cataldo) +* [`95e1c70`](https://github.com/eslint/eslint/commit/95e1c70cebde210a990ee786ec7ab1c8e522edb8) Upgrade: levn@0.4.1 (fixes #9366) (#13140) (Kai Cataldo) +* [`c41de38`](https://github.com/eslint/eslint/commit/c41de382a330d4789488fd2dcf6db5a3163bb5d2) Docs: fix curly multi-or-nest examples with comments (refs #12972) (#13151) (Milos Djermanovic) +* [`57221d5`](https://github.com/eslint/eslint/commit/57221d58c60e757062242f30ed574e0502fc7c31) Docs: fix eslint comment in no-inner-declarations examples (#13152) (Milos Djermanovic) +* [`301b450`](https://github.com/eslint/eslint/commit/301b450ecd1985ae84d1915124f4638ab2a2e6de) Fix: no-extra-parens export default sequence expression false positive (#13096) (Milos Djermanovic) +* [`af4472f`](https://github.com/eslint/eslint/commit/af4472fed794b86e63730702e4b27294a4118d09) Update: Improve report location for new-cap (refs #12334) (#13136) (Milos Djermanovic) +* [`bfa811c`](https://github.com/eslint/eslint/commit/bfa811ceee801fe8ba212a5c879e13743146e909) Fix: init error in extending recommended config (fixes #12707) (#12738) (YeonJuan) +* [`5dfd4eb`](https://github.com/eslint/eslint/commit/5dfd4eb50d84077a57950f119e5de8976070e49a) Docs: examples with arrow functions in no-return-assign (fixes #13135) (#13138) (Anix) +* [`adc8fa8`](https://github.com/eslint/eslint/commit/adc8fa88c9f223b984a3519ed159a055bf933b18) Docs: clarify exceptions in spaced-comment (fixes #13032) (#13126) (Anix) +* [`a784dac`](https://github.com/eslint/eslint/commit/a784dac8bc26f31edbca869b16d6ad91bd4e5cc4) Update: Improve report location newline-per-chained-call (refs #12334) (#13116) (Milos Djermanovic) +* [`5e07574`](https://github.com/eslint/eslint/commit/5e07574a91861fdcab6888b4c6829868030c3a39) Update: Improve report location for space-before-function-paren (#13128) (Chiawen Chen) +* [`d0d32a8`](https://github.com/eslint/eslint/commit/d0d32a8c2a6c60c7e6e2d32a054e5987efd4c263) Sponsors: Sync README with website (ESLint Jenkins) +* [`17e2fe4`](https://github.com/eslint/eslint/commit/17e2fe425168e675fe7607182615e50527eedf53) Update: Improve error message for fatal fixer errors (#13120) (Brad Zacher) +* [`7551f0c`](https://github.com/eslint/eslint/commit/7551f0c6fd12f0295cc7d6377bf1899c092e79d8) Fix: no check for shadowed Object (fixes #12809) (#13115) (Anix) +* [`988d842`](https://github.com/eslint/eslint/commit/988d8428811934943ce13b2d8b833d795ac2bb4a) Fix: add end location to report in no-useless-concat (refs #12334) (#13110) (Milos Djermanovic) +* [`0518ebb`](https://github.com/eslint/eslint/commit/0518ebb840b5bf2d646dbd8a9b3e246e7069eb79) Sponsors: Sync README with website (ESLint Jenkins) +* [`252fd41`](https://github.com/eslint/eslint/commit/252fd4133805f003151e0220b5d89bbb70cfacb1) Sponsors: Sync README with website (ESLint Jenkins) +* [`438dcbb`](https://github.com/eslint/eslint/commit/438dcbb981214d67e8848006ce45afc0fbfa1705) Update: Improve report location for comma-style (refs #12334) (#13111) (Chiawen Chen) +* [`85b7254`](https://github.com/eslint/eslint/commit/85b72548c0c893499c787b82f404348e1b50354d) Update: no-inner-declarations false negative in non-block (fixes #12222) (#13062) (Anix) +* [`6631ef1`](https://github.com/eslint/eslint/commit/6631ef1678bbdd48680fb3025c3692abd0aeec4a) Update: Improve report location for no-empty-function (refs #12334) (#13121) (Milos Djermanovic) +* [`b228f95`](https://github.com/eslint/eslint/commit/b228f958afaf507d6f6f99c90b2075b395733839) Docs: Update README team and sponsors (ESLint Jenkins) + v7.0.0-alpha.3 - March 27, 2020 * [`78c8cda`](https://github.com/eslint/eslint/commit/78c8cda5a5d82f5f8548c4528a6438d29756bb71) Breaking: RuleTester Improvements (refs eslint/rfcs#25) (#12955) (Milos Djermanovic) diff --git a/eslint/Makefile.js b/eslint/Makefile.js index a574f25..f5f3fd5 100644 --- a/eslint/Makefile.js +++ b/eslint/Makefile.js @@ -210,6 +210,9 @@ function generateRuleIndexPage() { } }); + // `.rules` will be `undefined` if all rules in category are deprecated. + categoriesData.categories = categoriesData.categories.filter(category => !!category.rules); + const output = yaml.safeDump(categoriesData, { sortKeys: true }); output.to(outputFile); @@ -613,7 +616,6 @@ target.gensite = function(prereleaseVersion) { htmlFullPath = fullPath.replace(".md", ".html"); if (test("-f", fullPath)) { - rm("-rf", fullPath); if (filePath.indexOf(".md") >= 0 && test("-f", htmlFullPath)) { @@ -743,7 +745,7 @@ target.gensite = function(prereleaseVersion) { echo(`> Updating files (Steps 4-9)${" ".repeat(50)}`); // 10. Copy temporary directory to site's docs folder - echo("> Copying the temporary directory the site (Step 10)"); + echo("> Copying the temporary directory into the site's docs folder (Step 10)"); let outputDir = DOCS_DIR; if (prereleaseVersion) { @@ -754,9 +756,13 @@ target.gensite = function(prereleaseVersion) { } cp("-rf", `${TEMP_DIR}*`, outputDir); - // 11. Generate rule listing page - echo("> Generating the rule listing (Step 11)"); - generateRuleIndexPage(); + // 11. Generate rules index page + if (prereleaseVersion) { + echo("> Skipping generating rules index page because this is a prerelease (Step 11)"); + } else { + echo("> Generating the rules index page (Step 11)"); + generateRuleIndexPage(); + } // 12. Delete temporary directory echo("> Removing the temporary directory (Step 12)"); diff --git a/eslint/README.md b/eslint/README.md index da75b5c..427544b 100644 --- a/eslint/README.md +++ b/eslint/README.md @@ -214,6 +214,11 @@ The people who review and implement new features.
薛定谔的猫 + + +
+Milos Djermanovic +
@@ -233,11 +238,6 @@ Pig Fang
YeonJuan - - -
-Milos Djermanovic -
@@ -250,9 +250,9 @@ The following companies, organizations, and individuals support ESLint's ongoing

Gold Sponsors

-

Shopify Salesforce MagicLab Airbnb

Silver Sponsors

+

Shopify Salesforce Airbnb

Silver Sponsors

AMP Project

Bronze Sponsors

-

Kasinot.fi Pelisivut Nettikasinot.org BonusFinder Deutschland Top Web Design Agencies Bugsnag Stability Monitoring Mixpanel VPS Server Free Icons by Icons8 UI UX Design Agencies clay Discord ThemeIsle TekHattan Marfeel Fire Stick Tricks

+

CasinoTop.com Casino Topp Writers Per Hour Anagram Solver vpn netflix Kasinot.fi Pelisivut Nettikasinot.org BonusFinder Deutschland Top Web Design Agencies Bugsnag Stability Monitoring Mixpanel VPS Server Free Icons by Icons8 UI UX Design Agencies clay Discord ThemeIsle TekHattan Marfeel Fire Stick Tricks

## Technology Sponsors diff --git a/eslint/bin/eslint.js b/eslint/bin/eslint.js index a9f51f1..75b4131 100755 --- a/eslint/bin/eslint.js +++ b/eslint/bin/eslint.js @@ -12,97 +12,135 @@ // to use V8's code cache to speed up instantiation time require("v8-compile-cache"); -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ - -const useStdIn = process.argv.includes("--stdin"), - init = process.argv.includes("--init"), - debug = process.argv.includes("--debug"); - // must do this initialization *before* other requires in order to work -if (debug) { +if (process.argv.includes("--debug")) { require("debug").enable("eslint:*,-eslint:code-path"); } //------------------------------------------------------------------------------ -// Requirements +// Helpers //------------------------------------------------------------------------------ -// now we can safely include the other modules that use debug -const path = require("path"), - fs = require("fs"), - cli = require("../lib/cli"); - -//------------------------------------------------------------------------------ -// Execution -//------------------------------------------------------------------------------ +/** + * Read data from stdin til the end. + * + * Note: See + * - https://github.com/nodejs/node/blob/master/doc/api/process.md#processstdin + * - https://github.com/nodejs/node/blob/master/doc/api/process.md#a-note-on-process-io + * - https://lists.gnu.org/archive/html/bug-gnu-emacs/2016-01/msg00419.html + * - https://github.com/nodejs/node/issues/7439 (historical) + * + * On Windows using `fs.readFileSync(STDIN_FILE_DESCRIPTOR, "utf8")` seems + * to read 4096 bytes before blocking and never drains to read further data. + * + * The investigation on the Emacs thread indicates: + * + * > Emacs on MS-Windows uses pipes to communicate with subprocesses; a + * > pipe on Windows has a 4K buffer. So as soon as Emacs writes more than + * > 4096 bytes to the pipe, the pipe becomes full, and Emacs then waits for + * > the subprocess to read its end of the pipe, at which time Emacs will + * > write the rest of the stuff. + * @returns {Promise} The read text. + */ +function readStdin() { + return new Promise((resolve, reject) => { + let content = ""; + let chunk = ""; + + process.stdin + .setEncoding("utf8") + .on("readable", () => { + while ((chunk = process.stdin.read()) !== null) { + content += chunk; + } + }) + .on("end", () => resolve(content)) + .on("error", reject); + }); +} -process.once("uncaughtException", err => { +/** + * Get the error message of a given value. + * @param {any} error The value to get. + * @returns {string} The error message. + */ +function getErrorMessage(error) { - // lazy load + // Lazy loading because those are used only if error happened. + const fs = require("fs"); + const path = require("path"); + const util = require("util"); const lodash = require("lodash"); - if (typeof err.messageTemplate === "string" && err.messageTemplate.length > 0) { - const template = lodash.template(fs.readFileSync(path.resolve(__dirname, `../messages/${err.messageTemplate}.txt`), "utf-8")); - const pkg = require("../package.json"); + // Foolproof -- thirdparty module might throw non-object. + if (typeof error !== "object" || error === null) { + return String(error); + } + + // Use templates if `error.messageTemplate` is present. + if (typeof error.messageTemplate === "string") { + try { + const templateFilePath = path.resolve( + __dirname, + `../messages/${error.messageTemplate}.txt` + ); + + // Use sync API because Node.js should exit at this tick. + const templateText = fs.readFileSync(templateFilePath, "utf-8"); + const template = lodash.template(templateText); + + return template(error.messageData || {}); + } catch { + + // Ignore template error then fallback to use `error.stack`. + } + } - console.error("\nOops! Something went wrong! :("); - console.error(`\nESLint: ${pkg.version}.\n\n${template(err.messageData || {})}`); - } else { - console.error(err.stack); + // Use the stacktrace if it's an error object. + if (typeof error.stack === "string") { + return error.stack; } + // Otherwise, dump the object. + return util.format("%o", error); +} + +/** + * Catch and report unexpected error. + * @param {any} error The thrown error object. + * @returns {void} + */ +function onFatalError(error) { process.exitCode = 2; -}); - -if (useStdIn) { - - /* - * Note: See - * - https://github.com/nodejs/node/blob/master/doc/api/process.md#processstdin - * - https://github.com/nodejs/node/blob/master/doc/api/process.md#a-note-on-process-io - * - https://lists.gnu.org/archive/html/bug-gnu-emacs/2016-01/msg00419.html - * - https://github.com/nodejs/node/issues/7439 (historical) - * - * On Windows using `fs.readFileSync(STDIN_FILE_DESCRIPTOR, "utf8")` seems - * to read 4096 bytes before blocking and never drains to read further data. - * - * The investigation on the Emacs thread indicates: - * - * > Emacs on MS-Windows uses pipes to communicate with subprocesses; a - * > pipe on Windows has a 4K buffer. So as soon as Emacs writes more than - * > 4096 bytes to the pipe, the pipe becomes full, and Emacs then waits for - * > the subprocess to read its end of the pipe, at which time Emacs will - * > write the rest of the stuff. - * - * Using the nodejs code example for reading from stdin. - */ - let contents = "", - chunk = ""; - - process.stdin.setEncoding("utf8"); - process.stdin.on("readable", () => { - - // Use a loop to make sure we read all available data. - while ((chunk = process.stdin.read()) !== null) { - contents += chunk; - } - }); - process.stdin.on("end", () => { - process.exitCode = cli.execute(process.argv, contents, "utf8"); - }); -} else if (init) { - const configInit = require("../lib/init/config-initializer"); - - configInit.initializeConfig().then(() => { - process.exitCode = 0; - }).catch(err => { - process.exitCode = 1; - console.error(err.message); - console.error(err.stack); - }); -} else { - process.exitCode = cli.execute(process.argv); + const { version } = require("../package.json"); + const message = getErrorMessage(error); + + console.error(` +Oops! Something went wrong! :( + +ESLint: ${version} + +${message}`); } + +//------------------------------------------------------------------------------ +// Execution +//------------------------------------------------------------------------------ + +(async function main() { + process.on("uncaughtException", onFatalError); + process.on("unhandledRejection", onFatalError); + + // Call the config initializer if `--init` is present. + if (process.argv.includes("--init")) { + await require("../lib/init/config-initializer").initializeConfig(); + return; + } + + // Otherwise, call the CLI. + process.exitCode = await require("../lib/cli").execute( + process.argv, + process.argv.includes("--stdin") ? await readStdin() : null + ); +}()).catch(onFatalError); diff --git a/eslint/conf/category-list.json b/eslint/conf/category-list.json index 6609734..cd3b816 100644 --- a/eslint/conf/category-list.json +++ b/eslint/conf/category-list.json @@ -4,7 +4,6 @@ { "name": "Best Practices", "description": "These rules relate to better ways of doing things to help you avoid problems:" }, { "name": "Strict Mode", "description": "These rules relate to strict mode directives:" }, { "name": "Variables", "description": "These rules relate to variable declarations:" }, - { "name": "Node.js and CommonJS", "description": "These rules relate to code running in Node.js, or in browsers with CommonJS:" }, { "name": "Stylistic Issues", "description": "These rules relate to style guidelines, and are therefore quite subjective:" }, { "name": "ECMAScript 6", "description": "These rules relate to ES6, also known as ES2015:" } ], diff --git a/eslint/docs/developer-guide/nodejs-api.md b/eslint/docs/developer-guide/nodejs-api.md index ee1a7a4..e980445 100644 --- a/eslint/docs/developer-guide/nodejs-api.md +++ b/eslint/docs/developer-guide/nodejs-api.md @@ -6,6 +6,20 @@ While ESLint is designed to be run on the command line, it's possible to use ESL ## Table of Contents +* [ESLint] + * [constructor()][eslint-constructor] + * [lintFiles()][eslint-lintFiles] + * [lintText()][eslint-lintText] + * [calculateConfigForFile()][eslint-calculateConfigForFile] + * [isPathIgnored()][eslint-isPathIgnored] + * [loadFormatter()][eslint-loadFormatter] + * [static version][eslint-version] + * [static outputFixes()][eslint-outputFixes] + * [static getErrorResults()][eslint-getErrorResults] + * [LintResult type](lintresult) + * [LintMessage type](lintmessage) + * [EditInfo type](editinfo) + * [Formatter type](formatter) * [SourceCode](#sourcecode) * [splitLines()](#sourcecode-splitlines) * [Linter](#linter) @@ -17,22 +31,361 @@ While ESLint is designed to be run on the command line, it's possible to use ESL * [defineParser()](#linter-defineparser) * [version](#linter-version) * [linter (deprecated)](#linter-1) -* [CLIEngine](#cliengine) - * [executeOnFiles()](#cliengine-executeonfiles) - * [resolveFileGlobPatterns()](#cliengine-resolvefileglobpatterns) - * [getConfigForFile()](#cliengine-getconfigforfile) - * [executeOnText()](#cliengine-executeontext) - * [addPlugin()](#cliengine-addplugin) - * [isPathIgnored()](#cliengine-ispathignored) - * [getFormatter()](#cliengine-getformatter) - * [getErrorResults()](#cliengine-geterrorresults) - * [outputFixes()](#cliengine-outputfixes) - * [getRules()](#cliengine-getrules) - * [version](#cliengine-version) +* [CLIEngine (deprecated)](#cliengine) * [RuleTester](#ruletester) * [Customizing RuleTester](#customizing-ruletester) * [Deprecated APIs](#deprecated-apis) +--- + +## ESLint class + +The `ESLint` class is the primary class to use in Node.js applications. + +This class depends on the Node.js `fs` module and the file system, so you cannot use it in browsers. If you want to lint code on browsers, use the [Linter](#linter) class instead. + +Here's a simple example of using the `ESLint` class: + +```js +const { ESLint } = require("eslint"); + +(async function main() { + // 1. Create an instance. + const eslint = new ESLint(); + + // 2. Lint files. + const results = await eslint.lintFiles(["lib/**/*.js"]); + + // 3. Format the results. + const formatter = await eslint.loadFormatter("stylish"); + const resultText = formatter.format(results); + + // 4. Output it. + console.log(resultText); +})().catch((error) => { + process.exitCode = 1; + console.error(error); +}); +``` + +And here is an example that autofixes lint problems: + +```js +const { ESLint } = require("eslint"); + +(async function main() { + // 1. Create an instance with the `fix` option. + const eslint = new ESLint({ fix: true }); + + // 2. Lint files. This doesn't modify target files. + const results = await eslint.lintFiles(["lib/**/*.js"]); + + // 3. Modify the files with the fixed code. + await ESLint.outputFixes(results); + + // 4. Format the results. + const formatter = await eslint.loadFormatter("stylish"); + const resultText = formatter.format(results); + + // 5. Output it. + console.log(resultText); +})().catch((error) => { + process.exitCode = 1; + console.error(error); +}); +``` + +### ◆ new ESLint(options) + +```js +const eslint = new ESLint(options); +``` + +Create a new `ESLint` instance. + +#### Parameters + +The `ESLint` constructor takes an `options` object. If you omit the `options` object then it uses default values for all options. The `options` object has the following properties. + +##### File Enumeration + +* `options.cwd` (`string`)
+ Default is `process.cwd()`. The working directory. This must be an absolute path. +* `options.errorOnUnmatchedPattern` (`boolean`)
+ Default is `true`. Unless set to `false`, the [`eslint.lintFiles()`][eslint-lintfiles] method will throw an error when no target files are found. +* `options.extensions` (`string[] | null`)
+ Default is `null`. If you pass directory paths to the [`eslint.lintFiles()`][eslint-lintfiles] method, ESLint checks the files in those directories that have the given extensions. For example, when passing the `src/` directory and `extensions` is `[".js", ".ts"]`, ESLint will lint `*.js` and `*.ts` files in `src/`. If `extensions` is `null`, ESLint checks `*.js` files and files that match `overrides[].files` patterns in your configuration.
**Note:** This option only applies when you pass directory paths to the [`eslint.lintFiles()`][eslint-lintfiles] method. If you pass glob patterns like `lib/**/*`, ESLint will lint all files matching the glob pattern regardless of extension. +* `options.globInputPaths` (`boolean`)
+ Default is `true`. If `false` is present, the [`eslint.lintFiles()`][eslint-lintfiles] method doesn't interpret glob patterns. +* `options.ignore` (`boolean`)
+ Default is `true`. If `false` is present, the [`eslint.lintFiles()`][eslint-lintfiles] method doesn't respect `.eslintignore` files or `ignorePatterns` in your configuration. +* `options.ignorePath` (`string | null`)
+ Default is `null`. The path to a file ESLint uses instead of `$CWD/.eslintignore`. If a path is present and the file doesn't exist, this constructor will throw an error. + +##### Linting + +* `options.allowInlineConfig` (`boolean`)
+ Default is `true`. If `false` is present, ESLint suppresses directive comments in source code. If this option is `false`, it overrides the `noInlineConfig` setting in your configurations. +* `options.baseConfig` (`ConfigData | null`)
+ Default is `null`. [Configuration object], extended by all configurations used with this instance. You can use this option to define the default settings that will be used if your configuration files don't configure it. +* `options.overrideConfig` (`ConfigData | null`)
+ Default is `null`. [Configuration object], overrides all configurations used with this instance. You can use this option to define the settings that will be used even if your configuration files configure it. +* `options.overrideConfigFile` (`string | null`)
+ Default is `null`. The path to a configuration file, overrides all configurations used with this instance. The `options.overrideConfig` option is applied after this option is applied. +* `options.plugins` (`Record | null`)
+ Default is `null`. The plugin implementations that ESLint uses for the `plugins` setting of your configuration. This is a map-like object. Those keys are plugin IDs and each value is implementation. +* `options.reportUnusedDisableDirectives` (`"error" | "warn" | "off" | null`)
+ Default is `null`. The severity to report unused eslint-disable directives. If this option is a severity, it overrides the `reportUnusedDisableDirectives` setting in your configurations. +* `options.resolvePluginsRelativeTo` (`string` | `null`)
+ Default is `null`. The path to a directory where plugins should be resolved from. If `null` is present, ESLint loads plugins from the location of the configuration file that contains the plugin setting. If a path is present, ESLint loads all plugins from there. +* `options.rulePaths` (`string[]`)
+ Default is `[]`. An array of paths to directories to load custom rules from. +* `options.useEslintrc` (`boolean`)
+ Default is `true`. If `false` is present, ESLint doesn't load configuration files (`.eslintrc.*` files). Only the configuration of the constructor options is valid. + +##### Autofix + +* `options.fix` (`boolean | (message: LintMessage) => boolean`)
+ Default is `false`. If `true` is present, the [`eslint.lintFiles()`][eslint-lintfiles] and [`eslint.lintText()`][eslint-linttext] methods work in autofix mode. If a predicate function is present, the methods pass each lint message to the function, then use only the lint messages for which the function returned `true`. +* `options.fixTypes` (`("problem" | "suggestion" | "layout")[] | null`)
+ Default is `null`. The types of the rules that the [`eslint.lintFiles()`][eslint-lintfiles] and [`eslint.lintText()`][eslint-linttext] methods use for autofix. + +##### Cache-related + +* `options.cache` (`boolean`)
+ Default is `false`. If `true` is present, the [`eslint.lintFiles()`][eslint-lintfiles] method caches lint results and uses it if each target file is not changed. Please mind that ESLint doesn't clear the cache when you upgrade ESLint plugins. In that case, you have to remove the cache file manually. The [`eslint.lintText()`][eslint-linttext] method doesn't use caches even if you pass the `options.filePath` to the method. +* `options.cacheLocation` (`string`)
+ Default is `.eslintcache`. The [`eslint.lintFiles()`][eslint-lintfiles] method writes caches into this file. + +### ◆ eslint.lintFiles(patterns) + +```js +const results = await eslint.lintFiles(patterns); +``` + +This method lints the files that match the glob patterns and then returns the results. + +#### Parameters + +* `patterns` (`string | string[]`)
+ The lint target files. This can contain any of file paths, directory paths, and glob patterns. + +#### Return Value + +* (`Promise`)
+ The promise that will be fulfilled with an array of [LintResult] objects. + +### ◆ eslint.lintText(code, options) + +```js +const results = await eslint.lintText(code, options); +``` + +This method lints the given source code text and then returns the results. + +By default, this method uses the configuration that applies to files in the current working directory (the `cwd` constructor option). If you want to use a different configuration, pass `options.filePath`, and ESLint will load the same configuration that [`eslint.lintFiles()`][eslint-lintfiles] would use for a file at `options.filePath`. + +If the `options.filePath` value is configured to be ignored, this method returns an empty array. If the `options.warnIgnored` option is set along with the `options.filePath` option, this method returns a [LintResult] object. In that case, the result may contain a warning that indicates the file was ignored. + +#### Parameters + +The second parameter `options` is omittable. + +* `code` (`string`)
+ The source code text to check. +* `options.filePath` (`string`)
+ Optional. The path to the file of the source code text. If omitted, the `result.filePath` becomes the string `""`. +* `options.warnIgnored` (`boolean`)
+ Optional. If `true` is present and the `options.filePath` is a file ESLint should ignore, this method returns a lint result contains a warning message. + +#### Return Value + +* (`Promise`)
+ The promise that will be fulfilled with an array of [LintResult] objects. This is an array (despite there being only one lint result) in order to keep the interfaces between this and the [`eslint.lintFiles()`][eslint-lintfiles] method similar. + +### ◆ eslint.calculateConfigForFile(filePath) + +```js +const config = await eslint.calculateConfigForFile(filePath); +``` + +This method calculates the configuration for a given file, which can be useful for debugging purposes. + +* It resolves and merges `extends` and `overrides` settings into the top level configuration. +* It resolves the `parser` setting to absolute paths. +* It normalizes the `plugins` setting to align short names. (e.g., `eslint-plugin-foo` → `foo`) +* It adds the `processor` setting if a legacy file extension processor is matched. +* It doesn't interpret the `env` setting to the `globals` and `parserOptions` settings, so the result object contains the `env` setting as is. + +#### Parameters + +* `filePath` (`string`)
+ The path to the file whose configuration you would like to calculate. Directory paths are forbidden because ESLint cannot handle the `overrides` setting. + +#### Return Value + +* (`Promise`)
+ The promise that will be fulfilled with a configuration object. + +### ◆ eslint.isPathIgnored(filePath) + +```js +const isPathIgnored = await eslint.isPathIgnored(filePath); +``` + +This method checks if a given file is ignored by your configuration. + +#### Parameters + +* `filePath` (`string`)
+ The path to the file you want to check. + +#### Return Value + +* (`Promise`)
+ The promise that will be fulfilled with whether the file is ignored or not. If the file is ignored, then it will return `true`. + +### ◆ eslint.loadFormatter(nameOrPath) + +```js +const formatter = await eslint.loadFormatter(nameOrPath); +``` + +This method loads a formatter. Formatters convert lint results to a human- or machine-readable string. + +#### Parameters + +* `nameOrPath` (`string | undefined`)
+ The path to the file you want to check. The following values are allowed: + * `undefined`. In this case, loads the `"stylish"` built-in formatter. + * A name of [built-in formatters][builtin-formatters]. + * A name of [third-party formatters][thirdparty-formatters]. For examples: + * `"foo"` will load `eslint-formatter-foo`. + * `"@foo"` will load `@foo/eslint-formatter`. + * `"@foo/bar"` will load `@foo/eslint-formatter-bar`. + * A path to the file that defines a formatter. The path must contain one or more path separators (`/`) in order to distinguish if it's a path or not. For example, start with `./`. + +#### Return Value + +* (`Promise`)
+ The promise that will be fulfilled with a [Formatter] object. + +### ◆ ESLint.version + +```js +const version = ESLint.version; +``` + +The version string of ESLint. E.g. `"7.0.0"`. + +This is a static property. + +### ◆ ESLint.outputFixes(results) + +```js +await ESLint.outputFixes(results); +``` + +This method writes code modified by ESLint's autofix feature into its respective file. If any of the modified files don't exist, this method does nothing. + +This is a static method. + +#### Parameters + +* `results` (`LintResult[]`)
+ The [LintResult] objects to write. + +#### Return Value + +* (`Promise`)
+ The promise that will be fulfilled after all files are written. + +### ◆ ESLint.getErrorResults(results) + +```js +const filteredResults = ESLint.getErrorResults(results); +``` + +This method copies the given results and removes warnings. The returned value contains only errors. + +This is a static method. + +#### Parameters + +* `results` (`LintResult[]`)
+ The [LintResult] objects to filter. + +#### Return Value + +* (`LintResult[]`)
+ The filtered [LintResult] objects. + +### ◆ LintResult type + +The `LintResult` value is the information of the linting result of each file. The [`eslint.lintFiles()`][eslint-lintfiles] and [`eslint.lintText()`][eslint-linttext] methods return it. It has the following properties: + +* `filePath` (`string`)
+ The absolute path to the file of this result. This is the string `""` if the file path is unknown (when you didn't pass the `options.filePath` option to the [`eslint.lintText()`][eslint-linttext] method). +* `messages` (`LintMessage[]`)
+ The array of [LintMessage] objects. +* `fixableErrorCount` (`number`)
+ The number of errors that can be fixed automatically by the `fix` constructor option. +* `fixableWarningCount` (`number`)
+ The number of warnings that can be fixed automatically by the `fix` constructor option. +* `errorCount` (`number`)
+ The number of errors. This includes fixable errors. +* `warningCount` (`number`)
+ The number of warnings. This includes fixable warnings. +* `output` (`string | undefined`)
+ The modified source code text. This property is undefined if any fixable messages didn't exist. +* `source` (`string | undefined`)
+ The original source code text. This property is undefined if any messages didn't exist or the `output` property exists. +* `usedDeprecatedRules` (`{ ruleId: string; replacedBy: string[] }[]`)
+ The information about the deprecated rules that were used to check this file. + +### ◆ LintMessage type + +The `LintMessage` value is the information of each linting error. The `messages` property of the [LintResult] type contains it. It has the following properties: + +* `ruleId` (`string` | `null`)
+ The rule name that generates this lint message. If this message is generated by the ESLint core rather than rules, this is `null`. +* `severity` (`1 | 2`)
+ The severity of this message. `1` means warning and `2` means error. +* `message` (`string`)
+ The error message. +* `line` (`number`)
+ The 1-based line number of the begin point of this message. +* `column` (`number`)
+ The 1-based column number of the begin point of this message. +* `endLine` (`number | undefined`)
+ The 1-based line number of the end point of this message. This property is undefined if this message is not a range. +* `endColumn` (`number | undefined`)
+ The 1-based column number of the end point of this message. This property is undefined if this message is not a range. +* `fix` (`EditInfo | undefined`)
+ The [EditInfo] object of autofix. This property is undefined if this message is not fixable. +* `suggestions` (`{ desc: string; fix: EditInfo }[] | undefined`)
+ The list of suggestions. Each suggestion is the pair of a description and an [EditInfo] object to fix code. API users such as editor integrations can choose one of them to fix the problem of this message. This property is undefined if this message doesn't have any suggestions. + +### ◆ EditInfo type + +The `EditInfo` value is information to edit text. The `fix` and `suggestions` properties of [LintMessage] type contain it. It has following properties: + +* `range` (`[number, number]`)
+ The pair of 0-based indices in source code text to remove. +* `text` (`string`)
+ The text to add. + +This edit information means replacing the range of the `range` property by the `text` property value. It's like `sourceCodeText.slice(0, edit.range[0]) + edit.text + sourceCodeText.slice(edit.range[1])`. Therefore, it's an add if the `range[0]` and `range[1]` property values are the same value, and it's removal if the `text` property value is empty string. + +### ◆ Formatter type + +The `Formatter` value is the object to convert the [LintResult] objects to text. The [eslint.loadFormatter()][eslint-loadformatter] method returns it. It has the following method: + +* `format` (`(results: LintResult[]) => string`)
+ The method to convert the [LintResult] objects to text. + +--- + ## SourceCode The `SourceCode` type represents the parsed source code that ESLint executes on. It's used internally in ESLint and is also available so that already-parsed code can be used. You can create a new instance of `SourceCode` by passing in the text string representing the code and an abstract syntax tree (AST) in [ESTree](https://github.com/estree/estree) format (including location information, range information, comments, and tokens): @@ -78,6 +431,8 @@ const codeLines = SourceCode.splitLines(code); */ ``` +--- + ## Linter The `Linter` object does the actual evaluation of the JavaScript code. It doesn't do any filesystem operations, it simply parses and reports on the code. In particular, the `Linter` object does not process configuration objects or files. @@ -336,8 +691,12 @@ const messages = linter.verify("var foo;", { Note: This API is deprecated as of 4.0.0. +--- + ## CLIEngine +⚠️ The `CLIEngine` class has been deprecated in favor of the `ESLint` class as of v7.0.0. + The primary Node.js API is `CLIEngine`, which is the underlying utility that runs the ESLint command line interface. This object will read the filesystem for configuration and file information but will not output any results. Instead, it allows you direct access to the important information so you can deal with the output yourself. You can get a reference to the `CLIEngine` by doing the following: @@ -819,6 +1178,8 @@ Map { require("eslint").CLIEngine.version; // '4.5.0' ``` +--- + ## RuleTester `eslint.RuleTester` is a utility to write tests for ESLint rules. It is used internally for the bundled rules that come with ESLint, and it can also be used by plugins. @@ -1007,7 +1368,31 @@ ruleTester.run("my-rule", myRule, { }) ``` +--- + ## Deprecated APIs * `cli` - the `cli` object has been deprecated in favor of `CLIEngine`. As of v1.0.0, `cli` is no longer exported and should not be used by external tools. * `linter` - the `linter` object has been deprecated in favor of `Linter` as of v4.0.0. +* `CLIEngine` - the `CLIEngine` class has been deprecated in favor of the `ESLint` class as of v7.0.0. + +--- + +[configuration object]: ../user-guide/configuring.md +[builtin-formatters]: https://eslint.org/docs/user-guide/formatters/ +[thirdparty-formatters]: https://www.npmjs.com/search?q=eslintformatter +[eslint]: #eslint-class +[eslint-constructor]: #-new-eslintoptions +[eslint-lintfiles]: #-eslintlintFilespatterns +[eslint-linttext]: #-eslintlintTextcode-options +[eslint-calculateconfigforfile]: #-eslintcalculateConfigForFilefilePath +[eslint-ispathignored]: #-eslintisPathIgnoredfilePath +[eslint-loadformatter]: #-eslintloadFormatternameOrPath +[eslint-version]: #-eslintversion +[eslint-outputfixes]: #-eslintoutputFixesresults +[eslint-geterrorresults]: #-eslintgetErrorResultsresults +[lintresult]: #-lintresult-type +[lintmessage]: #-lintmessage-type +[editinfo]: #-editinfo-type +[formatter]: #-formatter-type +[linter]: #linter diff --git a/eslint/docs/rules/array-callback-return.md b/eslint/docs/rules/array-callback-return.md index c0a5ad8..84bf0e7 100644 --- a/eslint/docs/rules/array-callback-return.md +++ b/eslint/docs/rules/array-callback-return.md @@ -13,7 +13,7 @@ var indexMap = myArray.reduce(function(memo, item, index) { ## Rule Details This rule enforces usage of `return` statement in callbacks of array's methods. -Additionaly, it may also enforce the `forEach` array method callback to __not__ return a value by using the `checkForEach` option. +Additionally, it may also enforce the `forEach` array method callback to __not__ return a value by using the `checkForEach` option. This rule finds callback functions of the following methods, then checks usage of `return` statement. diff --git a/eslint/docs/rules/callback-return.md b/eslint/docs/rules/callback-return.md index ba27e23..f425ccd 100644 --- a/eslint/docs/rules/callback-return.md +++ b/eslint/docs/rules/callback-return.md @@ -1,5 +1,7 @@ # Enforce Return After Callback (callback-return) +This rule was **deprecated** in ESLint v7.0.0. Please use the corresponding rule in [`eslint-plugin-node`](https://github.com/mysticatea/eslint-plugin-node). + The callback pattern is at the heart of most I/O and event-driven programming in JavaScript. diff --git a/eslint/docs/rules/curly.md b/eslint/docs/rules/curly.md index 9f4df27..aecbca5 100644 --- a/eslint/docs/rules/curly.md +++ b/eslint/docs/rules/curly.md @@ -181,10 +181,6 @@ while (true) { for (var i = 0; foo; i++) { doSomething(); } - -if (foo) - // some comment - bar(); ``` Examples of **correct** code for the `"multi-or-nest"` option: @@ -214,6 +210,18 @@ while (true) for (var i = 0; foo; i++) doSomething(); +``` + +For single-line statements preceded by a comment, braces are allowed but not required. + +Examples of additional **correct** code for the `"multi-or-nest"` option: + +```js +/*eslint curly: ["error", "multi-or-nest"]*/ + +if (foo) + // some comment + bar(); if (foo) { // some comment diff --git a/eslint/docs/rules/global-require.md b/eslint/docs/rules/global-require.md index c31134d..a81d5a3 100644 --- a/eslint/docs/rules/global-require.md +++ b/eslint/docs/rules/global-require.md @@ -1,5 +1,7 @@ # Enforce require() on the top-level module scope (global-require) +This rule was **deprecated** in ESLint v7.0.0. Please use the corresponding rule in [`eslint-plugin-node`](https://github.com/mysticatea/eslint-plugin-node). + In Node.js, module dependencies are included using the `require()` function, such as: ```js diff --git a/eslint/docs/rules/handle-callback-err.md b/eslint/docs/rules/handle-callback-err.md index 5733e42..dad537e 100644 --- a/eslint/docs/rules/handle-callback-err.md +++ b/eslint/docs/rules/handle-callback-err.md @@ -1,5 +1,7 @@ # Enforce Callback Error Handling (handle-callback-err) +This rule was **deprecated** in ESLint v7.0.0. Please use the corresponding rule in [`eslint-plugin-node`](https://github.com/mysticatea/eslint-plugin-node). + In Node.js, a common pattern for dealing with asynchronous behavior is called the callback pattern. This pattern expects an `Error` object or `null` as the first argument of the callback. Forgetting to handle these errors can lead to some really strange behavior in your application. diff --git a/eslint/docs/rules/no-buffer-constructor.md b/eslint/docs/rules/no-buffer-constructor.md index f287ce5..9902a71 100644 --- a/eslint/docs/rules/no-buffer-constructor.md +++ b/eslint/docs/rules/no-buffer-constructor.md @@ -1,5 +1,7 @@ # disallow use of the Buffer() constructor (no-buffer-constructor) +This rule was **deprecated** in ESLint v7.0.0. Please use the corresponding rule in [`eslint-plugin-node`](https://github.com/mysticatea/eslint-plugin-node). + In Node.js, the behavior of the `Buffer` constructor is different depending on the type of its argument. Passing an argument from user input to `Buffer()` without validating its type can lead to security vulnerabilities such as remote memory disclosure and denial of service. As a result, the `Buffer` constructor has been deprecated and should not be used. Use the producer methods `Buffer.from`, `Buffer.alloc`, and `Buffer.allocUnsafe` instead. ## Rule Details diff --git a/eslint/docs/rules/no-inner-declarations.md b/eslint/docs/rules/no-inner-declarations.md index 23fd925..707fc6e 100644 --- a/eslint/docs/rules/no-inner-declarations.md +++ b/eslint/docs/rules/no-inner-declarations.md @@ -81,6 +81,8 @@ function doSomethingElse() { function doAnotherThing() { } } } + +if (foo) function f(){} ``` Examples of **correct** code for this rule with the default `"functions"` option: @@ -102,6 +104,8 @@ var fn; if (test) { fn = function fnExpression() { }; } + +if (foo) var a; ``` ### both @@ -120,12 +124,17 @@ function doAnotherThing() { var bar = 81; } } + + +if (foo) var a; + +if (foo) function f(){} ``` Examples of **correct** code for this rule with the `"both"` option: ```js -/*eslint no-inner-declarations: "error"*/ +/*eslint no-inner-declarations: ["error", "both"]*/ /*eslint-env es6*/ var bar = 42; diff --git a/eslint/docs/rules/no-mixed-requires.md b/eslint/docs/rules/no-mixed-requires.md index ee63c99..c697d04 100644 --- a/eslint/docs/rules/no-mixed-requires.md +++ b/eslint/docs/rules/no-mixed-requires.md @@ -1,5 +1,7 @@ # disallow `require` calls to be mixed with regular variable declarations (no-mixed-requires) +This rule was **deprecated** in ESLint v7.0.0. Please use the corresponding rule in [`eslint-plugin-node`](https://github.com/mysticatea/eslint-plugin-node). + In the Node.js community it is often customary to separate initializations with calls to `require` modules from other variable declarations, sometimes also grouping them by the type of module. This rule helps you enforce this convention. ## Rule Details diff --git a/eslint/docs/rules/no-new-object.md b/eslint/docs/rules/no-new-object.md index 5186682..aef055c 100644 --- a/eslint/docs/rules/no-new-object.md +++ b/eslint/docs/rules/no-new-object.md @@ -27,7 +27,7 @@ Examples of **incorrect** code for this rule: var myObject = new Object(); -var myObject = new Object; +new Object(); ``` Examples of **correct** code for this rule: @@ -38,6 +38,9 @@ Examples of **correct** code for this rule: var myObject = new CustomObject(); var myObject = {}; + +var Object = function Object() {}; +new Object(); ``` ## When Not To Use It diff --git a/eslint/docs/rules/no-new-require.md b/eslint/docs/rules/no-new-require.md index 5ffaf2a..6cf1242 100644 --- a/eslint/docs/rules/no-new-require.md +++ b/eslint/docs/rules/no-new-require.md @@ -1,5 +1,7 @@ # Disallow new require (no-new-require) +This rule was **deprecated** in ESLint v7.0.0. Please use the corresponding rule in [`eslint-plugin-node`](https://github.com/mysticatea/eslint-plugin-node). + The `require` function is used to include modules that exist in separate files, such as: ```js diff --git a/eslint/docs/rules/no-path-concat.md b/eslint/docs/rules/no-path-concat.md index 998ec10..fddf27b 100644 --- a/eslint/docs/rules/no-path-concat.md +++ b/eslint/docs/rules/no-path-concat.md @@ -1,5 +1,7 @@ # Disallow string concatenation when using `__dirname` and `__filename` (no-path-concat) +This rule was **deprecated** in ESLint v7.0.0. Please use the corresponding rule in [`eslint-plugin-node`](https://github.com/mysticatea/eslint-plugin-node). + In Node.js, the `__dirname` and `__filename` global variables contain the directory path and the file path of the currently executing script file, respectively. Sometimes, developers try to use these variables to create paths to other files, such as: ```js diff --git a/eslint/docs/rules/no-process-env.md b/eslint/docs/rules/no-process-env.md index 9db800f..83bfa6d 100644 --- a/eslint/docs/rules/no-process-env.md +++ b/eslint/docs/rules/no-process-env.md @@ -1,5 +1,7 @@ # Disallow process.env (no-process-env) +This rule was **deprecated** in ESLint v7.0.0. Please use the corresponding rule in [`eslint-plugin-node`](https://github.com/mysticatea/eslint-plugin-node). + The `process.env` object in Node.js is used to store deployment/configuration parameters. Littering it through out a project could lead to maintenance issues as it's another kind of global dependency. As such, it could lead to merge conflicts in a multi-user setup and deployment issues in a multi-server setup. Instead, one of the best practices is to define all those parameters in a single configuration/settings file which could be accessed throughout the project. diff --git a/eslint/docs/rules/no-process-exit.md b/eslint/docs/rules/no-process-exit.md index 0070a09..54f2661 100644 --- a/eslint/docs/rules/no-process-exit.md +++ b/eslint/docs/rules/no-process-exit.md @@ -1,5 +1,7 @@ # Disallow process.exit() (no-process-exit) +This rule was **deprecated** in ESLint v7.0.0. Please use the corresponding rule in [`eslint-plugin-node`](https://github.com/mysticatea/eslint-plugin-node). + The `process.exit()` method in Node.js is used to immediately stop the Node.js process and exit. This is a dangerous operation because it can occur in any method at any point in time, potentially stopping a Node.js application completely when an error occurs. For example: ```js diff --git a/eslint/docs/rules/no-restricted-modules.md b/eslint/docs/rules/no-restricted-modules.md index 7f9c926..3fc412f 100644 --- a/eslint/docs/rules/no-restricted-modules.md +++ b/eslint/docs/rules/no-restricted-modules.md @@ -1,5 +1,7 @@ # Disallow Node.js modules (no-restricted-modules) +This rule was **deprecated** in ESLint v7.0.0. Please use the corresponding rule in [`eslint-plugin-node`](https://github.com/mysticatea/eslint-plugin-node). + A module in Node.js is a simple or complex functionality organized in a JavaScript file which can be reused throughout the Node.js application. The keyword `require` is used in Node.js/CommonJS to import modules into an application. This way you can have dynamic loading where the loaded module name isn't predefined /static, or where you conditionally load a module only if it's "truly required". diff --git a/eslint/docs/rules/no-return-assign.md b/eslint/docs/rules/no-return-assign.md index 781d597..ed40c27 100644 --- a/eslint/docs/rules/no-return-assign.md +++ b/eslint/docs/rules/no-return-assign.md @@ -40,6 +40,14 @@ function doSomething() { function doSomething() { return foo += 2; } + +const foo = (a, b) => a = b + +const bar = (a, b, c) => (a = b, c == b) + +function doSomething() { + return foo = bar && foo > 0; +} ``` Examples of **correct** code for the default `"except-parens"` option: @@ -58,6 +66,14 @@ function doSomething() { function doSomething() { return (foo = bar + 2); } + +const foo = (a, b) => (a = b) + +const bar = (a, b, c) => ((a = b), c == b) + +function doSomething() { + return (foo = bar) && foo > 0; +} ``` ### always diff --git a/eslint/docs/rules/no-sync.md b/eslint/docs/rules/no-sync.md index 50ab515..949df6d 100644 --- a/eslint/docs/rules/no-sync.md +++ b/eslint/docs/rules/no-sync.md @@ -1,5 +1,7 @@ # Disallow Synchronous Methods (no-sync) +This rule was **deprecated** in ESLint v7.0.0. Please use the corresponding rule in [`eslint-plugin-node`](https://github.com/mysticatea/eslint-plugin-node). + In Node.js, most I/O is done through asynchronous methods. However, there are often synchronous versions of the asynchronous methods. For example, `fs.exists()` and `fs.existsSync()`. In some contexts, using synchronous operations is okay (if, as with ESLint, you are writing a command line utility). However, in other contexts the use of synchronous operations is considered a bad practice that should be avoided. For example, if you are running a high-travel web server on Node.js, you should consider carefully if you want to allow any synchronous operations that could lock up the server. ## Rule Details diff --git a/eslint/docs/rules/object-curly-newline.md b/eslint/docs/rules/object-curly-newline.md index 4e1cce7..eabc03e 100644 --- a/eslint/docs/rules/object-curly-newline.md +++ b/eslint/docs/rules/object-curly-newline.md @@ -498,6 +498,9 @@ import { foo, bar } from 'foo-bar'; +import { + foo, bar +} from 'foo-bar'; import { foo as f, bar diff --git a/eslint/docs/rules/prefer-rest-params.md b/eslint/docs/rules/prefer-rest-params.md index 6930b4f..c67e744 100644 --- a/eslint/docs/rules/prefer-rest-params.md +++ b/eslint/docs/rules/prefer-rest-params.md @@ -14,6 +14,8 @@ This rule is aimed to flag usage of `arguments` variables. Examples of **incorrect** code for this rule: ```js +/*eslint prefer-rest-params: "error"*/ + function foo() { console.log(arguments); } @@ -32,6 +34,8 @@ function foo(action) { Examples of **correct** code for this rule: ```js +/*eslint prefer-rest-params: "error"*/ + function foo(...args) { console.log(args); } diff --git a/eslint/docs/rules/spaced-comment.md b/eslint/docs/rules/spaced-comment.md index 3d0a5d9..5a885f9 100644 --- a/eslint/docs/rules/spaced-comment.md +++ b/eslint/docs/rules/spaced-comment.md @@ -21,7 +21,7 @@ The rule takes two options. * This rule can also take a 2nd option, an object with any of the following keys: `"exceptions"` and `"markers"`. - * The `"exceptions"` value is an array of string patterns which are considered exceptions to the rule. + * The `"exceptions"` value is an array of string patterns which are considered exceptions to the rule. The rule will not warn when the pattern starts from the beginning of the comment and repeats until the end of the line or `*/` if the comment is a single line comment. Please note that exceptions are ignored if the first argument is `"never"`. ``` @@ -175,6 +175,12 @@ Examples of **incorrect** code for this rule with the `"always"` option combined /*-+-+-+-+-+-+-+*/ ``` +```js +/* eslint spaced-comment: ["error", "always", { "block": { "exceptions": ["*"] } }] */ + +/******** COMMENT *******/ +``` + Examples of **correct** code for this rule with the `"always"` option combined with `"exceptions"`: ```js @@ -221,6 +227,16 @@ Examples of **correct** code for this rule with the `"always"` option combined w /*-+-+-+-+-+-+-+*/ ``` +```js +/* eslint spaced-comment: ["error", "always", { "block": { "exceptions": ["*"] } }] */ + +/***************/ + +/******** +COMMENT +*******/ +``` + ### markers Examples of **incorrect** code for this rule with the `"always"` option combined with `"markers"`: diff --git a/eslint/docs/user-guide/README.md b/eslint/docs/user-guide/README.md index 31160d1..7339bfe 100644 --- a/eslint/docs/user-guide/README.md +++ b/eslint/docs/user-guide/README.md @@ -36,3 +36,4 @@ If you were using a prior version of ESLint, you can get help with the transitio - [migrating-to-4.0.0](migrating-to-4.0.0.md) - [migrating-to-5.0.0](migrating-to-5.0.0.md) - [migrating-to-6.0.0](migrating-to-6.0.0.md) +- [migrating-to-7.0.0](migrating-to-7.0.0.md) diff --git a/eslint/docs/user-guide/migrating-to-7.0.0.md b/eslint/docs/user-guide/migrating-to-7.0.0.md new file mode 100644 index 0000000..cb39336 --- /dev/null +++ b/eslint/docs/user-guide/migrating-to-7.0.0.md @@ -0,0 +1,232 @@ +# Migrating to v7.0.0 + +ESLint v7.0.0 is a major release of ESLint. We have made a few breaking changes in this release. This guide is intended to walk you through the breaking changes. + +The lists below are ordered roughly by the number of users each change is expected to affect, where the first items are expected to affect the most users. + +## Table of Content + +### Breaking changes for users + +- [Node.js 8 is no longer supported](#drop-node-8) +- [Lint files matched by `overrides[].files` by default](#additional-lint-targets) +- [The base path of `overrides` and `ignorePatterns` is changed if the config file is given by the `--config`/`--ignore-path` options](#base-path-change) +- [The place where ESLint loads plugins from is changed](#plugin-loading-change) +- [Runtime deprecation warnings for `~/.eslintrc.*` config files](#runtime-deprecation-on-personal-config-files) +- [Default ignore patterns have changed](#default-ignore-patterns) +- [Description in directive comments](#description-in-directive-comments) +- [Node.js/CommonJS rules are deprecated](#deprecate-node-rules) +- [Several rules have been updated to cover more cases](#rules-strict) +- [`eslint:recommended` has been updated](#eslint-recommended) + +### Breaking changes for plugin developers + +- [Node.js 8 is no longer supported](#drop-node-8) +- [Lint files matched by `overrides[].files` by default](#additional-lint-targets) +- [Plugin resolution has been updated](#plugin-loading-change) +- [Additional validation added to the `RuleTester` class](#rule-tester-strict) + +### Breaking changes for integration developers + +- [Node.js 8 is no longer supported](#drop-node-8) +- [Plugin resolution has been updated](#plugin-loading-change) +- [The `CLIEngine` class has been deprecated](#deprecate-cliengine) + +--- + +## Node.js 8 is no longer supported + +Node.js 8 reached EOL in December 2019, and we are officially dropping support for it in this release. ESLint now supports the following versions of Node.js: + +- Node.js 10 (`10.12.0` and above) +- Node.js 12 and above + +**To address:** Make sure you upgrade to at least Node.js `10.12.0` when using ESLint v7.0.0. One important thing to double check is the Node.js version supported by your editor when using ESLint via editor integrations. If you are unable to upgrade, we recommend continuing to use ESLint 6 until you are able to upgrade Node.js. + +**Related issue(s):** [RFC44](https://github.com/eslint/rfcs/blob/master/designs/2019-drop-node8/README.md), [#12700](https://github.com/eslint/eslint/pull/12700) + +## Lint files matched by `overrides[].files` by default + +Previously to v7.0.0, ESLint would only lint files with a `.js` extension by default if you give directories like `eslint src`. + +ESLint v7.0.0 will now additionally lint files with other extensions (`.ts`, `.vue`, etc.) if the extension is explicitly matched by an `overrides[].files` entry. This will allow for users to lint files that don't end with `*.js` to be linted without having to use the `--ext` command line flag, as well as allow shared configuration authors to enable linting of these files without additional overhead for the end user. Please note that patterns that end with `*` are exempt from this behavior and will behave as they did previously. For example, if the following config file is present, + +```yml +# .eslintrc.yml +extends: my-config-js +overrides: + - files: "*.ts" + extends: my-config-ts +``` + +then running `eslint src` would check both `*.js` and `*.ts` files in the `src` directory. + +**To address:** Using the `--ext` CLI option will override this new behavior. Run ESLint with `--ext .js` if you are using `overrides` but only want to lint files that have a `.js` extension. + +If you maintain plugins that check files with extensions other than `.js`, this feature will allow you to check these files by default by configuring an `overrides` setting in your `recommended` preset. + +**Related issue(s):** [RFC20](https://github.com/eslint/rfcs/blob/master/designs/2019-additional-lint-targets/README.md), [#12677](https://github.com/eslint/eslint/pull/12677) + +## The base path of `overrides` and `ignorePatterns` has changed when using the `--config`/`--ignore-path` options + +Up until now, ESLint has resolved the following paths relative to the directory path of the _entry_ configuration file: + +- Configuration files (`.eslintrc.*`) + - relative paths in the `overrides[].files` setting + - relative paths in the `overrides[].excludedFiles` setting + - paths which start with `/` in the `ignorePatterns` setting +- Ignore files (`.eslintignore`) + - paths which start with `/` + +Starting in ESLint v7.0.0, configuration files and ignore files passed to ESLint using the `--config path/to/a-config` and `--ignore-path path/to/a-ignore` CLI flags, respectively, will resolve from the current working directory rather than the file location. This allows for users to utilize shared plugins without having to install them directly in their project. + +**To address:** Update the affected paths if you are using a configuration or ignore file via the `--config` or `--ignore-path` CLI options. + +**Related issue(s):** [RFC37](https://github.com/eslint/rfcs/blob/master/designs/2019-changing-base-path-in-config-files-that-cli-options-specify/README.md), [#12887](https://github.com/eslint/eslint/pull/12887) + +## Plugin resolution has been updated + +In previous versions, ESLint resolved all plugins from the current working directory by default. + +Starting in ESLint v7.0.0, `plugins` are resolved relative to the directory path of the _entry_ configuration file. + +This will not change anything in most cases. If a configuration file in a subdirectory has `plugins` defined, the plugins will be loaded from the subdirectory (or ancestor directories that include the current working directory if not found). + +This means that if you are using a config file from a shared location via `--config` option, the plugins that the config file declare will be loaded from the shared config file location. + +**To address:** Ensure that plugins are installed in a place that can be resolved relative to your configuration file or use `--resolve-plugins-relative-to .` to override this change. + +**Related issue(s):** [RFC47](https://github.com/eslint/rfcs/blob/master/designs/2019-plugin-loading-improvement/README.md), [#12922](https://github.com/eslint/eslint/pull/12922) + +## Runtime deprecation warnings for `~/.eslintrc.*` config files + +Personal config files have been deprecated since [v6.7.0](https://eslint.org/blog/2019/11/eslint-v6.7.0-released). ESLint v7.0.0 will start printing runtime deprecation warnings. It will print a warning for the following situations: + +1. When a project does not have a configuration file present and ESLint loads configuration from `~/.eslintrc.*`. +1. When a project has a configuration file and ESLint ignored a `~/.eslintrc.*` configuration file. This occurs when the `$HOME` directory is an ancestor directory of the project and the project's configuration files doesn't contain `root:true`. + +**To address:** Remove `~/.eslintrc.*` configuration files and add a `.eslintrc.*` configuration file to your project. Alternatively, use the `--config` option to use shared config files. + +**Related issue(s):** [RFC32](https://github.com/eslint/rfcs/tree/master/designs/2019-deprecating-personal-config/README.md), [#12678](https://github.com/eslint/eslint/pull/12678) + +## Default ignore patterns have changed + +Up until now, ESLint has ignored the following files by default: + +- Dotfiles (`.*`) +- `node_modules` in the current working directory (`/node_modules/*`) +- `bower_components` in the current working directory (`/bower_components/*`) + +ESLint v7.0.0 ignores `node_modules/*` of subdirectories as well, but no longer ignores `bower_components/*` and `.eslintrc.js`. Therefore, the new default ignore patterns are: + +- Dotfiles except `.eslintrc.*` (`.*` but not `.eslintrc.*`) +- `node_modules` (`/**/node_modules/*`) + +**To address:** Modify your `.eslintignore` or the `ignorePatterns` property of your config file if you don't want to lint `bower_components/*` and `.eslintrc.js`. + +**Related issue(s):** [RFC51](https://github.com/eslint/rfcs/blob/master/designs/2019-update-default-ignore-patterns/README.md), [#12888](https://github.com/eslint/eslint/pull/12888) + +## Descriptions in directive comments + +In older version of ESLint, there was no convenient way to indicate why a directive comment – such as `/*eslint-disable*/` – was necessary. + +To allow for the colocation of comments that provide context with the directive, ESLint v7.0.0 adds the ability to append arbitrary text in directive comments by ignoring text following `--` surrounded by whitespace. For example: + +```js +// eslint-disable-next-line a-rule, another-rule -- those are buggy!! +``` + +**To address:** If you have `--` surrounded by whitespace in directive comments, consider moving it into your configuration file. + +**Related issue(s):** [RFC33](https://github.com/eslint/rfcs/blob/master/designs/2019-description-in-directive-comments/README.md), [#12699](https://github.com/eslint/eslint/pull/12699) + +## Node.js/CommonJS rules have been deprecated + +The ten Node.js/CommonJS rules in core have been deprecated and moved to the [eslint-plugin-node](https://github.com/mysticatea/eslint-plugin-node) plugin. + +**To address:** As per [our deprecation policy](https://eslint.org/docs/user-guide/rule-deprecation), the deprecated rules will remain in core for the foreseeable future and are still available for use. However, we will no longer be updating or fixing any bugs in those rules. To use a supported version of the rules, we recommend using the corresponding rules in the plugin instead. + +| Deprecated Rules | Replacement | +| :--------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------ | +| [callback-return](https://eslint.org/docs/rules/callback-return) | [node/callback-return](https://github.com/mysticatea/eslint-plugin-node/blob/v11.1.0/docs/rules/callback-return.md) | +| [global-require](https://eslint.org/docs/rules/global-require) | [node/global-require](https://github.com/mysticatea/eslint-plugin-node/blob/v11.1.0/docs/rules/global-require.md) | +| [handle-callback-err](https://eslint.org/docs/rules/handle-callback-err) | [node/handle-callback-err](https://github.com/mysticatea/eslint-plugin-node/blob/v11.1.0/docs/rules/handle-callback-err.md) | +| [no-mixed-requires](https://eslint.org/docs/rules/no-mixed-requires) | [node/no-mixed-requires](https://github.com/mysticatea/eslint-plugin-node/blob/v11.1.0/docs/rules/no-mixed-requires.md) | +| [no-new-require](https://eslint.org/docs/rules/no-new-require) | [node/no-new-require](https://github.com/mysticatea/eslint-plugin-node/blob/v11.1.0/docs/rules/no-new-require.md) | +| [no-path-concat](https://eslint.org/docs/rules/no-path-concat) | [node/no-path-concat](https://github.com/mysticatea/eslint-plugin-node/blob/v11.1.0/docs/rules/no-path-concat.md) | +| [no-process-env](https://eslint.org/docs/rules/no-process-env) | [node/no-process-env](https://github.com/mysticatea/eslint-plugin-node/blob/v11.1.0/docs/rules/no-process-env.md) | +| [no-process-exit](https://eslint.org/docs/rules/no-process-exit) | [node/no-process-exit](https://github.com/mysticatea/eslint-plugin-node/blob/v11.1.0/docs/rules/no-process-exit.md) | +| [no-restricted-modules](https://eslint.org/docs/rules/no-restricted-modules) | [node/no-restricted-require](https://github.com/mysticatea/eslint-plugin-node/blob/v11.1.0/docs/rules/no-restricted-require.md) | +| [no-sync](https://eslint.org/docs/rules/no-sync) | [node/no-sync](https://github.com/mysticatea/eslint-plugin-node/blob/v11.1.0/docs/rules/no-sync.md) | + +**Related issue(s):** [#12898](https://github.com/eslint/eslint/pull/12898) + +## Several rules have been updated to cover more cases + +Several rules have been enhanced and now report additional errors: + +- [accessor-pairs](https://eslint.org/docs/rules/accessor-pairs) rule now recognizes class members by default. +- [array-callback-return](https://eslint.org/docs/rules/array-callback-return) rule now recognizes `flatMap` method. +- [computed-property-spacing](https://eslint.org/docs/rules/computed-property-spacing) rule now recognizes class members by default. +- [func-names](https://eslint.org/docs/rules/func-names) rule now recognizes function declarations in default exports. +- [no-extra-parens](https://eslint.org/docs/rules/no-extra-parens) rule now recognizes parentheses in assignment targets. +- [no-dupe-class-members](https://eslint.org/docs/rules/no-dupe-class-members) rule now recognizes computed keys for static class members. +- [no-magic-number](https://eslint.org/docs/rules/no-magic-number) rule now recognizes bigint literals. +- [radix](https://eslint.org/docs/rules/radix) rule now recognizes invalid numbers for the second parameter of `parseInt()`. +- [use-isnan](https://eslint.org/docs/rules/use-isnan) rule now recognizes class members by default. +- [yoda](https://eslint.org/docs/rules/yoda) rule now recognizes bigint literals. + +**To address:** Fix errors or disable these rules. + +**Related issue(s):** [#12490](https://github.com/eslint/eslint/pull/12490), [#12608](https://github.com/eslint/eslint/pull/12608), [#12670](https://github.com/eslint/eslint/pull/12670), [#12701](https://github.com/eslint/eslint/pull/12701), [#12765](https://github.com/eslint/eslint/pull/12765), [#12837](https://github.com/eslint/eslint/pull/12837), [#12913](https://github.com/eslint/eslint/pull/12913), [#12915](https://github.com/eslint/eslint/pull/12915), [#12919](https://github.com/eslint/eslint/pull/12919) + +## `eslint:recommended` has been updated + +Three new rules have been enabled in the `eslint:recommended` preset. + +- [no-dupe-else-if](https://eslint.org/docs/rules/no-dupe-else-if) +- [no-import-assign](https://eslint.org/docs/rules/no-import-assign) +- [no-setter-return](https://eslint.org/docs/rules/no-setter-return) + +**To address:** Fix errors or disable these rules. + +**Related issue(s):** [#12920](https://github.com/eslint/eslint/pull/12920) + +## Additional validation added to the `RuleTester` class + +The `RuleTester` now validates the following: + +- It fails test cases if the rule under test uses the non-standard `node.start` or `node.end` properties. Rules should use `node.range` instead. +- It fails test cases if the rule under test provides an autofix but a test case doesn't have an `output` property. Add an `output` property to test cases to test the rule's autofix functionality. +- It fails test cases if any unknown properties are found in the objects in the `errors` property. + +**To address:** Modify your rule or test case if existing test cases fail. + +**Related issue(s):** [RFC25](https://github.com/eslint/rfcs/blob/master/designs/2019-rule-tester-improvements/README.md), [#12096](https://github.com/eslint/eslint/pull/12096), [#12955](https://github.com/eslint/eslint/pull/12955) + +## The `CLIEngine` class has been deprecated + +The [`CLIEngine` class](https://eslint.org/docs/developer-guide/nodejs-api#cliengine) has been deprecated and replaced by the new [`ESLint` class](https://eslint.org/docs/developer-guide/nodejs-api#eslint-class). + +The `CLIEngine` class provides a synchronous API that is blocking the implementation of features such as parallel linting, supporting ES modules in shareable configs/parsers/plugins/formatters, and adding the ability to visually display the progress of linting runs. The new `ESLint` class provides an asynchronous API that ESLint core will now using going forward. `CLIEngine` will remain in core for the foreseeable future but may be removed in a future major version. + +**To address:** Update your code to use the new `ESLint` class if you are currently using `CLIEngine`. The following table maps the existing `CLIEngine` methods to their `ESLint` counterparts: + +| `CLIEngine` | `ESLint` | +| :------------------------------------------- | :--------------------------------- | +| `executeOnFiles(patterns)` | `lintFiles(patterns)` | +| `executeOnText(text, filePath, warnIgnored)` | `lintText(text, options)` | +| `getFormatter(name)` | `loadFormatter(name)` | +| `getConfigForFile(filePath)` | `calculateConfigForFile(filePath)` | +| `isPathIgnored(filePath)` | `isPathIgnored(filePath)` | +| `static outputFixes(results)` | `static outputFixes(results)` | +| `static getErrorResults(results)` | `static getErrorResults(results)` | +| `static getFormatter(name)` | (removed ※1) | +| `addPlugin(pluginId, definition)` | the `plugins` constructor option | +| `getRules()` | (not implemented yet) | +| `resolveFileGlobPatterns()` | (removed ※2) | + +- ※1 The `engine.getFormatter()` method currently returns the object of loaded packages as-is, which made it difficult to add new features to formatters for backward compatibility reasons. The new `eslint.loadFormatter()` method returns an adapter object that wraps the object of loaded packages, to ease the process of adding new features. Additionally, the adapter object has access to the `ESLint` instance to calculate default data (using loaded plugin rules to make `rulesMeta`, for example). As a result, the `ESLint` class only implements an instance version of the `loadFormatter()` method. +- ※2 Since ESLint 6, ESLint uses different logic from the `resolveFileGlobPatterns()` method to iterate files, making this method obsolete. + +**Related issue(s):** [RFC40](https://github.com/eslint/rfcs/blob/master/designs/2019-move-to-async-api/README.md), [#12939](https://github.com/eslint/eslint/pull/12939) diff --git a/eslint/lib/api.js b/eslint/lib/api.js index 40a5cc9..e4b6643 100644 --- a/eslint/lib/api.js +++ b/eslint/lib/api.js @@ -6,6 +6,7 @@ "use strict"; const { CLIEngine } = require("./cli-engine"); +const { ESLint } = require("./eslint"); const { Linter } = require("./linter"); const { RuleTester } = require("./rule-tester"); const { SourceCode } = require("./source-code"); @@ -13,6 +14,7 @@ const { SourceCode } = require("./source-code"); module.exports = { Linter, CLIEngine, + ESLint, RuleTester, SourceCode }; diff --git a/eslint/lib/cli-engine/cascading-config-array-factory.js b/eslint/lib/cli-engine/cascading-config-array-factory.js index b53f67b..f54605c 100644 --- a/eslint/lib/cli-engine/cascading-config-array-factory.js +++ b/eslint/lib/cli-engine/cascading-config-array-factory.js @@ -279,6 +279,18 @@ class CascadingConfigArrayFactory { ); } + /** + * Set the config data to override all configs. + * Require to call `clearCache()` method after this method is called. + * @param {ConfigData} configData The config data to override all configs. + * @returns {void} + */ + setOverrideConfig(configData) { + const slots = internalSlotsMap.get(this); + + slots.cliConfigData = configData; + } + /** * Clear config cache. * @returns {void} diff --git a/eslint/lib/cli-engine/cli-engine.js b/eslint/lib/cli-engine/cli-engine.js index 72d1fa4..b6aa995 100644 --- a/eslint/lib/cli-engine/cli-engine.js +++ b/eslint/lib/cli-engine/cli-engine.js @@ -39,6 +39,7 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]); // For VSCode IntelliSense /** @typedef {import("../shared/types").ConfigData} ConfigData */ +/** @typedef {import("../shared/types").DeprecatedRuleInfo} DeprecatedRuleInfo */ /** @typedef {import("../shared/types").LintMessage} LintMessage */ /** @typedef {import("../shared/types").ParserOptions} ParserOptions */ /** @typedef {import("../shared/types").Plugin} Plugin */ @@ -50,29 +51,29 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]); /** * The options to configure a CLI engine with. * @typedef {Object} CLIEngineOptions - * @property {boolean} allowInlineConfig Enable or disable inline configuration comments. - * @property {ConfigData} baseConfig Base config object, extended by all configs used with this CLIEngine instance - * @property {boolean} cache Enable result caching. - * @property {string} cacheLocation The cache file to use instead of .eslintcache. - * @property {string} configFile The configuration file to use. - * @property {string} cwd The value to use for the current working directory. - * @property {string[]} envs An array of environments to load. - * @property {string[]|null} extensions An array of file extensions to check. - * @property {boolean|Function} fix Execute in autofix mode. If a function, should return a boolean. - * @property {string[]} fixTypes Array of rule types to apply fixes for. - * @property {string[]} globals An array of global variables to declare. - * @property {boolean} ignore False disables use of .eslintignore. - * @property {string} ignorePath The ignore file to use instead of .eslintignore. - * @property {string|string[]} ignorePattern One or more glob patterns to ignore. - * @property {boolean} useEslintrc False disables looking for .eslintrc - * @property {string} parser The name of the parser to use. - * @property {ParserOptions} parserOptions An object of parserOption settings to use. - * @property {string[]} plugins An array of plugins to load. - * @property {Record} rules An object of rules to use. - * @property {string[]} rulePaths An array of directories to load custom rules from. - * @property {boolean} reportUnusedDisableDirectives `true` adds reports for unused eslint-disable directives - * @property {boolean} globInputPaths Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. - * @property {string} resolvePluginsRelativeTo The folder where plugins should be resolved from, defaulting to the CWD + * @property {boolean} [allowInlineConfig] Enable or disable inline configuration comments. + * @property {ConfigData} [baseConfig] Base config object, extended by all configs used with this CLIEngine instance + * @property {boolean} [cache] Enable result caching. + * @property {string} [cacheLocation] The cache file to use instead of .eslintcache. + * @property {string} [configFile] The configuration file to use. + * @property {string} [cwd] The value to use for the current working directory. + * @property {string[]} [envs] An array of environments to load. + * @property {string[]|null} [extensions] An array of file extensions to check. + * @property {boolean|Function} [fix] Execute in autofix mode. If a function, should return a boolean. + * @property {string[]} [fixTypes] Array of rule types to apply fixes for. + * @property {string[]} [globals] An array of global variables to declare. + * @property {boolean} [ignore] False disables use of .eslintignore. + * @property {string} [ignorePath] The ignore file to use instead of .eslintignore. + * @property {string|string[]} [ignorePattern] One or more glob patterns to ignore. + * @property {boolean} [useEslintrc] False disables looking for .eslintrc + * @property {string} [parser] The name of the parser to use. + * @property {ParserOptions} [parserOptions] An object of parserOption settings to use. + * @property {string[]} [plugins] An array of plugins to load. + * @property {Record} [rules] An object of rules to use. + * @property {string[]} [rulePaths] An array of directories to load custom rules from. + * @property {boolean} [reportUnusedDisableDirectives] `true` adds reports for unused eslint-disable directives + * @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. + * @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD */ /** @@ -88,13 +89,6 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]); * @property {string} [output] The source code of the file that was linted, with as many fixes applied as possible. */ -/** - * Information of deprecated rules. - * @typedef {Object} DeprecatedRuleInfo - * @property {string} ruleId The rule ID. - * @property {string[]} replacedBy The rule IDs that replace this deprecated rule. - */ - /** * Linting results. * @typedef {Object} LintReport @@ -821,16 +815,22 @@ class CLIEngine { lintResultCache.reconcile(); } - // Collect used deprecated rules. - const usedDeprecatedRules = Array.from( - iterateRuleDeprecationWarnings(lastConfigArrays) - ); - debug(`Linting complete in: ${Date.now() - startTime}ms`); + let usedDeprecatedRules; + return { results, ...calculateStatsPerRun(results), - usedDeprecatedRules + + // Initialize it lazily because CLI and `ESLint` API don't use it. + get usedDeprecatedRules() { + if (!usedDeprecatedRules) { + usedDeprecatedRules = Array.from( + iterateRuleDeprecationWarnings(lastConfigArrays) + ); + } + return usedDeprecatedRules; + } }; } @@ -858,9 +858,9 @@ class CLIEngine { const startTime = Date.now(); const resolvedFilename = filename && path.resolve(cwd, filename); + // Clear the last used config arrays. lastConfigArrays.length = 0; - if (resolvedFilename && this.isPathIgnored(resolvedFilename)) { if (warnIgnored) { results.push(createIgnoreResult(resolvedFilename, cwd)); @@ -892,16 +892,22 @@ class CLIEngine { })); } - // Collect used deprecated rules. - const usedDeprecatedRules = Array.from( - iterateRuleDeprecationWarnings(lastConfigArrays) - ); - debug(`Linting complete in: ${Date.now() - startTime}ms`); + let usedDeprecatedRules; + return { results, ...calculateStatsPerRun(results), - usedDeprecatedRules + + // Initialize it lazily because CLI and `ESLint` API don't use it. + get usedDeprecatedRules() { + if (!usedDeprecatedRules) { + usedDeprecatedRules = Array.from( + iterateRuleDeprecationWarnings(lastConfigArrays) + ); + } + return usedDeprecatedRules; + } }; } @@ -955,11 +961,10 @@ class CLIEngine { } /** - * Returns the formatter representing the given format or null if no formatter - * with the given name can be found. + * Returns the formatter representing the given format or null if the `format` is not a string. * @param {string} [format] The name of the format to load or the path to a * custom formatter. - * @returns {Function} The formatter function or null if not found. + * @returns {(Function|null)} The formatter function or null if the `format` is not a string. */ getFormatter(format) { diff --git a/eslint/lib/cli-engine/config-array-factory.js b/eslint/lib/cli-engine/config-array-factory.js index b1429af..fa3fdb3 100644 --- a/eslint/lib/cli-engine/config-array-factory.js +++ b/eslint/lib/cli-engine/config-array-factory.js @@ -817,7 +817,7 @@ class ConfigArrayFactory { if (configData) { return this._normalizeConfigData(configData, { ...ctx, - filePath: plugin.filePath, + filePath: plugin.filePath || ctx.filePath, name: `${ctx.name} » plugin:${plugin.id}/${configName}` }); } @@ -978,7 +978,7 @@ class ConfigArrayFactory { if (plugin) { return new ConfigDependency({ definition: normalizePlugin(plugin), - filePath: ctx.filePath, + filePath: "", // It's unknown where the plugin came from. id, importerName: ctx.name, importerPath: ctx.filePath diff --git a/eslint/lib/cli-engine/config-array/config-array.js b/eslint/lib/cli-engine/config-array/config-array.js index b343419..42a7362 100644 --- a/eslint/lib/cli-engine/config-array/config-array.js +++ b/eslint/lib/cli-engine/config-array/config-array.js @@ -107,7 +107,7 @@ function getMatchedIndices(elements, filePath) { for (let i = elements.length - 1; i >= 0; --i) { const element = elements[i]; - if (!element.criteria || element.criteria.test(filePath)) { + if (!element.criteria || (filePath && element.criteria.test(filePath))) { indices.push(i); } } diff --git a/eslint/lib/cli-engine/config-array/ignore-pattern.js b/eslint/lib/cli-engine/config-array/ignore-pattern.js index 92690b9..6eaec42 100644 --- a/eslint/lib/cli-engine/config-array/ignore-pattern.js +++ b/eslint/lib/cli-engine/config-array/ignore-pattern.js @@ -71,7 +71,13 @@ function getCommonAncestorPath(sourcePaths) { } } - return result || path.sep; + let resolvedResult = result || path.sep; + + // if Windows common ancestor is root of drive must have trailing slash to be absolute. + if (resolvedResult && resolvedResult.endsWith(":") && process.platform === "win32") { + resolvedResult += path.sep; + } + return resolvedResult; } /** diff --git a/eslint/lib/cli.js b/eslint/lib/cli.js index 815ce68..ce11878 100644 --- a/eslint/lib/cli.js +++ b/eslint/lib/cli.js @@ -17,105 +17,176 @@ const fs = require("fs"), path = require("path"), - { CLIEngine } = require("./cli-engine"), - options = require("./options"), + { promisify } = require("util"), + { ESLint } = require("./eslint"), + CLIOptions = require("./options"), log = require("./shared/logging"), RuntimeInfo = require("./shared/runtime-info"); const debug = require("debug")("eslint:cli"); +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/** @typedef {import("./eslint/eslint").ESLintOptions} ESLintOptions */ +/** @typedef {import("./eslint/eslint").LintMessage} LintMessage */ +/** @typedef {import("./eslint/eslint").LintResult} LintResult */ + //------------------------------------------------------------------------------ // Helpers //------------------------------------------------------------------------------ +const mkdir = promisify(fs.mkdir); +const stat = promisify(fs.stat); +const writeFile = promisify(fs.writeFile); + /** * Predicate function for whether or not to apply fixes in quiet mode. * If a message is a warning, do not apply a fix. - * @param {LintResult} lintResult The lint result. + * @param {LintMessage} message The lint result. * @returns {boolean} True if the lint message is an error (and thus should be * autofixed), false otherwise. */ -function quietFixPredicate(lintResult) { - return lintResult.severity === 2; +function quietFixPredicate(message) { + return message.severity === 2; } /** * Translates the CLI options into the options expected by the CLIEngine. * @param {Object} cliOptions The CLI options to translate. - * @returns {CLIEngineOptions} The options object for the CLIEngine. + * @returns {ESLintOptions} The options object for the CLIEngine. * @private */ -function translateOptions(cliOptions) { +function translateOptions({ + cache, + cacheFile, + cacheLocation, + config, + env, + errorOnUnmatchedPattern, + eslintrc, + ext, + fix, + fixDryRun, + fixType, + global, + ignore, + ignorePath, + ignorePattern, + inlineConfig, + parser, + parserOptions, + plugin, + quiet, + reportUnusedDisableDirectives, + resolvePluginsRelativeTo, + rule, + rulesdir +}) { return { - envs: cliOptions.env, - extensions: cliOptions.ext, - rules: cliOptions.rule, - plugins: cliOptions.plugin, - globals: cliOptions.global, - ignore: cliOptions.ignore, - ignorePath: cliOptions.ignorePath, - ignorePattern: cliOptions.ignorePattern, - configFile: cliOptions.config, - rulePaths: cliOptions.rulesdir, - useEslintrc: cliOptions.eslintrc, - parser: cliOptions.parser, - parserOptions: cliOptions.parserOptions, - cache: cliOptions.cache, - cacheFile: cliOptions.cacheFile, - cacheLocation: cliOptions.cacheLocation, - fix: (cliOptions.fix || cliOptions.fixDryRun) && (cliOptions.quiet ? quietFixPredicate : true), - fixTypes: cliOptions.fixType, - allowInlineConfig: cliOptions.inlineConfig, - reportUnusedDisableDirectives: cliOptions.reportUnusedDisableDirectives, - resolvePluginsRelativeTo: cliOptions.resolvePluginsRelativeTo, - errorOnUnmatchedPattern: cliOptions.errorOnUnmatchedPattern + allowInlineConfig: inlineConfig, + cache, + cacheLocation: cacheLocation || cacheFile, + errorOnUnmatchedPattern, + extensions: ext, + fix: (fix || fixDryRun) && (quiet ? quietFixPredicate : true), + fixTypes: fixType, + ignore, + ignorePath, + overrideConfig: { + env: env && env.reduce((obj, name) => { + obj[name] = true; + return obj; + }, {}), + globals: global && global.reduce((obj, name) => { + if (name.endsWith(":true")) { + obj[name.slice(0, -5)] = "writable"; + } else { + obj[name] = "readonly"; + } + return obj; + }, {}), + ignorePatterns: ignorePattern, + parser, + parserOptions, + plugins: plugin, + rules: rule + }, + overrideConfigFile: config, + reportUnusedDisableDirectives: reportUnusedDisableDirectives ? "error" : void 0, + resolvePluginsRelativeTo, + rulePaths: rulesdir, + useEslintrc: eslintrc }; } +/** + * Count error messages. + * @param {LintResult[]} results The lint results. + * @returns {{errorCount:number;warningCount:number}} The number of error messages. + */ +function countErrors(results) { + let errorCount = 0; + let warningCount = 0; + + for (const result of results) { + errorCount += result.errorCount; + warningCount += result.warningCount; + } + + return { errorCount, warningCount }; +} + +/** + * Check if a given file path is a directory or not. + * @param {string} filePath The path to a file to check. + * @returns {Promise} `true` if the given path is a directory. + */ +async function isDirectory(filePath) { + try { + return (await stat(filePath)).isDirectory(); + } catch (error) { + if (error.code === "ENOENT" || error.code === "ENOTDIR") { + return false; + } + throw error; + } +} + /** * Outputs the results of the linting. - * @param {CLIEngine} engine The CLIEngine to use. + * @param {ESLint} engine The ESLint instance to use. * @param {LintResult[]} results The results to print. * @param {string} format The name of the formatter to use or the path to the formatter. * @param {string} outputFile The path for the output file. - * @returns {boolean} True if the printing succeeds, false if not. + * @returns {Promise} True if the printing succeeds, false if not. * @private */ -function printResults(engine, results, format, outputFile) { +async function printResults(engine, results, format, outputFile) { let formatter; - let rulesMeta; try { - formatter = engine.getFormatter(format); + formatter = await engine.loadFormatter(format); } catch (e) { log.error(e.message); return false; } - const output = formatter(results, { - get rulesMeta() { - if (!rulesMeta) { - rulesMeta = {}; - for (const [ruleId, rule] of engine.getRules()) { - rulesMeta[ruleId] = rule.meta; - } - } - return rulesMeta; - } - }); + const output = formatter.format(results); if (output) { if (outputFile) { const filePath = path.resolve(process.cwd(), outputFile); - if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) { + if (await isDirectory(filePath)) { log.error("Cannot write to output file path, it is a directory: %s", outputFile); return false; } try { - fs.mkdirSync(path.dirname(filePath), { recursive: true }); - fs.writeFileSync(filePath, output); + await mkdir(path.dirname(filePath), { recursive: true }); + await writeFile(filePath, output); } catch (ex) { log.error("There was a problem writing the output file:\n%s", ex); return false; @@ -126,7 +197,6 @@ function printResults(engine, results, format, outputFile) { } return true; - } //------------------------------------------------------------------------------ @@ -143,28 +213,33 @@ const cli = { * Executes the CLI based on an array of arguments that is passed in. * @param {string|Array|Object} args The arguments to process. * @param {string} [text] The text to lint (used for TTY). - * @returns {int} The exit code for the operation. + * @returns {Promise} The exit code for the operation. */ - execute(args, text) { + async execute(args, text) { if (Array.isArray(args)) { debug("CLI args: %o", args.slice(2)); } - - let currentOptions; + let options; try { - currentOptions = options.parse(args); + options = CLIOptions.parse(args); } catch (error) { log.error(error.message); return 2; } - const files = currentOptions._; + const files = options._; const useStdin = typeof text === "string"; - if (currentOptions.version) { + if (options.help) { + log.info(CLIOptions.generateHelp()); + return 0; + } + if (options.version) { log.info(RuntimeInfo.version()); - } else if (currentOptions.envInfo) { + return 0; + } + if (options.envInfo) { try { log.info(RuntimeInfo.environment()); return 0; @@ -172,7 +247,9 @@ const cli = { log.error(err.message); return 2; } - } else if (currentOptions.printConfig) { + } + + if (options.printConfig) { if (files.length) { log.error("The --print-config option must be used with exactly one file name."); return 2; @@ -182,58 +259,67 @@ const cli = { return 2; } - const engine = new CLIEngine(translateOptions(currentOptions)); - const fileConfig = engine.getConfigForFile(currentOptions.printConfig); + const engine = new ESLint(translateOptions(options)); + const fileConfig = + await engine.calculateConfigForFile(options.printConfig); log.info(JSON.stringify(fileConfig, null, " ")); return 0; - } else if (currentOptions.help || (!files.length && !useStdin)) { - log.info(options.generateHelp()); - } else { - debug(`Running on ${useStdin ? "text" : "files"}`); - - if (currentOptions.fix && currentOptions.fixDryRun) { - log.error("The --fix option and the --fix-dry-run option cannot be used together."); - return 2; - } + } - if (useStdin && currentOptions.fix) { - log.error("The --fix option is not available for piped-in code; use --fix-dry-run instead."); - return 2; - } + debug(`Running on ${useStdin ? "text" : "files"}`); - if (currentOptions.fixType && !currentOptions.fix && !currentOptions.fixDryRun) { - log.error("The --fix-type option requires either --fix or --fix-dry-run."); - return 2; - } + if (options.fix && options.fixDryRun) { + log.error("The --fix option and the --fix-dry-run option cannot be used together."); + return 2; + } + if (useStdin && options.fix) { + log.error("The --fix option is not available for piped-in code; use --fix-dry-run instead."); + return 2; + } + if (options.fixType && !options.fix && !options.fixDryRun) { + log.error("The --fix-type option requires either --fix or --fix-dry-run."); + return 2; + } - const engine = new CLIEngine(translateOptions(currentOptions)); - const report = useStdin ? engine.executeOnText(text, currentOptions.stdinFilename, true) : engine.executeOnFiles(files); + const engine = new ESLint(translateOptions(options)); + let results; - if (currentOptions.fix) { - debug("Fix mode enabled - applying fixes"); - CLIEngine.outputFixes(report); - } + if (useStdin) { + results = await engine.lintText(text, { + filePath: options.stdinFilename, + warnIgnored: true + }); + } else { + results = await engine.lintFiles(files); + } - if (currentOptions.quiet) { - debug("Quiet mode enabled - filtering out warnings"); - report.results = CLIEngine.getErrorResults(report.results); - } + if (options.fix) { + debug("Fix mode enabled - applying fixes"); + await ESLint.outputFixes(results); + } - if (printResults(engine, report.results, currentOptions.format, currentOptions.outputFile)) { - const tooManyWarnings = currentOptions.maxWarnings >= 0 && report.warningCount > currentOptions.maxWarnings; + if (options.quiet) { + debug("Quiet mode enabled - filtering out warnings"); + results = ESLint.getErrorResults(results); + } - if (!report.errorCount && tooManyWarnings) { - log.error("ESLint found too many warnings (maximum: %s).", currentOptions.maxWarnings); - } + if (await printResults(engine, results, options.format, options.outputFile)) { + const { errorCount, warningCount } = countErrors(results); + const tooManyWarnings = + options.maxWarnings >= 0 && warningCount > options.maxWarnings; - return (report.errorCount || tooManyWarnings) ? 1 : 0; + if (!errorCount && tooManyWarnings) { + log.error( + "ESLint found too many warnings (maximum: %s).", + options.maxWarnings + ); } - return 2; + return (errorCount || tooManyWarnings) ? 1 : 0; } - return 0; + return 2; } }; diff --git a/eslint/lib/eslint/eslint.js b/eslint/lib/eslint/eslint.js new file mode 100644 index 0000000..d195aab --- /dev/null +++ b/eslint/lib/eslint/eslint.js @@ -0,0 +1,656 @@ +/** + * @fileoverview Main API Class + * @author Kai Cataldo + * @author Toru Nagashima + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const path = require("path"); +const fs = require("fs"); +const { promisify } = require("util"); +const { CLIEngine, getCLIEngineInternalSlots } = require("../cli-engine/cli-engine"); +const BuiltinRules = require("../rules"); +const { getRuleSeverity } = require("../shared/config-ops"); +const { version } = require("../../package.json"); + +//------------------------------------------------------------------------------ +// Typedefs +//------------------------------------------------------------------------------ + +/** @typedef {import("../cli-engine/cli-engine").LintReport} CLIEngineLintReport */ +/** @typedef {import("../shared/types").DeprecatedRuleInfo} DeprecatedRuleInfo */ +/** @typedef {import("../shared/types").ConfigData} ConfigData */ +/** @typedef {import("../shared/types").LintMessage} LintMessage */ +/** @typedef {import("../shared/types").Plugin} Plugin */ +/** @typedef {import("../shared/types").Rule} Rule */ +/** @typedef {import("./load-formatter").Formatter} Formatter */ + +/** + * The options with which to configure the ESLint instance. + * @typedef {Object} ESLintOptions + * @property {boolean} [allowInlineConfig] Enable or disable inline configuration comments. + * @property {ConfigData} [baseConfig] Base config object, extended by all configs used with this instance + * @property {boolean} [cache] Enable result caching. + * @property {string} [cacheLocation] The cache file to use instead of .eslintcache. + * @property {string} [cwd] The value to use for the current working directory. + * @property {boolean} [errorOnUnmatchedPattern] If `false` then `ESLint#lintFiles()` doesn't throw even if no target files found. Defaults to `true`. + * @property {string[]} [extensions] An array of file extensions to check. + * @property {boolean|Function} [fix] Execute in autofix mode. If a function, should return a boolean. + * @property {string[]} [fixTypes] Array of rule types to apply fixes for. + * @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. + * @property {boolean} [ignore] False disables use of .eslintignore. + * @property {string} [ignorePath] The ignore file to use instead of .eslintignore. + * @property {ConfigData} [overrideConfig] Override config object, overrides all configs used with this instance + * @property {string} [overrideConfigFile] The configuration file to use. + * @property {Record} [plugins] An array of plugin implementations. + * @property {"error" | "warn" | "off"} [reportUnusedDisableDirectives] the severity to report unused eslint-disable directives. + * @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD. + * @property {string[]} [rulePaths] An array of directories to load custom rules from. + * @property {boolean} [useEslintrc] False disables looking for .eslintrc.* files. + */ + +/** + * A rules metadata object. + * @typedef {Object} RulesMeta + * @property {string} id The plugin ID. + * @property {Object} definition The plugin definition. + */ + +/** + * A linting result. + * @typedef {Object} LintResult + * @property {string} filePath The path to the file that was linted. + * @property {LintMessage[]} messages All of the messages for the result. + * @property {number} errorCount Number of errors for the result. + * @property {number} warningCount Number of warnings for the result. + * @property {number} fixableErrorCount Number of fixable errors for the result. + * @property {number} fixableWarningCount Number of fixable warnings for the result. + * @property {string} [source] The source code of the file that was linted. + * @property {string} [output] The source code of the file that was linted, with as many fixes applied as possible. + * @property {DeprecatedRuleInfo[]} usedDeprecatedRules The list of used deprecated rules. + */ + +/** + * Private members for the `ESLint` instance. + * @typedef {Object} ESLintPrivateMembers + * @property {CLIEngine} cliEngine The wrapped CLIEngine instance. + * @property {ESLintOptions} options The options used to instantiate the ESLint instance. + */ + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +const writeFile = promisify(fs.writeFile); + +/** + * The map with which to store private class members. + * @type {WeakMap} + */ +const privateMembersMap = new WeakMap(); + +/** + * Check if a given value is a non-empty string or not. + * @param {any} x The value to check. + * @returns {boolean} `true` if `x` is a non-empty string. + */ +function isNonEmptyString(x) { + return typeof x === "string" && x.trim() !== ""; +} + +/** + * Check if a given value is an array of non-empty stringss or not. + * @param {any} x The value to check. + * @returns {boolean} `true` if `x` is an array of non-empty stringss. + */ +function isArrayOfNonEmptyString(x) { + return Array.isArray(x) && x.every(isNonEmptyString); +} + +/** + * Check if a given value is a valid fix type or not. + * @param {any} x The value to check. + * @returns {boolean} `true` if `x` is valid fix type. + */ +function isFixType(x) { + return x === "problem" || x === "suggestion" || x === "layout"; +} + +/** + * Check if a given value is an array of fix types or not. + * @param {any} x The value to check. + * @returns {boolean} `true` if `x` is an array of fix types. + */ +function isFixTypeArray(x) { + return Array.isArray(x) && x.every(isFixType); +} + +/** + * The error for invalid options. + */ +class ESLintInvalidOptionsError extends Error { + constructor(messages) { + super(`Invalid Options:\n- ${messages.join("\n- ")}`); + this.code = "ESLINT_INVALID_OPTIONS"; + Error.captureStackTrace(this, ESLintInvalidOptionsError); + } +} + +/** + * Validates and normalizes options for the wrapped CLIEngine instance. + * @param {ESLintOptions} options The options to process. + * @returns {ESLintOptions} The normalized options. + */ +function processOptions({ + allowInlineConfig = true, // ← we cannot use `overrideConfig.noInlineConfig` instead because `allowInlineConfig` has side-effect that suppress warnings that show inline configs are ignored. + baseConfig = null, + cache = false, + cacheLocation = ".eslintcache", + cwd = process.cwd(), + errorOnUnmatchedPattern = true, + extensions = null, // ← should be null by default because if it's an array then it suppresses RFC20 feature. + fix = false, + fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property. + globInputPaths = true, + ignore = true, + ignorePath = null, // ← should be null by default because if it's a string then it may throw ENOENT. + overrideConfig = null, + overrideConfigFile = null, + plugins = {}, + reportUnusedDisableDirectives = null, // ← should be null by default because if it's a string then it overrides the 'reportUnusedDisableDirectives' setting in config files. And we cannot use `overrideConfig.reportUnusedDisableDirectives` instead because we cannot configure the `error` severity with that. + resolvePluginsRelativeTo = null, // ← should be null by default because if it's a string then it suppresses RFC47 feature. + rulePaths = [], + useEslintrc = true, + ...unknownOptions +}) { + const errors = []; + const unknownOptionKeys = Object.keys(unknownOptions); + + if (unknownOptionKeys.length >= 1) { + errors.push(`Unknown options: ${unknownOptionKeys.join(", ")}`); + if (unknownOptionKeys.includes("cacheFile")) { + errors.push("'cacheFile' has been removed. Please use the 'cacheLocation' option instead."); + } + if (unknownOptionKeys.includes("configFile")) { + errors.push("'configFile' has been removed. Please use the 'overrideConfigFile' option instead."); + } + if (unknownOptionKeys.includes("envs")) { + errors.push("'envs' has been removed. Please use the 'overrideConfig.env' option instead."); + } + if (unknownOptionKeys.includes("globals")) { + errors.push("'globals' has been removed. Please use the 'overrideConfig.globals' option instead."); + } + if (unknownOptionKeys.includes("ignorePattern")) { + errors.push("'ignorePattern' has been removed. Please use the 'overrideConfig.ignorePatterns' option instead."); + } + if (unknownOptionKeys.includes("parser")) { + errors.push("'parser' has been removed. Please use the 'overrideConfig.parser' option instead."); + } + if (unknownOptionKeys.includes("parserOptions")) { + errors.push("'parserOptions' has been removed. Please use the 'overrideConfig.parserOptions' option instead."); + } + if (unknownOptionKeys.includes("rules")) { + errors.push("'rules' has been removed. Please use the 'overrideConfig.rules' option instead."); + } + } + if (typeof allowInlineConfig !== "boolean") { + errors.push("'allowInlineConfig' must be a boolean."); + } + if (typeof baseConfig !== "object") { + errors.push("'baseConfig' must be an object or null."); + } + if (typeof cache !== "boolean") { + errors.push("'cache' must be a boolean."); + } + if (!isNonEmptyString(cacheLocation)) { + errors.push("'cacheLocation' must be a non-empty string."); + } + if (!isNonEmptyString(cwd) || !path.isAbsolute(cwd)) { + errors.push("'cwd' must be an absolute path."); + } + if (typeof errorOnUnmatchedPattern !== "boolean") { + errors.push("'errorOnUnmatchedPattern' must be a boolean."); + } + if (!isArrayOfNonEmptyString(extensions) && extensions !== null) { + errors.push("'extensions' must be an array of non-empty strings or null."); + } + if (typeof fix !== "boolean" && typeof fix !== "function") { + errors.push("'fix' must be a boolean or a function."); + } + if (fixTypes !== null && !isFixTypeArray(fixTypes)) { + errors.push("'fixTypes' must be an array of any of \"problem\", \"suggestion\", and \"layout\"."); + } + if (typeof globInputPaths !== "boolean") { + errors.push("'globInputPaths' must be a boolean."); + } + if (typeof ignore !== "boolean") { + errors.push("'ignore' must be a boolean."); + } + if (!isNonEmptyString(ignorePath) && ignorePath !== null) { + errors.push("'ignorePath' must be a non-empty string or null."); + } + if (typeof overrideConfig !== "object") { + errors.push("'overrideConfig' must be an object or null."); + } + if (!isNonEmptyString(overrideConfigFile) && overrideConfigFile !== null) { + errors.push("'overrideConfigFile' must be a non-empty string or null."); + } + if (typeof plugins !== "object") { + errors.push("'plugins' must be an object or null."); + } else if (plugins !== null && Object.keys(plugins).includes("")) { + errors.push("'plugins' must not include an empty string."); + } + if (Array.isArray(plugins)) { + errors.push("'plugins' doesn't add plugins to configuration to load. Please use the 'overrideConfig.plugins' option instead."); + } + if ( + reportUnusedDisableDirectives !== "error" && + reportUnusedDisableDirectives !== "warn" && + reportUnusedDisableDirectives !== "off" && + reportUnusedDisableDirectives !== null + ) { + errors.push("'reportUnusedDisableDirectives' must be any of \"error\", \"warn\", \"off\", and null."); + } + if ( + !isNonEmptyString(resolvePluginsRelativeTo) && + resolvePluginsRelativeTo !== null + ) { + errors.push("'resolvePluginsRelativeTo' must be a non-empty string or null."); + } + if (!isArrayOfNonEmptyString(rulePaths)) { + errors.push("'rulePaths' must be an array of non-empty strings."); + } + if (typeof useEslintrc !== "boolean") { + errors.push("'useElintrc' must be a boolean."); + } + + if (errors.length > 0) { + throw new ESLintInvalidOptionsError(errors); + } + + return { + allowInlineConfig, + baseConfig, + cache, + cacheLocation, + configFile: overrideConfigFile, + cwd, + errorOnUnmatchedPattern, + extensions, + fix, + fixTypes, + globInputPaths, + ignore, + ignorePath, + reportUnusedDisableDirectives, + resolvePluginsRelativeTo, + rulePaths, + useEslintrc + }; +} + +/** + * Check if a value has one or more properties and that value is not undefined. + * @param {any} obj The value to check. + * @returns {boolean} `true` if `obj` has one or more properties that that value is not undefined. + */ +function hasDefinedProperty(obj) { + if (typeof obj === "object" && obj !== null) { + for (const key in obj) { + if (typeof obj[key] !== "undefined") { + return true; + } + } + } + return false; +} + +/** + * Create rulesMeta object. + * @param {Map} rules a map of rules from which to generate the object. + * @returns {Object} metadata for all enabled rules. + */ +function createRulesMeta(rules) { + return Array.from(rules).reduce((retVal, [id, rule]) => { + retVal[id] = rule.meta; + return retVal; + }, {}); +} + +/** @type {WeakMap} */ +const usedDeprecatedRulesCache = new WeakMap(); + +/** + * Create used deprecated rule list. + * @param {CLIEngine} cliEngine The CLIEngine instance. + * @param {string} maybeFilePath The absolute path to a lint target file or `""`. + * @returns {DeprecatedRuleInfo[]} The used deprecated rule list. + */ +function getOrFindUsedDeprecatedRules(cliEngine, maybeFilePath) { + const { + configArrayFactory, + options: { cwd } + } = getCLIEngineInternalSlots(cliEngine); + const filePath = path.isAbsolute(maybeFilePath) + ? maybeFilePath + : path.join(cwd, "__placeholder__.js"); + const configArray = configArrayFactory.getConfigArrayForFile(filePath); + const config = configArray.extractConfig(filePath); + + // Most files use the same config, so cache it. + if (!usedDeprecatedRulesCache.has(config)) { + const pluginRules = configArray.pluginRules; + const retv = []; + + for (const [ruleId, ruleConf] of Object.entries(config.rules)) { + if (getRuleSeverity(ruleConf) === 0) { + continue; + } + const rule = pluginRules.get(ruleId) || BuiltinRules.get(ruleId); + const meta = rule && rule.meta; + + if (meta && meta.deprecated) { + retv.push({ ruleId, replacedBy: meta.replacedBy || [] }); + } + } + + usedDeprecatedRulesCache.set(config, Object.freeze(retv)); + } + + return usedDeprecatedRulesCache.get(config); +} + +/** + * Processes the linting results generated by a CLIEngine linting report to + * match the ESLint class's API. + * @param {CLIEngine} cliEngine The CLIEngine instance. + * @param {CLIEngineLintReport} report The CLIEngine linting report to process. + * @returns {LintResult[]} The processed linting results. + */ +function processCLIEngineLintReport(cliEngine, { results }) { + const descriptor = { + configurable: true, + enumerable: true, + get() { + return getOrFindUsedDeprecatedRules(cliEngine, this.filePath); + } + }; + + for (const result of results) { + Object.defineProperty(result, "usedDeprecatedRules", descriptor); + } + + return results; +} + +/** + * An Array.prototype.sort() compatible compare function to order results by their file path. + * @param {LintResult} a The first lint result. + * @param {LintResult} b The second lint result. + * @returns {number} An integer representing the order in which the two results should occur. + */ +function compareResultsByFilePath(a, b) { + if (a.filePath < b.filePath) { + return -1; + } + + if (a.filePath > b.filePath) { + return 1; + } + + return 0; +} + +class ESLint { + + /** + * Creates a new instance of the main ESLint API. + * @param {ESLintOptions} options The options for this instance. + */ + constructor(options = {}) { + const processedOptions = processOptions(options); + const cliEngine = new CLIEngine(processedOptions); + const { + additionalPluginPool, + configArrayFactory, + lastConfigArrays + } = getCLIEngineInternalSlots(cliEngine); + let updated = false; + + /* + * Address `plugins` to add plugin implementations. + * Operate the `additionalPluginPool` internal slot directly to avoid + * using `addPlugin(id, plugin)` method that resets cache everytime. + */ + if (options.plugins) { + for (const [id, plugin] of Object.entries(options.plugins)) { + additionalPluginPool.set(id, plugin); + updated = true; + } + } + + /* + * Address `overrideConfig` to set override config. + * Operate the `configArrayFactory` internal slot directly because this + * functionality doesn't exist as the public API of CLIEngine. + */ + if (hasDefinedProperty(options.overrideConfig)) { + configArrayFactory.setOverrideConfig(options.overrideConfig); + updated = true; + } + + // Update caches. + if (updated) { + configArrayFactory.clearCache(); + lastConfigArrays[0] = configArrayFactory.getConfigArrayForFile(); + } + + // Initialize private properties. + privateMembersMap.set(this, { + cliEngine, + options: processedOptions + }); + } + + /** + * The version text. + * @type {string} + */ + static get version() { + return version; + } + + /** + * Outputs fixes from the given results to files. + * @param {LintResult[]} results The lint results. + * @returns {Promise} Returns a promise that is used to track side effects. + */ + static async outputFixes(results) { + if (!Array.isArray(results)) { + throw new Error("'results' must be an array"); + } + + await Promise.all( + results + .filter(result => { + if (typeof result !== "object" || result === null) { + throw new Error("'results' must include only objects"); + } + return ( + typeof result.output === "string" && + path.isAbsolute(result.filePath) + ); + }) + .map(r => writeFile(r.filePath, r.output)) + ); + } + + /** + * Returns results that only contains errors. + * @param {LintResult[]} results The results to filter. + * @returns {LintResult[]} The filtered results. + */ + static getErrorResults(results) { + return CLIEngine.getErrorResults(results); + } + + /** + * Executes the current configuration on an array of file and directory names. + * @param {string[]} patterns An array of file and directory names. + * @returns {Promise} The results of linting the file patterns given. + */ + async lintFiles(patterns) { + if (!isNonEmptyString(patterns) && !isArrayOfNonEmptyString(patterns)) { + throw new Error("'patterns' must be a non-empty string or an array of non-empty strings"); + } + const { cliEngine } = privateMembersMap.get(this); + + return processCLIEngineLintReport( + cliEngine, + cliEngine.executeOnFiles(patterns) + ); + } + + /** + * Executes the current configuration on text. + * @param {string} code A string of JavaScript code to lint. + * @param {Object} [options] The options. + * @param {string} [options.filePath] The path to the file of the source code. + * @param {boolean} [options.warnIgnored] When set to true, warn if given filePath is an ignored path. + * @returns {Promise} The results of linting the string of code given. + */ + async lintText(code, options = {}) { + if (typeof code !== "string") { + throw new Error("'code' must be a string"); + } + if (typeof options !== "object") { + throw new Error("'options' must be an object, null, or undefined"); + } + const { + filePath, + warnIgnored = false, + ...unknownOptions + } = options || {}; + + for (const key of Object.keys(unknownOptions)) { + throw new Error(`'options' must not include the unknown option '${key}'`); + } + if (filePath !== void 0 && !isNonEmptyString(filePath)) { + throw new Error("'options.filePath' must be a non-empty string or undefined"); + } + if (typeof warnIgnored !== "boolean") { + throw new Error("'options.warnIgnored' must be a boolean or undefined"); + } + + const { cliEngine } = privateMembersMap.get(this); + + return processCLIEngineLintReport( + cliEngine, + cliEngine.executeOnText(code, filePath, warnIgnored) + ); + } + + /** + * Returns the formatter representing the given formatter name. + * @param {string} [name] The name of the formattter to load. + * The following values are allowed: + * - `undefined` ... Load `stylish` builtin formatter. + * - A builtin formatter name ... Load the builtin formatter. + * - A thirdparty formatter name: + * - `foo` → `eslint-formatter-foo` + * - `@foo` → `@foo/eslint-formatter` + * - `@foo/bar` → `@foo/eslint-formatter-bar` + * - A file path ... Load the file. + * @returns {Promise} A promise resolving to the formatter object. + * This promise will be rejected if the given formatter was not found or not + * a function. + */ + async loadFormatter(name = "stylish") { + if (typeof name !== "string") { + throw new Error("'name' must be a string"); + } + + const { cliEngine } = privateMembersMap.get(this); + const formatter = cliEngine.getFormatter(name); + + if (typeof formatter !== "function") { + throw new Error(`Formatter must be a function, but got a ${typeof formatter}.`); + } + + return { + + /** + * The main formatter method. + * @param {LintResults[]} results The lint results to format. + * @returns {string} The formatted lint results. + */ + format(results) { + let rulesMeta = null; + + results.sort(compareResultsByFilePath); + + return formatter(results, { + get rulesMeta() { + if (!rulesMeta) { + rulesMeta = createRulesMeta(cliEngine.getRules()); + } + + return rulesMeta; + } + }); + } + }; + } + + /** + * Returns a configuration object for the given file based on the CLI options. + * This is the same logic used by the ESLint CLI executable to determine + * configuration for each file it processes. + * @param {string} filePath The path of the file to retrieve a config object for. + * @returns {Promise} A configuration object for the file. + */ + async calculateConfigForFile(filePath) { + if (!isNonEmptyString(filePath)) { + throw new Error("'filePath' must be a non-empty string"); + } + const { cliEngine } = privateMembersMap.get(this); + + return cliEngine.getConfigForFile(filePath); + } + + /** + * Checks if a given path is ignored by ESLint. + * @param {string} filePath The path of the file to check. + * @returns {Promise} Whether or not the given path is ignored. + */ + async isPathIgnored(filePath) { + if (!isNonEmptyString(filePath)) { + throw new Error("'filePath' must be a non-empty string"); + } + const { cliEngine } = privateMembersMap.get(this); + + return cliEngine.isPathIgnored(filePath); + } +} + +//------------------------------------------------------------------------------ +// Public Interface +//------------------------------------------------------------------------------ + +module.exports = { + ESLint, + + /** + * Get the private class members of a given ESLint instance for tests. + * @param {ESLint} instance The ESLint instance to get. + * @returns {ESLintPrivateMembers} The instance's private class members. + */ + getESLintPrivateMembers(instance) { + return privateMembersMap.get(instance); + } +}; diff --git a/eslint/lib/eslint/index.js b/eslint/lib/eslint/index.js new file mode 100644 index 0000000..c9185ee --- /dev/null +++ b/eslint/lib/eslint/index.js @@ -0,0 +1,7 @@ +"use strict"; + +const { ESLint } = require("./eslint"); + +module.exports = { + ESLint +}; diff --git a/eslint/lib/init/autoconfig.js b/eslint/lib/init/autoconfig.js index 64be3d2..2b0aa12 100644 --- a/eslint/lib/init/autoconfig.js +++ b/eslint/lib/init/autoconfig.js @@ -301,7 +301,7 @@ class Registry { ruleSetIdx += 1; if (cb) { - cb(totalFilesLinting); // eslint-disable-line callback-return + cb(totalFilesLinting); // eslint-disable-line node/callback-return } }); @@ -316,10 +316,10 @@ class Registry { /** * Extract rule configuration into eslint:recommended where possible. * - * This will return a new config with `"extends": "eslint:recommended"` and + * This will return a new config with `["extends": [ ..., "eslint:recommended"]` and * only the rules which have configurations different from the recommended config. * @param {Object} config config object - * @returns {Object} config object using `"extends": "eslint:recommended"` + * @returns {Object} config object using `"extends": ["eslint:recommended"]` */ function extendFromRecommended(config) { const newConfig = Object.assign({}, config); @@ -333,7 +333,7 @@ function extendFromRecommended(config) { delete newConfig.rules[ruleId]; } }); - newConfig.extends = RECOMMENDED_CONFIG_NAME; + newConfig.extends.unshift(RECOMMENDED_CONFIG_NAME); return newConfig; } diff --git a/eslint/lib/init/config-initializer.js b/eslint/lib/init/config-initializer.js index 28dfad1..70f0a25 100644 --- a/eslint/lib/init/config-initializer.js +++ b/eslint/lib/init/config-initializer.js @@ -15,6 +15,7 @@ const util = require("util"), inquirer = require("inquirer"), ProgressBar = require("progress"), semver = require("semver"), + espree = require("espree"), recConfig = require("../../conf/eslint-recommended"), ConfigOps = require("../shared/config-ops"), log = require("../shared/logging"), @@ -31,8 +32,6 @@ const debug = require("debug")("eslint:config-initializer"); // Private //------------------------------------------------------------------------------ -const DEFAULT_ECMA_VERSION = 2018; - /* istanbul ignore next: hard to test fs function */ /** * Create .eslintrc file in the current working directory @@ -265,8 +264,7 @@ function processAnswers(answers) { extends: [] }; - // set the latest ECMAScript version - config.parserOptions.ecmaVersion = DEFAULT_ECMA_VERSION; + config.parserOptions.ecmaVersion = espree.latestEcmaVersion; config.env.es6 = true; config.globals = { Atomics: "readonly", diff --git a/eslint/lib/init/source-code-utils.js b/eslint/lib/init/source-code-utils.js index dfc170a..dca6541 100644 --- a/eslint/lib/init/source-code-utils.js +++ b/eslint/lib/init/source-code-utils.js @@ -23,7 +23,7 @@ const { CLIEngine } = require("../cli-engine"); * TODO1: Expose the API that enumerates target files. * TODO2: Extract the creation logic of `SourceCode` from `Linter` class. */ -const { getCLIEngineInternalSlots } = require("../cli-engine/cli-engine"); // eslint-disable-line no-restricted-modules +const { getCLIEngineInternalSlots } = require("../cli-engine/cli-engine"); // eslint-disable-line node/no-restricted-require const debug = require("debug")("eslint:source-code-utils"); @@ -97,7 +97,7 @@ function getSourceCodeOfFiles(patterns, options, callback) { sourceCodes[filename] = sourceCode; } if (callback) { - callback(filenames.length); // eslint-disable-line callback-return + callback(filenames.length); // eslint-disable-line node/callback-return } }); diff --git a/eslint/lib/options.js b/eslint/lib/options.js index 98dc04b..1681f1d 100644 --- a/eslint/lib/options.js +++ b/eslint/lib/options.js @@ -46,7 +46,6 @@ module.exports = optionator({ { option: "ext", type: "[String]", - default: ".js", description: "Specify JavaScript file extensions" }, { diff --git a/eslint/lib/rule-tester/rule-tester.js b/eslint/lib/rule-tester/rule-tester.js index 1c17371..77df1de 100644 --- a/eslint/lib/rule-tester/rule-tester.js +++ b/eslint/lib/rule-tester/rule-tester.js @@ -563,7 +563,12 @@ class RuleTester { output = SourceCodeFixer.applyFixes(code, messages).output; const errorMessageInFix = linter.verify(output, config, filename).find(m => m.fatal); - assert(!errorMessageInFix, `A fatal parsing error occurred in autofix: ${errorMessageInFix && errorMessageInFix.message}`); + assert(!errorMessageInFix, [ + "A fatal parsing error occurred in autofix.", + `Error: ${errorMessageInFix && errorMessageInFix.message}`, + "Autofix output:", + output + ].join("\n")); } else { output = code; } diff --git a/eslint/lib/rules/array-callback-return.js b/eslint/lib/rules/array-callback-return.js index eb38965..62ba7b7 100644 --- a/eslint/lib/rules/array-callback-return.js +++ b/eslint/lib/rules/array-callback-return.js @@ -29,22 +29,6 @@ function isReachable(segment) { return segment.reachable; } -/** - * Gets a readable location. - * - * - FunctionExpression -> the function name or `function` keyword. - * - ArrowFunctionExpression -> `=>` token. - * @param {ASTNode} node A function node to get. - * @param {SourceCode} sourceCode A source code to get tokens. - * @returns {ASTNode|Token} The node or the token of a location. - */ -function getLocation(node, sourceCode) { - if (node.type === "ArrowFunctionExpression") { - return sourceCode.getTokenBefore(node.body); - } - return node.id || node; -} - /** * Checks a given node is a MemberExpression node which has the specified name's * property. @@ -179,6 +163,7 @@ module.exports = { create(context) { const options = context.options[0] || { allowImplicit: false, checkForEach: false }; + const sourceCode = context.getSourceCode(); let funcInfo = { arrayMethodName: null, @@ -217,12 +202,12 @@ module.exports = { } if (messageId) { - let name = astUtils.getFunctionNameWithKind(funcInfo.node); + let name = astUtils.getFunctionNameWithKind(node); name = messageId === "expectedNoReturnValue" ? lodash.upperFirst(name) : name; context.report({ node, - loc: getLocation(node, context.getSourceCode()).loc.start, + loc: astUtils.getFunctionHeadLoc(node, sourceCode), messageId, data: { name } }); diff --git a/eslint/lib/rules/callback-return.js b/eslint/lib/rules/callback-return.js index c5263cd..5df792d 100644 --- a/eslint/lib/rules/callback-return.js +++ b/eslint/lib/rules/callback-return.js @@ -10,6 +10,10 @@ module.exports = { meta: { + deprecated: true, + + replacedBy: ["node/callback-return"], + type: "suggestion", docs: { diff --git a/eslint/lib/rules/comma-style.js b/eslint/lib/rules/comma-style.js index bc22f05..f1a23d6 100644 --- a/eslint/lib/rules/comma-style.js +++ b/eslint/lib/rules/comma-style.js @@ -146,10 +146,7 @@ module.exports = { // lone comma context.report({ node: reportItem, - loc: { - line: commaToken.loc.end.line, - column: commaToken.loc.start.column - }, + loc: commaToken.loc, messageId: "unexpectedLineBeforeAndAfterComma", fix: getFixerFunction(styleType, previousItemToken, commaToken, currentItemToken) }); @@ -158,6 +155,7 @@ module.exports = { context.report({ node: reportItem, + loc: commaToken.loc, messageId: "expectedCommaFirst", fix: getFixerFunction(style, previousItemToken, commaToken, currentItemToken) }); @@ -166,10 +164,7 @@ module.exports = { context.report({ node: reportItem, - loc: { - line: commaToken.loc.end.line, - column: commaToken.loc.end.column - }, + loc: commaToken.loc, messageId: "expectedCommaLast", fix: getFixerFunction(style, previousItemToken, commaToken, currentItemToken) }); diff --git a/eslint/lib/rules/func-call-spacing.js b/eslint/lib/rules/func-call-spacing.js index e2edd42..dccdd0a 100644 --- a/eslint/lib/rules/func-call-spacing.js +++ b/eslint/lib/rules/func-call-spacing.js @@ -63,7 +63,8 @@ module.exports = { }, messages: { - unexpected: "Unexpected newline between function name and paren.", + unexpectedWhitespace: "Unexpected whitespace between function name and paren.", + unexpectedNewline: "Unexpected newline between function name and paren.", missing: "Missing space between function name and paren." } }, @@ -116,7 +117,7 @@ module.exports = { context.report({ node, loc: leftToken.loc.start, - messageId: "unexpected", + messageId: "unexpectedWhitespace", fix(fixer) { /* @@ -143,7 +144,7 @@ module.exports = { context.report({ node, loc: leftToken.loc.start, - messageId: "unexpected", + messageId: "unexpectedNewline", fix(fixer) { return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " "); } diff --git a/eslint/lib/rules/getter-return.js b/eslint/lib/rules/getter-return.js index e1468a5..c54ebfb 100644 --- a/eslint/lib/rules/getter-return.js +++ b/eslint/lib/rules/getter-return.js @@ -25,17 +25,6 @@ function isReachable(segment) { return segment.reachable; } -/** - * Gets a readable location. - * - * - FunctionExpression -> the function name or `function` keyword. - * @param {ASTNode} node A function node to get. - * @returns {ASTNode|Token} The node or the token of a location. - */ -function getId(node) { - return node.id || node; -} - //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -75,6 +64,7 @@ module.exports = { create(context) { const options = context.options[0] || { allowImplicit: false }; + const sourceCode = context.getSourceCode(); let funcInfo = { upper: null, @@ -99,7 +89,7 @@ module.exports = { ) { context.report({ node, - loc: getId(node).loc.start, + loc: astUtils.getFunctionHeadLoc(node, sourceCode), messageId: funcInfo.hasReturn ? "expectedAlways" : "expected", data: { name: astUtils.getFunctionNameWithKind(funcInfo.node) diff --git a/eslint/lib/rules/global-require.js b/eslint/lib/rules/global-require.js index 4af3a6a..9bd073b 100644 --- a/eslint/lib/rules/global-require.js +++ b/eslint/lib/rules/global-require.js @@ -48,6 +48,10 @@ function isShadowed(scope, node) { module.exports = { meta: { + deprecated: true, + + replacedBy: ["node/global-require"], + type: "suggestion", docs: { diff --git a/eslint/lib/rules/handle-callback-err.js b/eslint/lib/rules/handle-callback-err.js index 6409466..8ad63bb 100644 --- a/eslint/lib/rules/handle-callback-err.js +++ b/eslint/lib/rules/handle-callback-err.js @@ -11,6 +11,10 @@ module.exports = { meta: { + deprecated: true, + + replacedBy: ["node/handle-callback-err"], + type: "suggestion", docs: { diff --git a/eslint/lib/rules/key-spacing.js b/eslint/lib/rules/key-spacing.js index c405043..57abb00 100644 --- a/eslint/lib/rules/key-spacing.js +++ b/eslint/lib/rules/key-spacing.js @@ -45,7 +45,7 @@ function isSingleLine(node) { /** * Checks whether the properties on a single line. * @param {ASTNode[]} properties List of Property AST nodes. - * @returns {boolean} True if all properies is on a single line. + * @returns {boolean} True if all properties is on a single line. */ function isSingleLineProperties(properties) { const [firstProp] = properties, diff --git a/eslint/lib/rules/new-cap.js b/eslint/lib/rules/new-cap.js index 7cce968..0faf45e 100644 --- a/eslint/lib/rules/new-cap.js +++ b/eslint/lib/rules/new-cap.js @@ -235,7 +235,7 @@ module.exports = { callee = callee.property; } - context.report({ node, loc: callee.loc.start, messageId }); + context.report({ node, loc: callee.loc, messageId }); } //-------------------------------------------------------------------------- diff --git a/eslint/lib/rules/newline-per-chained-call.js b/eslint/lib/rules/newline-per-chained-call.js index 8ad8838..4254fec 100644 --- a/eslint/lib/rules/newline-per-chained-call.js +++ b/eslint/lib/rules/newline-per-chained-call.js @@ -90,16 +90,19 @@ module.exports = { } if (depth > ignoreChainWithDepth && astUtils.isTokenOnSameLine(callee.object, callee.property)) { + const firstTokenAfterObject = sourceCode.getTokenAfter(callee.object, astUtils.isNotClosingParenToken); + context.report({ node: callee.property, - loc: callee.property.loc.start, + loc: { + start: firstTokenAfterObject.loc.start, + end: callee.loc.end + }, messageId: "expected", data: { callee: getPropertyText(callee) }, fix(fixer) { - const firstTokenAfterObject = sourceCode.getTokenAfter(callee.object, astUtils.isNotClosingParenToken); - return fixer.insertTextBefore(firstTokenAfterObject, "\n"); } }); diff --git a/eslint/lib/rules/no-buffer-constructor.js b/eslint/lib/rules/no-buffer-constructor.js index bf4c889..5dce047 100644 --- a/eslint/lib/rules/no-buffer-constructor.js +++ b/eslint/lib/rules/no-buffer-constructor.js @@ -10,6 +10,10 @@ module.exports = { meta: { + deprecated: true, + + replacedBy: ["node/no-deprecated-api"], + type: "problem", docs: { diff --git a/eslint/lib/rules/no-empty-function.js b/eslint/lib/rules/no-empty-function.js index c743211..c512f8c 100644 --- a/eslint/lib/rules/no-empty-function.js +++ b/eslint/lib/rules/no-empty-function.js @@ -151,7 +151,7 @@ module.exports = { ) { context.report({ node, - loc: node.body.loc.start, + loc: node.body.loc, messageId: "unexpected", data: { name } }); diff --git a/eslint/lib/rules/no-extra-parens.js b/eslint/lib/rules/no-extra-parens.js index a3dd5ba..7cbb752 100644 --- a/eslint/lib/rules/no-extra-parens.js +++ b/eslint/lib/rules/no-extra-parens.js @@ -560,7 +560,11 @@ module.exports = { tokensToIgnore.add(secondToken); } - if (hasExcessParens(node)) { + const hasExtraParens = node.parent.type === "ExportDefaultDeclaration" + ? hasExcessParensWithPrecedence(node, PRECEDENCE_OF_ASSIGNMENT_EXPR) + : hasExcessParens(node); + + if (hasExtraParens) { report(node); } } diff --git a/eslint/lib/rules/no-inner-declarations.js b/eslint/lib/rules/no-inner-declarations.js index e1c29e0..0768bc6 100644 --- a/eslint/lib/rules/no-inner-declarations.js +++ b/eslint/lib/rules/no-inner-declarations.js @@ -5,10 +5,19 @@ "use strict"; +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); + //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ +const validParent = new Set(["Program", "ExportNamedDeclaration", "ExportDefaultDeclaration"]); +const validBlockStatementParent = new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]); + module.exports = { meta: { type: "problem", @@ -33,54 +42,37 @@ module.exports = { create(context) { - /** - * Find the nearest Program or Function ancestor node. - * @returns {Object} Ancestor's type and distance from node. - */ - function nearestBody() { - const ancestors = context.getAncestors(); - let ancestor = ancestors.pop(), - generation = 1; - - while (ancestor && ["Program", "FunctionDeclaration", - "FunctionExpression", "ArrowFunctionExpression" - ].indexOf(ancestor.type) < 0) { - generation += 1; - ancestor = ancestors.pop(); - } - - return { - - // Type of containing ancestor - type: ancestor.type, - - // Separation between ancestor and node - distance: generation - }; - } - /** * Ensure that a given node is at a program or function body's root. * @param {ASTNode} node Declaration node to check. * @returns {void} */ function check(node) { - const body = nearestBody(), - valid = ((body.type === "Program" && body.distance === 1) || - body.distance === 2); - - if (!valid) { - context.report({ - node, - messageId: "moveDeclToRoot", - data: { - type: (node.type === "FunctionDeclaration" ? "function" : "variable"), - body: (body.type === "Program" ? "program" : "function body") - } - }); + const parent = node.parent; + + if ( + parent.type === "BlockStatement" && validBlockStatementParent.has(parent.parent.type) + ) { + return; + } + + if (validParent.has(parent.type)) { + return; } + + const upperFunction = astUtils.getUpperFunction(parent); + + context.report({ + node, + messageId: "moveDeclToRoot", + data: { + type: (node.type === "FunctionDeclaration" ? "function" : "variable"), + body: (upperFunction === null ? "program" : "function body") + } + }); } + return { FunctionDeclaration: check, diff --git a/eslint/lib/rules/no-lone-blocks.js b/eslint/lib/rules/no-lone-blocks.js index d706988..290784b 100644 --- a/eslint/lib/rules/no-lone-blocks.js +++ b/eslint/lib/rules/no-lone-blocks.js @@ -49,7 +49,7 @@ module.exports = { } /** - * Checks for any ocurrence of a BlockStatement in a place where lists of statements can appear + * Checks for any occurrence of a BlockStatement in a place where lists of statements can appear * @param {ASTNode} node The node to check * @returns {boolean} True if the node is a lone block. */ diff --git a/eslint/lib/rules/no-mixed-requires.js b/eslint/lib/rules/no-mixed-requires.js index 8e988e3..bfe9b7a 100644 --- a/eslint/lib/rules/no-mixed-requires.js +++ b/eslint/lib/rules/no-mixed-requires.js @@ -11,6 +11,10 @@ module.exports = { meta: { + deprecated: true, + + replacedBy: ["node/no-mixed-requires"], + type: "suggestion", docs: { diff --git a/eslint/lib/rules/no-new-object.js b/eslint/lib/rules/no-new-object.js index f3e99c9..e9f915d 100644 --- a/eslint/lib/rules/no-new-object.js +++ b/eslint/lib/rules/no-new-object.js @@ -5,6 +5,12 @@ "use strict"; +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); + //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -28,10 +34,17 @@ module.exports = { }, create(context) { - return { - NewExpression(node) { + const variable = astUtils.getVariableByName( + context.getScope(), + node.callee.name + ); + + if (variable && variable.identifiers.length > 0) { + return; + } + if (node.callee.name === "Object") { context.report({ node, @@ -40,6 +53,5 @@ module.exports = { } } }; - } }; diff --git a/eslint/lib/rules/no-new-require.js b/eslint/lib/rules/no-new-require.js index df12a42..7f81e83 100644 --- a/eslint/lib/rules/no-new-require.js +++ b/eslint/lib/rules/no-new-require.js @@ -11,6 +11,10 @@ module.exports = { meta: { + deprecated: true, + + replacedBy: ["node/no-new-require"], + type: "suggestion", docs: { diff --git a/eslint/lib/rules/no-path-concat.js b/eslint/lib/rules/no-path-concat.js index 9fa8b85..77a03a7 100644 --- a/eslint/lib/rules/no-path-concat.js +++ b/eslint/lib/rules/no-path-concat.js @@ -10,6 +10,10 @@ module.exports = { meta: { + deprecated: true, + + replacedBy: ["node/no-path-concat"], + type: "suggestion", docs: { diff --git a/eslint/lib/rules/no-process-env.js b/eslint/lib/rules/no-process-env.js index 0f8d7f8..24bb9f9 100644 --- a/eslint/lib/rules/no-process-env.js +++ b/eslint/lib/rules/no-process-env.js @@ -10,6 +10,10 @@ module.exports = { meta: { + deprecated: true, + + replacedBy: ["node/no-process-env"], + type: "suggestion", docs: { diff --git a/eslint/lib/rules/no-process-exit.js b/eslint/lib/rules/no-process-exit.js index 2987166..9c70ea8 100644 --- a/eslint/lib/rules/no-process-exit.js +++ b/eslint/lib/rules/no-process-exit.js @@ -10,6 +10,10 @@ module.exports = { meta: { + deprecated: true, + + replacedBy: ["node/no-process-exit"], + type: "suggestion", docs: { diff --git a/eslint/lib/rules/no-restricted-modules.js b/eslint/lib/rules/no-restricted-modules.js index abd8d5c..61834ce 100644 --- a/eslint/lib/rules/no-restricted-modules.js +++ b/eslint/lib/rules/no-restricted-modules.js @@ -40,6 +40,10 @@ const arrayOfStringsOrObjects = { module.exports = { meta: { + deprecated: true, + + replacedBy: ["node/no-restricted-require"], + type: "suggestion", docs: { diff --git a/eslint/lib/rules/no-sync.js b/eslint/lib/rules/no-sync.js index d811105..9790d1f 100644 --- a/eslint/lib/rules/no-sync.js +++ b/eslint/lib/rules/no-sync.js @@ -13,6 +13,10 @@ module.exports = { meta: { + deprecated: true, + + replacedBy: ["node/no-sync"], + type: "suggestion", docs: { diff --git a/eslint/lib/rules/no-unexpected-multiline.js b/eslint/lib/rules/no-unexpected-multiline.js index eb72008..b5ec20d 100644 --- a/eslint/lib/rules/no-unexpected-multiline.js +++ b/eslint/lib/rules/no-unexpected-multiline.js @@ -53,7 +53,11 @@ module.exports = { const nodeExpressionEnd = sourceCode.getTokenBefore(openParen); if (openParen.loc.start.line !== nodeExpressionEnd.loc.end.line) { - context.report({ node, loc: openParen.loc.start, messageId, data: { char: openParen.value } }); + context.report({ + node, + loc: openParen.loc, + messageId + }); } } @@ -71,18 +75,24 @@ module.exports = { }, TaggedTemplateExpression(node) { - if (node.tag.loc.end.line === node.quasi.loc.start.line) { - return; - } - - // handle generics type parameters on template tags - const tokenBefore = sourceCode.getTokenBefore(node.quasi); - - if (tokenBefore.loc.end.line === node.quasi.loc.start.line) { - return; + const { quasi } = node; + + // handles common tags, parenthesized tags, and typescript's generic type arguments + const tokenBefore = sourceCode.getTokenBefore(quasi); + + if (tokenBefore.loc.end.line !== quasi.loc.start.line) { + context.report({ + node, + loc: { + start: quasi.loc.start, + end: { + line: quasi.loc.start.line, + column: quasi.loc.start.column + 1 + } + }, + messageId: "taggedTemplate" + }); } - - context.report({ node, loc: node.loc.start, messageId: "taggedTemplate" }); }, CallExpression(node) { diff --git a/eslint/lib/rules/no-useless-concat.js b/eslint/lib/rules/no-useless-concat.js index aa46742..cfc60c8 100644 --- a/eslint/lib/rules/no-useless-concat.js +++ b/eslint/lib/rules/no-useless-concat.js @@ -105,7 +105,7 @@ module.exports = { context.report({ node, - loc: operatorToken.loc.start, + loc: operatorToken.loc, messageId: "unexpectedConcat" }); } diff --git a/eslint/lib/rules/space-before-function-paren.js b/eslint/lib/rules/space-before-function-paren.js index af609c2..1021a11 100644 --- a/eslint/lib/rules/space-before-function-paren.js +++ b/eslint/lib/rules/space-before-function-paren.js @@ -127,7 +127,10 @@ module.exports = { if (hasSpacing && functionConfig === "never") { context.report({ node, - loc: leftToken.loc.end, + loc: { + start: leftToken.loc.end, + end: rightToken.loc.start + }, messageId: "unexpectedSpace", fix(fixer) { const comments = sourceCode.getCommentsBefore(rightToken); @@ -145,7 +148,7 @@ module.exports = { } else if (!hasSpacing && functionConfig === "always") { context.report({ node, - loc: leftToken.loc.end, + loc: rightToken.loc, messageId: "missingSpace", fix: fixer => fixer.insertTextAfter(leftToken, " ") }); diff --git a/eslint/lib/rules/yoda.js b/eslint/lib/rules/yoda.js index c4ff3f8..f1159e5 100644 --- a/eslint/lib/rules/yoda.js +++ b/eslint/lib/rules/yoda.js @@ -20,7 +20,7 @@ const astUtils = require("./utils/ast-utils"); * @returns {boolean} Whether or not it is a comparison operator. */ function isComparisonOperator(operator) { - return (/^(==|===|!=|!==|<|>|<=|>=)$/u).test(operator); + return /^(==|===|!=|!==|<|>|<=|>=)$/u.test(operator); } /** @@ -29,7 +29,7 @@ function isComparisonOperator(operator) { * @returns {boolean} Whether or not it is an equality operator. */ function isEqualityOperator(operator) { - return (/^(==|===)$/u).test(operator); + return /^(==|===)$/u.test(operator); } /** @@ -50,10 +50,12 @@ function isRangeTestOperator(operator) { * real literal and should be treated as such. */ function isNegativeNumericLiteral(node) { - return (node.type === "UnaryExpression" && + return ( + node.type === "UnaryExpression" && node.operator === "-" && node.prefix && - astUtils.isNumericLiteral(node.argument)); + astUtils.isNumericLiteral(node.argument) + ); } /** @@ -71,25 +73,21 @@ function isStaticTemplateLiteral(node) { * @returns {boolean} True if the node should be treated as a single Literal node. */ function looksLikeLiteral(node) { - return isNegativeNumericLiteral(node) || - isStaticTemplateLiteral(node); + return isNegativeNumericLiteral(node) || isStaticTemplateLiteral(node); } /** * Attempts to derive a Literal node from nodes that are treated like literals. * @param {ASTNode} node Node to normalize. - * @param {number} [defaultValue] The default value to be returned if the node - * is not a Literal. * @returns {ASTNode} One of the following options. * 1. The original node if the node is already a Literal * 2. A normalized Literal node with the negative number as the value if the * node represents a negative number literal. * 3. A normalized Literal node with the string as the value if the node is * a Template Literal without expression. - * 4. The Literal node which has the `defaultValue` argument if it exists. - * 5. Otherwise `null`. + * 4. Otherwise `null`. */ -function getNormalizedLiteral(node, defaultValue) { +function getNormalizedLiteral(node) { if (node.type === "Literal") { return node; } @@ -110,14 +108,6 @@ function getNormalizedLiteral(node, defaultValue) { }; } - if (defaultValue) { - return { - type: "Literal", - value: defaultValue, - raw: String(defaultValue) - }; - } - return null; } @@ -183,7 +173,7 @@ module.exports = { type: "suggestion", docs: { - description: "require or disallow \"Yoda\" conditions", + description: 'require or disallow "Yoda" conditions', category: "Best Practices", recommended: false, url: "https://eslint.org/docs/rules/yoda" @@ -211,16 +201,19 @@ module.exports = { fixable: "code", messages: { - expected: "Expected literal to be on the {{expectedSide}} side of {{operator}}." + expected: + "Expected literal to be on the {{expectedSide}} side of {{operator}}." } }, create(context) { // Default to "never" (!always) if no option - const always = (context.options[0] === "always"); - const exceptRange = (context.options[1] && context.options[1].exceptRange); - const onlyEquality = (context.options[1] && context.options[1].onlyEquality); + const always = context.options[0] === "always"; + const exceptRange = + context.options[1] && context.options[1].exceptRange; + const onlyEquality = + context.options[1] && context.options[1].onlyEquality; const sourceCode = context.getSourceCode(); @@ -243,13 +236,23 @@ module.exports = { * @returns {boolean} Whether node is a "between" range test. */ function isBetweenTest() { - let leftLiteral, rightLiteral; + if (node.operator === "&&" && same(left.right, right.left)) { + const leftLiteral = getNormalizedLiteral(left.left); + const rightLiteral = getNormalizedLiteral(right.right); + + if (leftLiteral === null && rightLiteral === null) { + return false; + } - return (node.operator === "&&" && - (leftLiteral = getNormalizedLiteral(left.left)) && - (rightLiteral = getNormalizedLiteral(right.right, Number.POSITIVE_INFINITY)) && - leftLiteral.value <= rightLiteral.value && - same(left.right, right.left)); + if (rightLiteral === null || leftLiteral === null) { + return true; + } + + if (leftLiteral.value <= rightLiteral.value) { + return true; + } + } + return false; } /** @@ -257,13 +260,24 @@ module.exports = { * @returns {boolean} Whether node is an "outside" range test. */ function isOutsideTest() { - let leftLiteral, rightLiteral; + if (node.operator === "||" && same(left.left, right.right)) { + const leftLiteral = getNormalizedLiteral(left.right); + const rightLiteral = getNormalizedLiteral(right.left); + + if (leftLiteral === null && rightLiteral === null) { + return false; + } + + if (rightLiteral === null || leftLiteral === null) { + return true; + } + + if (leftLiteral.value <= rightLiteral.value) { + return true; + } + } - return (node.operator === "||" && - (leftLiteral = getNormalizedLiteral(left.right, Number.NEGATIVE_INFINITY)) && - (rightLiteral = getNormalizedLiteral(right.left)) && - leftLiteral.value <= rightLiteral.value && - same(left.left, right.right)); + return false; } /** @@ -276,13 +290,15 @@ module.exports = { return astUtils.isParenthesised(sourceCode, node); } - return (node.type === "LogicalExpression" && + return ( + node.type === "LogicalExpression" && left.type === "BinaryExpression" && right.type === "BinaryExpression" && isRangeTestOperator(left.operator) && isRangeTestOperator(right.operator) && (isBetweenTest() || isOutsideTest()) && - isParenWrapped()); + isParenWrapped() + ); } const OPERATOR_FLIP_MAP = { @@ -303,21 +319,52 @@ module.exports = { */ function getFlippedString(node) { const tokenBefore = sourceCode.getTokenBefore(node); - const operatorToken = sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator); - const textBeforeOperator = sourceCode.getText().slice(sourceCode.getTokenBefore(operatorToken).range[1], operatorToken.range[0]); - const textAfterOperator = sourceCode.getText().slice(operatorToken.range[1], sourceCode.getTokenAfter(operatorToken).range[0]); - const leftText = sourceCode.getText().slice(node.range[0], sourceCode.getTokenBefore(operatorToken).range[1]); + const operatorToken = sourceCode.getFirstTokenBetween( + node.left, + node.right, + token => token.value === node.operator + ); + const textBeforeOperator = sourceCode + .getText() + .slice( + sourceCode.getTokenBefore(operatorToken).range[1], + operatorToken.range[0] + ); + const textAfterOperator = sourceCode + .getText() + .slice( + operatorToken.range[1], + sourceCode.getTokenAfter(operatorToken).range[0] + ); + const leftText = sourceCode + .getText() + .slice( + node.range[0], + sourceCode.getTokenBefore(operatorToken).range[1] + ); const firstRightToken = sourceCode.getTokenAfter(operatorToken); - const rightText = sourceCode.getText().slice(firstRightToken.range[0], node.range[1]); + const rightText = sourceCode + .getText() + .slice(firstRightToken.range[0], node.range[1]); let prefix = ""; - if (tokenBefore && tokenBefore.range[1] === node.range[0] && - !astUtils.canTokensBeAdjacent(tokenBefore, firstRightToken)) { + if ( + tokenBefore && + tokenBefore.range[1] === node.range[0] && + !astUtils.canTokensBeAdjacent(tokenBefore, firstRightToken) + ) { prefix = " "; } - return prefix + rightText + textBeforeOperator + OPERATOR_FLIP_MAP[operatorToken.value] + textAfterOperator + leftText; + return ( + prefix + + rightText + + textBeforeOperator + + OPERATOR_FLIP_MAP[operatorToken.value] + + textAfterOperator + + leftText + ); } //-------------------------------------------------------------------------- @@ -331,8 +378,12 @@ module.exports = { // If `expectedLiteral` is not a literal, and `expectedNonLiteral` is a literal, raise an error. if ( - (expectedNonLiteral.type === "Literal" || looksLikeLiteral(expectedNonLiteral)) && - !(expectedLiteral.type === "Literal" || looksLikeLiteral(expectedLiteral)) && + (expectedNonLiteral.type === "Literal" || + looksLikeLiteral(expectedNonLiteral)) && + !( + expectedLiteral.type === "Literal" || + looksLikeLiteral(expectedLiteral) + ) && !(!isEqualityOperator(node.operator) && onlyEquality) && isComparisonOperator(node.operator) && !(exceptRange && isRangeTest(context.getAncestors().pop())) @@ -344,12 +395,11 @@ module.exports = { operator: node.operator, expectedSide: always ? "left" : "right" }, - fix: fixer => fixer.replaceText(node, getFlippedString(node)) + fix: fixer => + fixer.replaceText(node, getFlippedString(node)) }); } - } }; - } }; diff --git a/eslint/lib/shared/relative-module-resolver.js b/eslint/lib/shared/relative-module-resolver.js index fa6cca7..80335c5 100644 --- a/eslint/lib/shared/relative-module-resolver.js +++ b/eslint/lib/shared/relative-module-resolver.js @@ -11,6 +11,7 @@ const Module = require("module"); * `Module.createRequire` is added in v12.2.0. It supports URL as well. * We only support the case where the argument is a filepath, not a URL. */ +// eslint-disable-next-line node/no-unsupported-features/node-builtins, node/no-deprecated-api const createRequire = Module.createRequire || Module.createRequireFromPath; module.exports = { diff --git a/eslint/lib/shared/types.js b/eslint/lib/shared/types.js index bf37327..bbd95d1 100644 --- a/eslint/lib/shared/types.js +++ b/eslint/lib/shared/types.js @@ -141,3 +141,10 @@ module.exports = {}; * @property {Record} [processors] The definition of plugin processors. * @property {Record} [rules] The definition of plugin rules. */ + +/** + * Information of deprecated rules. + * @typedef {Object} DeprecatedRuleInfo + * @property {string} ruleId The rule ID. + * @property {string[]} replacedBy The rule IDs that replace this deprecated rule. + */ diff --git a/eslint/package.json b/eslint/package.json index ed15f61..b775ebd 100644 --- a/eslint/package.json +++ b/eslint/package.json @@ -1,6 +1,6 @@ { "name": "eslint", - "version": "7.0.0-alpha.3", + "version": "7.0.0", "author": "Nicholas C. Zakas ", "description": "An AST-based pattern checker for JavaScript.", "bin": { @@ -48,14 +48,14 @@ "dependencies": { "@babel/code-frame": "^7.0.0", "ajv": "^6.10.0", - "chalk": "^3.0.0", - "cross-spawn": "^7.0.1", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0", "eslint-visitor-keys": "^1.1.0", - "espree": "^6.2.1", + "espree": "^7.0.0", "esquery": "^1.2.0", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", @@ -69,16 +69,16 @@ "is-glob": "^4.0.0", "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", + "levn": "^0.4.1", "lodash": "^4.17.14", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", - "optionator": "^0.8.3", + "optionator": "^0.9.1", "progress": "^2.0.0", - "regexpp": "^3.0.0", - "semver": "^7.1.1", + "regexpp": "^3.1.0", + "semver": "^7.2.1", "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.0.1", + "strip-json-comments": "^3.1.0", "table": "^5.2.3", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" @@ -93,39 +93,40 @@ "common-tags": "^1.8.0", "core-js": "^3.1.3", "dateformat": "^3.0.3", - "ejs": "^2.6.1", + "ejs": "^3.0.2", + "escape-string-regexp": "^3.0.0", "eslint": "file:.", "eslint-config-eslint": "file:packages/eslint-config-eslint", "eslint-plugin-eslint-plugin": "^2.2.1", "eslint-plugin-internal-rules": "file:tools/internal-rules", - "eslint-plugin-jsdoc": "^15.9.5", - "eslint-plugin-node": "^9.0.0", - "eslint-release": "^1.2.0", + "eslint-plugin-jsdoc": "^22.1.0", + "eslint-plugin-node": "^11.1.0", + "eslint-release": "^2.0.0", "eslump": "^2.0.0", "esprima": "^4.0.1", - "glob": "^7.1.3", + "glob": "^7.1.6", "jsdoc": "^3.5.5", "karma": "^4.0.1", - "karma-chrome-launcher": "^2.2.0", + "karma-chrome-launcher": "^3.1.0", "karma-mocha": "^1.3.0", "karma-mocha-reporter": "^2.2.3", "karma-webpack": "^4.0.0-rc.6", "leche": "^2.2.3", - "lint-staged": "^8.1.5", + "lint-staged": "^10.1.2", "load-perf": "^0.2.0", - "markdownlint": "^0.15.0", - "markdownlint-cli": "^0.17.0", + "markdownlint": "^0.19.0", + "markdownlint-cli": "^0.22.0", "memfs": "^3.0.1", - "mocha": "^6.1.2", + "mocha": "^7.1.1", "mocha-junit-reporter": "^1.23.0", "npm-license": "^0.3.3", - "nyc": "^14.1.1", + "nyc": "^15.0.1", "proxyquire": "^2.0.1", - "puppeteer": "^1.18.0", - "recast": "^0.18.1", + "puppeteer": "^2.1.1", + "recast": "^0.19.0", "regenerator-runtime": "^0.13.2", "shelljs": "^0.8.2", - "sinon": "^7.3.2", + "sinon": "^9.0.1", "temp": "^0.9.0", "webpack": "^4.35.0", "webpack-cli": "^3.3.5", diff --git a/eslint/tests/lib/cli-engine/_utils.js b/eslint/tests/_utils/in-memory-fs.js similarity index 77% rename from eslint/tests/lib/cli-engine/_utils.js rename to eslint/tests/_utils/in-memory-fs.js index 1d1bcd2..5ef4f04 100644 --- a/eslint/tests/lib/cli-engine/_utils.js +++ b/eslint/tests/_utils/in-memory-fs.js @@ -2,12 +2,13 @@ * @fileoverview Define classes what use the in-memory file system. * * This provides utilities to test `ConfigArrayFactory`, - * `CascadingConfigArrayFactory`, `FileEnumerator`, and `CLIEngine`. + * `CascadingConfigArrayFactory`, `FileEnumerator`, `CLIEngine`, and `ESLint`. * * - `defineConfigArrayFactoryWithInMemoryFileSystem({ cwd, files })` * - `defineCascadingConfigArrayFactoryWithInMemoryFileSystem({ cwd, files })` * - `defineFileEnumeratorWithInMemoryFileSystem({ cwd, files })` * - `defineCLIEngineWithInMemoryFileSystem({ cwd, files })` + * - `defineESLintWithInMemoryFileSystem({ cwd, files })` * * Those functions define correspond classes with the in-memory file system. * Those search config files, parsers, and plugins in the `files` option via the @@ -55,23 +56,25 @@ const path = require("path"); const vm = require("vm"); +const { Volume, createFsFromVolume } = require("memfs"); const Proxyquire = require("proxyquire/lib/proxyquire"); -const { defineInMemoryFs } = require("../_utils"); const CascadingConfigArrayFactoryPath = - require.resolve("../../../lib/cli-engine/cascading-config-array-factory"); + require.resolve("../../lib/cli-engine/cascading-config-array-factory"); const CLIEnginePath = - require.resolve("../../../lib/cli-engine/cli-engine"); + require.resolve("../../lib/cli-engine/cli-engine"); const ConfigArrayFactoryPath = - require.resolve("../../../lib/cli-engine/config-array-factory"); + require.resolve("../../lib/cli-engine/config-array-factory"); const FileEnumeratorPath = - require.resolve("../../../lib/cli-engine/file-enumerator"); + require.resolve("../../lib/cli-engine/file-enumerator"); const LoadRulesPath = - require.resolve("../../../lib/cli-engine/load-rules"); + require.resolve("../../lib/cli-engine/load-rules"); +const ESLintPath = + require.resolve("../../lib/eslint/eslint"); const ESLintAllPath = - require.resolve("../../../conf/eslint-all"); + require.resolve("../../conf/eslint-all"); const ESLintRecommendedPath = - require.resolve("../../../conf/eslint-recommended"); + require.resolve("../../conf/eslint-recommended"); // Ensure the needed files has been loaded and cached. require(CascadingConfigArrayFactoryPath); @@ -79,6 +82,7 @@ require(CLIEnginePath); require(ConfigArrayFactoryPath); require(FileEnumeratorPath); require(LoadRulesPath); +require(ESLintPath); require("js-yaml"); require("espree"); @@ -236,12 +240,57 @@ function fsImportFresh(fs, stubs, absolutePath) { ); } +/** + * Define in-memory file system. + * @param {Object} options The options. + * @param {() => string} [options.cwd] The current working directory. + * @param {Object} [options.files] The initial files definition in the in-memory file system. + * @returns {import("fs")} The stubbed `ConfigArrayFactory` class. + */ +function defineInMemoryFs({ + cwd = process.cwd, + files = {} +} = {}) { + + /** + * The in-memory file system for this mock. + * @type {import("fs")} + */ + const fs = createFsFromVolume(new Volume()); + + fs.mkdirSync(cwd(), { recursive: true }); + + /* + * Write all files to the in-memory file system and compile all JavaScript + * files then set to `stubs`. + */ + (function initFiles(directoryPath, definition) { + for (const [filename, content] of Object.entries(definition)) { + const filePath = path.resolve(directoryPath, filename); + const parentPath = path.dirname(filePath); + + if (typeof content === "object") { + initFiles(filePath, content); + } else if (typeof content === "string") { + if (!fs.existsSync(parentPath)) { + fs.mkdirSync(parentPath, { recursive: true }); + } + fs.writeFileSync(filePath, content); + } else { + throw new Error(`Invalid content: ${typeof content}`); + } + } + }(cwd(), files)); + + return fs; +} + /** * Define stubbed `ConfigArrayFactory` class what uses the in-memory file system. * @param {Object} options The options. * @param {() => string} [options.cwd] The current working directory. * @param {Object} [options.files] The initial files definition in the in-memory file system. - * @returns {{ fs: import("fs"), RelativeModuleResolver: import("../../../lib/shared/relative-module-resolver"), ConfigArrayFactory: import("../../../lib/cli-engine/config-array-factory")["ConfigArrayFactory"] }} The stubbed `ConfigArrayFactory` class. + * @returns {{ fs: import("fs"), RelativeModuleResolver: import("../../lib/shared/relative-module-resolver"), ConfigArrayFactory: import("../../lib/cli-engine/config-array-factory")["ConfigArrayFactory"] }} The stubbed `ConfigArrayFactory` class. */ function defineConfigArrayFactoryWithInMemoryFileSystem({ cwd = process.cwd, @@ -438,9 +487,55 @@ function defineCLIEngineWithInMemoryFileSystem({ }; } +/** + * Define stubbed `ESLint` class that uses the in-memory file system. + * @param {Object} options The options. + * @param {() => string} [options.cwd] The current working directory. + * @param {Object} [options.files] The initial files definition in the in-memory file system. + * @returns {{ fs: import("fs"), RelativeModuleResolver: import("../../lib/shared/relative-module-resolver"), ConfigArrayFactory: import("../../lib/cli-engine/config-array-factory")["ConfigArrayFactory"], CascadingConfigArrayFactory: import("../../lib/cli-engine/cascading-config-array-factory")["CascadingConfigArrayFactory"], FileEnumerator: import("../../lib/cli-engine/file-enumerator")["FileEnumerator"], ESLint: import("../../lib/eslint/eslint")["ESLint"], getCLIEngineInternalSlots: import("../../lib//eslint/eslint")["getESLintInternalSlots"] }} The stubbed `ESLint` class. + */ +function defineESLintWithInMemoryFileSystem({ + cwd = process.cwd, + files = {} +} = {}) { + const { + fs, + RelativeModuleResolver, + ConfigArrayFactory, + CascadingConfigArrayFactory, + FileEnumerator, + CLIEngine, + getCLIEngineInternalSlots + } = defineCLIEngineWithInMemoryFileSystem({ cwd, files }); + const { ESLint, getESLintPrivateMembers } = proxyquire(ESLintPath, { + "../cli-engine/cli-engine": { CLIEngine, getCLIEngineInternalSlots } + }); + + // Override the default cwd. + return { + fs, + RelativeModuleResolver, + ConfigArrayFactory, + CascadingConfigArrayFactory, + FileEnumerator, + CLIEngine, + getCLIEngineInternalSlots, + ESLint: cwd === process.cwd + ? ESLint + : class extends ESLint { + constructor(options) { + super({ cwd: cwd(), ...options }); + } + }, + getESLintPrivateMembers + }; +} + module.exports = { + defineInMemoryFs, defineConfigArrayFactoryWithInMemoryFileSystem, defineCascadingConfigArrayFactoryWithInMemoryFileSystem, defineFileEnumeratorWithInMemoryFileSystem, - defineCLIEngineWithInMemoryFileSystem + defineCLIEngineWithInMemoryFileSystem, + defineESLintWithInMemoryFileSystem }; diff --git a/eslint/tests/_utils/index.js b/eslint/tests/_utils/index.js new file mode 100644 index 0000000..0431e3a --- /dev/null +++ b/eslint/tests/_utils/index.js @@ -0,0 +1,39 @@ +"use strict"; + +const { + defineInMemoryFs, + defineConfigArrayFactoryWithInMemoryFileSystem, + defineCascadingConfigArrayFactoryWithInMemoryFileSystem, + defineFileEnumeratorWithInMemoryFileSystem, + defineCLIEngineWithInMemoryFileSystem, + defineESLintWithInMemoryFileSystem +} = require("./in-memory-fs"); + + +/** + * Prevents leading spaces in a multiline template literal from appearing in the resulting string + * @param {string[]} strings The strings in the template literal + * @param {any[]} values The interpolation values in the template literal. + * @returns {string} The template literal, with spaces removed from all lines + */ +function unIndent(strings, ...values) { + const text = strings + .map((s, i) => (i === 0 ? s : values[i - 1] + s)) + .join(""); + const lines = text.replace(/^\n/u, "").replace(/\n\s*$/u, "").split("\n"); + const lineIndents = lines.filter(line => line.trim()).map(line => line.match(/ */u)[0].length); + const minLineIndent = Math.min(...lineIndents); + + return lines.map(line => line.slice(minLineIndent)).join("\n"); +} + + +module.exports = { + unIndent, + defineInMemoryFs, + defineConfigArrayFactoryWithInMemoryFileSystem, + defineCascadingConfigArrayFactoryWithInMemoryFileSystem, + defineFileEnumeratorWithInMemoryFileSystem, + defineCLIEngineWithInMemoryFileSystem, + defineESLintWithInMemoryFileSystem +}; diff --git a/eslint/tests/bench/large.js b/eslint/tests/bench/large.js index 24ba727..c94e885 100644 --- a/eslint/tests/bench/large.js +++ b/eslint/tests/bench/large.js @@ -52779,7 +52779,7 @@ var state = require("./state.js").state; var style = require("./style.js"); // We need this module here because environments such as IE and Rhino -// don't necessarilly expose the 'console' API and browserify uses +// don't necessarily expose the 'console' API and browserify uses // it to log things. It's a sad state of affair, really. var console = require("console-browserify"); @@ -52838,7 +52838,7 @@ var JSHINT = (function () { immed : true, // if immediate invocations must be wrapped in parens iterator : true, // if the `__iterator__` property should be allowed jquery : true, // if jQuery globals should be predefined - lastsemic : true, // if semicolons may be ommitted for the trailing + lastsemic : true, // if semicolons may be omitted for the trailing // statements inside of a one-line blocks. laxbreak : true, // if line breaks should not be checked laxcomma : true, // if line breaks should not be checked around commas @@ -59115,7 +59115,7 @@ Lexer.prototype = { }, /* - * Scan for any occurence of mixed tabs and spaces. If smarttabs option + * Scan for any occurrence of mixed tabs and spaces. If smarttabs option * is on, ignore tabs followed by spaces. * * Tabs followed by one space followed by a block comment are allowed. @@ -59135,7 +59135,7 @@ Lexer.prototype = { }, /* - * Scan for any occurence of non-breaking spaces. Non-breaking spaces + * Scan for any occurrence of non-breaking spaces. Non-breaking spaces * can be mistakenly typed on OS X with option-space. Non UTF-8 web * pages with non-breaking pages produce syntax errors. */ @@ -59288,7 +59288,7 @@ Lexer.prototype = { /* * Produce the next token. This function is called by advance() to get - * the next token. It retuns a token in a JSLint-compatible format. + * the next token. It returns a token in a JSLint-compatible format. */ token: function () { /*jshint loopfunc:true */ diff --git a/eslint/tests/bench/small.js b/eslint/tests/bench/small.js index b61cf5c..d2cdf35 100644 --- a/eslint/tests/bench/small.js +++ b/eslint/tests/bench/small.js @@ -1596,7 +1596,7 @@ ko.exportSymbol('computed', ko.dependentObservable); // Make "ko.computed" an al ko.exportSymbol('isComputed', ko.isComputed); (function() { - var maxNestedObservableDepth = 10; // Escape the (unlikely) pathalogical case where an observable's current value is itself (or similar reference cycle) + var maxNestedObservableDepth = 10; // Escape the (unlikely) pathological case where an observable's current value is itself (or similar reference cycle) ko.toJS = function(rootObject) { if (arguments.length == 0) diff --git a/eslint/tests/bin/eslint.js b/eslint/tests/bin/eslint.js index 8b771dc..cfc5128 100644 --- a/eslint/tests/bin/eslint.js +++ b/eslint/tests/bin/eslint.js @@ -8,7 +8,9 @@ const childProcess = require("child_process"); const fs = require("fs"); const assert = require("chai").assert; -const EXECUTABLE_PATH = require("path").resolve(`${__dirname}/../../bin/eslint.js`); +const path = require("path"); + +const EXECUTABLE_PATH = path.resolve(path.join(__dirname, "../../bin/eslint.js")); /** * Returns a Promise for when a child process exits @@ -90,7 +92,8 @@ describe("bin/eslint.js", () => { warningCount: 0, fixableErrorCount: 0, fixableWarningCount: 0, - output: "var foo = bar;\n" + output: "var foo = bar;\n", + usedDeprecatedRules: [] } ]); @@ -166,7 +169,7 @@ describe("bin/eslint.js", () => { it("successfully handles more than 4k data via stdin", () => { const child = runESLint(["--stdin", "--no-eslintrc"]); - const large = fs.createReadStream(`${__dirname}/../bench/large.js`, "utf8"); + const large = fs.createReadStream(path.join(__dirname, "../bench/large.js"), "utf8"); large.pipe(child.stdin); @@ -176,13 +179,13 @@ describe("bin/eslint.js", () => { describe("running on files", () => { it("has exit code 0 if no linting errors occur", () => assertExitCode(runESLint(["bin/eslint.js"]), 0)); - it("has exit code 0 if a linting warning is reported", () => assertExitCode(runESLint(["bin/eslint.js", "--env", "es6", "--no-eslintrc", "--rule", "semi: [1, never]"]), 0)); - it("has exit code 1 if a linting error is reported", () => assertExitCode(runESLint(["bin/eslint.js", "--env", "es6", "--no-eslintrc", "--rule", "semi: [2, never]"]), 1)); + it("has exit code 0 if a linting warning is reported", () => assertExitCode(runESLint(["bin/eslint.js", "--env", "es2020", "--no-eslintrc", "--rule", "semi: [1, never]"]), 0)); + it("has exit code 1 if a linting error is reported", () => assertExitCode(runESLint(["bin/eslint.js", "--env", "es2020", "--no-eslintrc", "--rule", "semi: [2, never]"]), 1)); it("has exit code 1 if a syntax error is thrown", () => assertExitCode(runESLint(["README.md"]), 1)); }); describe("automatically fixing files", () => { - const fixturesPath = `${__dirname}/../fixtures/autofix-integration`; + const fixturesPath = path.join(__dirname, "../fixtures/autofix-integration"); const tempFilePath = `${fixturesPath}/temp.js`; const startingText = fs.readFileSync(`${fixturesPath}/left-pad.js`).toString(); const expectedFixedText = fs.readFileSync(`${fixturesPath}/left-pad-expected.js`).toString(); @@ -359,7 +362,7 @@ describe("bin/eslint.js", () => { }); it("prints the error message pointing to line of code", () => { - const invalidConfig = `${__dirname}/../fixtures/bin/.eslintrc.yml`; + const invalidConfig = path.join(__dirname, "../fixtures/bin/.eslintrc.yml"); const child = runESLint(["--no-ignore", invalidConfig]); const exitCodeAssertion = assertExitCode(child, 2); const outputAssertion = getOutput(child).then(output => { diff --git a/eslint/tests/fixtures/fix-types/ignore-missing-meta.expected.js b/eslint/tests/fixtures/fix-types/ignore-missing-meta.expected.js index b6e3019..dc4cb72 100644 --- a/eslint/tests/fixtures/fix-types/ignore-missing-meta.expected.js +++ b/eslint/tests/fixtures/fix-types/ignore-missing-meta.expected.js @@ -1,5 +1,5 @@ /* eslint semi: "error" */ -/* eslint no-program: "error" */ +/* eslint test/no-program: "error" */ /* eslint prefer-arrow-callback: "error" */ "use strict"; diff --git a/eslint/tests/fixtures/fix-types/ignore-missing-meta.js b/eslint/tests/fixtures/fix-types/ignore-missing-meta.js index ee9b9d9..4e43de0 100644 --- a/eslint/tests/fixtures/fix-types/ignore-missing-meta.js +++ b/eslint/tests/fixtures/fix-types/ignore-missing-meta.js @@ -1,5 +1,5 @@ /* eslint semi: "error" */ -/* eslint no-program: "error" */ +/* eslint test/no-program: "error" */ /* eslint prefer-arrow-callback: "error" */ "use strict"; diff --git a/eslint/tests/lib/_utils.js b/eslint/tests/lib/_utils.js deleted file mode 100644 index 76e973c..0000000 --- a/eslint/tests/lib/_utils.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @fileoverview utils for rule tests. - * @author 唯然 - */ - -"use strict"; - -const path = require("path"); -const { Volume, createFsFromVolume } = require("memfs"); - -/** - * Prevents leading spaces in a multiline template literal from appearing in the resulting string - * @param {string[]} strings The strings in the template literal - * @param {any[]} values The interpolation values in the template literal. - * @returns {string} The template literal, with spaces removed from all lines - */ -function unIndent(strings, ...values) { - const text = strings - .map((s, i) => (i === 0 ? s : values[i - 1] + s)) - .join(""); - const lines = text.replace(/^\n/u, "").replace(/\n\s*$/u, "").split("\n"); - const lineIndents = lines.filter(line => line.trim()).map(line => line.match(/ */u)[0].length); - const minLineIndent = Math.min(...lineIndents); - - return lines.map(line => line.slice(minLineIndent)).join("\n"); -} - -/** - * Define in-memory file system. - * @param {Object} options The options. - * @param {() => string} [options.cwd] The current working directory. - * @param {Object} [options.files] The initial files definition in the in-memory file system. - * @returns {import("fs")} The stubbed `ConfigArrayFactory` class. - */ -function defineInMemoryFs({ - cwd = process.cwd, - files = {} -} = {}) { - - /** - * The in-memory file system for this mock. - * @type {import("fs")} - */ - const fs = createFsFromVolume(new Volume()); - - fs.mkdirSync(cwd(), { recursive: true }); - - /* - * Write all files to the in-memory file system and compile all JavaScript - * files then set to `stubs`. - */ - (function initFiles(directoryPath, definition) { - for (const [filename, content] of Object.entries(definition)) { - const filePath = path.resolve(directoryPath, filename); - const parentPath = path.dirname(filePath); - - if (typeof content === "object") { - initFiles(filePath, content); - } else if (typeof content === "string") { - if (!fs.existsSync(parentPath)) { - fs.mkdirSync(parentPath, { recursive: true }); - } - fs.writeFileSync(filePath, content); - } else { - throw new Error(`Invalid content: ${typeof content}`); - } - } - }(cwd(), files)); - - return fs; -} - -module.exports = { - defineInMemoryFs, - unIndent -}; diff --git a/eslint/tests/lib/cli-engine/cascading-config-array-factory.js b/eslint/tests/lib/cli-engine/cascading-config-array-factory.js index f981771..8471ac3 100644 --- a/eslint/tests/lib/cli-engine/cascading-config-array-factory.js +++ b/eslint/tests/lib/cli-engine/cascading-config-array-factory.js @@ -12,7 +12,7 @@ const sh = require("shelljs"); const sinon = require("sinon"); const { ConfigArrayFactory } = require("../../../lib/cli-engine/config-array-factory"); const { ExtractedConfig } = require("../../../lib/cli-engine/config-array/extracted-config"); -const { defineCascadingConfigArrayFactoryWithInMemoryFileSystem } = require("./_utils"); +const { defineCascadingConfigArrayFactoryWithInMemoryFileSystem } = require("../../_utils"); /** @typedef {InstanceType["CascadingConfigArrayFactory"]>} CascadingConfigArrayFactory */ /** @typedef {ReturnType} ConfigArray */ diff --git a/eslint/tests/lib/cli-engine/cli-engine.js b/eslint/tests/lib/cli-engine/cli-engine.js index 6880bf9..a8394b6 100644 --- a/eslint/tests/lib/cli-engine/cli-engine.js +++ b/eslint/tests/lib/cli-engine/cli-engine.js @@ -18,8 +18,7 @@ const assert = require("chai").assert, os = require("os"), hash = require("../../../lib/cli-engine/hash"), { CascadingConfigArrayFactory } = require("../../../lib/cli-engine/cascading-config-array-factory"), - { unIndent } = require("../_utils"), - { defineCLIEngineWithInMemoryFileSystem } = require("./_utils"); + { unIndent, defineCLIEngineWithInMemoryFileSystem } = require("../../_utils"); const proxyquire = require("proxyquire").noCallThru().noPreserveCache(); const fCache = require("file-entry-cache"); @@ -822,7 +821,9 @@ describe("CLIEngine", () => { engine = new CLIEngine({ parser: "espree", - envs: ["es6"], + parserOptions: { + ecmaVersion: 2020 + }, useEslintrc: false }); @@ -868,6 +869,29 @@ describe("CLIEngine", () => { assert.strictEqual(report.results[0].messages.length, 0); }); + it("should fall back to defaults when extensions is set to an empty array", () => { + + engine = new CLIEngine({ + cwd: getFixturePath("configurations"), + configFile: getFixturePath("configurations", "quotes-error.json"), + extensions: [] + }); + const report = engine.executeOnFiles([getFixturePath("single-quoted.js")]); + + assert.strictEqual(report.results.length, 1); + assert.strictEqual(report.results[0].messages.length, 1); + assert.strictEqual(report.errorCount, 1); + assert.strictEqual(report.warningCount, 0); + assert.strictEqual(report.fixableErrorCount, 1); + assert.strictEqual(report.fixableWarningCount, 0); + assert.strictEqual(report.results[0].messages[0].ruleId, "quotes"); + assert.strictEqual(report.results[0].messages[0].severity, 2); + assert.strictEqual(report.results[0].errorCount, 1); + assert.strictEqual(report.results[0].warningCount, 0); + assert.strictEqual(report.results[0].fixableErrorCount, 1); + assert.strictEqual(report.results[0].fixableWarningCount, 0); + }); + it("should report zero messages when given a directory with a .js and a .js2 file", () => { engine = new CLIEngine({ @@ -3208,7 +3232,7 @@ describe("CLIEngine", () => { `); }); - it("should use overriden processor; should report HTML blocks but not fix HTML blocks if the processor for '*.html' didn't support autofix.", () => { + it("should use overridden processor; should report HTML blocks but not fix HTML blocks if the processor for '*.html' didn't support autofix.", () => { CLIEngine = defineCLIEngineWithInMemoryFileSystem({ cwd: () => root, files: { diff --git a/eslint/tests/lib/cli-engine/config-array-factory.js b/eslint/tests/lib/cli-engine/config-array-factory.js index 20f256b..3b979c1 100644 --- a/eslint/tests/lib/cli-engine/config-array-factory.js +++ b/eslint/tests/lib/cli-engine/config-array-factory.js @@ -11,7 +11,7 @@ const { spy } = require("sinon"); const { ConfigArray } = require("../../../lib/cli-engine/config-array"); const { OverrideTester } = require("../../../lib/cli-engine/config-array"); const { createContext } = require("../../../lib/cli-engine/config-array-factory"); -const { defineConfigArrayFactoryWithInMemoryFileSystem } = require("./_utils"); +const { defineConfigArrayFactoryWithInMemoryFileSystem } = require("../../_utils"); const tempDir = path.join(os.tmpdir(), "eslint/config-array-factory"); @@ -1026,7 +1026,7 @@ describe("ConfigArrayFactory", () => { }); }); - it("should have the given config data at the thrid element.", () => { + it("should have the given config data at the third element.", () => { assertConfigArrayElement(configArray[2], { name: ".eslintrc", rules: { eqeqeq: 1 } diff --git a/eslint/tests/lib/cli-engine/config-array/ignore-pattern.js b/eslint/tests/lib/cli-engine/config-array/ignore-pattern.js index 443d350..9d652f3 100644 --- a/eslint/tests/lib/cli-engine/config-array/ignore-pattern.js +++ b/eslint/tests/lib/cli-engine/config-array/ignore-pattern.js @@ -6,6 +6,7 @@ const assert = require("assert"); const path = require("path"); +const sinon = require("sinon"); const { IgnorePattern } = require("../../../../lib/cli-engine/config-array/ignore-pattern"); describe("IgnorePattern", () => { @@ -52,74 +53,127 @@ describe("IgnorePattern", () => { describe("static createIgnore(ignorePatterns)", () => { describe("with two patterns should return a function, and the function", () => { - const cwd = process.cwd(); - const basePath1 = path.join(cwd, "foo/bar"); - const basePath2 = path.join(cwd, "abc/"); - const ignores = IgnorePattern.createIgnore([ - new IgnorePattern(["*.js", "/*.ts", "!a.*", "!/b.*"], basePath1), - new IgnorePattern(["*.js", "/*.ts", "!a.*", "!/b.*"], basePath2) - ]); - const patterns = [ - ["a.js", false], - ["a.ts", false], - ["b.js", false], - ["b.ts", false], - ["c.js", false], - ["c.ts", false], - ["dir/a.js", false], - ["dir/a.ts", false], - ["dir/b.js", false], - ["dir/b.ts", false], - ["dir/c.js", false], - ["dir/c.ts", false], - ["foo/bar/a.js", false], - ["foo/bar/a.ts", false], - ["foo/bar/b.js", false], - ["foo/bar/b.ts", false], - ["foo/bar/c.js", true], - ["foo/bar/c.ts", true], - ["foo/bar/dir/a.js", false], - ["foo/bar/dir/a.ts", false], - ["foo/bar/dir/b.js", true], - ["foo/bar/dir/b.ts", false], - ["foo/bar/dir/c.js", true], - ["foo/bar/dir/c.ts", false], - ["abc/a.js", false], - ["abc/a.ts", false], - ["abc/b.js", false], - ["abc/b.ts", false], - ["abc/c.js", true], - ["abc/c.ts", true], - ["abc/dir/a.js", false], - ["abc/dir/a.ts", false], - ["abc/dir/b.js", true], - ["abc/dir/b.ts", false], - ["abc/dir/c.js", true], - ["abc/dir/c.ts", false] - ]; - - for (const [filename, expected] of patterns) { - it(`should return ${expected} if '${filename}' was given.`, () => { - assert.strictEqual(ignores(path.join(cwd, filename)), expected); + + /** + * performs static createIgnre assertions against the cwd. + * @param {string} cwd cwd to be the base path for assertions + * @returns {void} + */ + function assertions(cwd) { + const basePath1 = path.join(cwd, "foo/bar"); + const basePath2 = path.join(cwd, "abc/"); + const ignores = IgnorePattern.createIgnore([ + new IgnorePattern(["*.js", "/*.ts", "!a.*", "!/b.*"], basePath1), + new IgnorePattern(["*.js", "/*.ts", "!a.*", "!/b.*"], basePath2) + ]); + const patterns = [ + ["a.js", false], + ["a.ts", false], + ["b.js", false], + ["b.ts", false], + ["c.js", false], + ["c.ts", false], + ["dir/a.js", false], + ["dir/a.ts", false], + ["dir/b.js", false], + ["dir/b.ts", false], + ["dir/c.js", false], + ["dir/c.ts", false], + ["foo/bar/a.js", false], + ["foo/bar/a.ts", false], + ["foo/bar/b.js", false], + ["foo/bar/b.ts", false], + ["foo/bar/c.js", true], + ["foo/bar/c.ts", true], + ["foo/bar/dir/a.js", false], + ["foo/bar/dir/a.ts", false], + ["foo/bar/dir/b.js", true], + ["foo/bar/dir/b.ts", false], + ["foo/bar/dir/c.js", true], + ["foo/bar/dir/c.ts", false], + ["abc/a.js", false], + ["abc/a.ts", false], + ["abc/b.js", false], + ["abc/b.ts", false], + ["abc/c.js", true], + ["abc/c.ts", true], + ["abc/dir/a.js", false], + ["abc/dir/a.ts", false], + ["abc/dir/b.js", true], + ["abc/dir/b.ts", false], + ["abc/dir/c.js", true], + ["abc/dir/c.ts", false] + ]; + + for (const [filename, expected] of patterns) { + it(`should return ${expected} if '${filename}' was given.`, () => { + assert.strictEqual(ignores(path.join(cwd, filename)), expected); + }); + } + + it("should return false if '.dot.js' and false was given.", () => { + assert.strictEqual(ignores(path.join(cwd, ".dot.js"), false), true); }); - } - it("should return false if '.dot.js' and false was given.", () => { - assert.strictEqual(ignores(path.join(cwd, ".dot.js"), false), true); - }); + it("should return true if '.dot.js' and true were given.", () => { + assert.strictEqual(ignores(path.join(cwd, ".dot.js"), true), false); + }); - it("should return true if '.dot.js' and true were given.", () => { - assert.strictEqual(ignores(path.join(cwd, ".dot.js"), true), false); - }); + it("should return false if '.dot/foo.js' and false was given.", () => { + assert.strictEqual(ignores(path.join(cwd, ".dot/foo.js"), false), true); + }); + it("should return true if '.dot/foo.js' and true were given.", () => { + assert.strictEqual(ignores(path.join(cwd, ".dot/foo.js"), true), false); + }); + } - it("should return false if '.dot/foo.js' and false was given.", () => { - assert.strictEqual(ignores(path.join(cwd, ".dot/foo.js"), false), true); - }); + assertions(process.cwd()); + + /* + * This will catch regressions of Windows specific issue #12850 when run on CI nodes. + * This runs the full set of assertions for the function returned from IgnorePattern.createIgnore. + * When run on Windows CI nodes the .root drive i.e C:\ will be supplied + * forcing getCommonAncestors to resolve to the root of the drive thus catching any regrssion of 12850. + * When run on *nix CI nodes provides additional coverage on this OS too. + * assertions when run on Windows CI nodes and / on *nix OS + */ + assertions(path.parse(process.cwd()).root); + }); + }); - it("should return true if '.dot/foo.js' and true were given.", () => { - assert.strictEqual(ignores(path.join(cwd, ".dot/foo.js"), true), false); - }); + describe("static createIgnore(ignorePatterns)", () => { + + /* + * This test will catch regressions of Windows specific issue #12850 when run on your local dev box + * irrespective of if you are running a Windows or *nix style OS. + * When running on *nix sinon is used to emulate Windows behaviors of path and platform APIs + * thus ensuring that the Windows specific fix is exercised and any regression is caught. + */ + it("with common ancestor of drive root on windows should not throw", () => { + try { + + /* + * When not on Windows return win32 values so local runs on *nix hit the same code path as on Windows + * thus enabling developers with *nix style OS to catch and debug any future regression of #12850 without + * requiring a Windows based OS. + */ + if (process.platform !== "win32") { + sinon.stub(process, "platform").value("win32"); + sinon.stub(path, "sep").value(path.win32.sep); + sinon.replace(path, "isAbsolute", path.win32.isAbsolute); + } + + const ignores = IgnorePattern.createIgnore([ + new IgnorePattern(["*.js"], "C:\\foo\\bar"), + new IgnorePattern(["*.js"], "C:\\abc\\") + ]); + + // calls to this should not throw when getCommonAncestor returns root of drive + ignores("C:\\abc\\contract.d.ts"); + } finally { + sinon.restore(); + } }); }); }); diff --git a/eslint/tests/lib/cli-engine/file-enumerator.js b/eslint/tests/lib/cli-engine/file-enumerator.js index c62578c..4372886 100644 --- a/eslint/tests/lib/cli-engine/file-enumerator.js +++ b/eslint/tests/lib/cli-engine/file-enumerator.js @@ -11,7 +11,7 @@ const { assert } = require("chai"); const sh = require("shelljs"); const { CascadingConfigArrayFactory } = require("../../../lib/cli-engine/cascading-config-array-factory"); -const { defineFileEnumeratorWithInMemoryFileSystem } = require("./_utils"); +const { defineFileEnumeratorWithInMemoryFileSystem } = require("../../_utils"); describe("FileEnumerator", () => { describe("'iterateFiles(patterns)' method should iterate files and configs.", () => { diff --git a/eslint/tests/lib/cli.js b/eslint/tests/lib/cli.js index 6af3b5c..a1d9a23 100644 --- a/eslint/tests/lib/cli.js +++ b/eslint/tests/lib/cli.js @@ -6,8 +6,8 @@ "use strict"; /* - * NOTE: If you are adding new tests for cli.js, use verifyCLIEngineOpts(). The - * test only needs to verify that CLIEngine receives the correct opts. + * NOTE: If you are adding new tests for cli.js, use verifyESLintOpts(). The + * test only needs to verify that ESLint receives the correct opts. */ //------------------------------------------------------------------------------ @@ -15,7 +15,9 @@ //------------------------------------------------------------------------------ const assert = require("chai").assert, - CLIEngine = require("../../lib/cli-engine/index").CLIEngine, + stdAssert = require("assert"), + { ESLint } = require("../../lib/eslint"), + BuiltinRules = require("../../lib/rules"), path = require("path"), sinon = require("sinon"), fs = require("fs"), @@ -44,30 +46,30 @@ describe("cli", () => { }); /** - * Verify that CLIEngine receives correct opts via cli.execute(). + * Verify that ESLint class receives correct opts via await cli.execute(). * @param {string} cmd CLI command. - * @param {Object} opts Options hash that should match that received by CLIEngine. + * @param {Object} opts Options hash that should match that received by ESLint class. * @returns {void} */ - function verifyCLIEngineOpts(cmd, opts) { + async function verifyESLintOpts(cmd, opts) { - // create a fake CLIEngine to test with - const fakeCLIEngine = sinon.mock().withExactArgs(sinon.match(opts)); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match(opts)); - Object.defineProperties(fakeCLIEngine.prototype, Object.getOwnPropertyDescriptors(CLIEngine.prototype)); - sinon.stub(fakeCLIEngine.prototype, "executeOnFiles").returns({}); - sinon.stub(fakeCLIEngine.prototype, "getFormatter").returns(sinon.spy()); + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: sinon.spy() }); const localCLI = proxyquire("../../lib/cli", { - "./cli-engine/index": { CLIEngine: fakeCLIEngine }, + "./eslint": { ESLint: fakeESLint }, "./shared/logging": log }); - localCLI.execute(cmd); + await localCLI.execute(cmd); sinon.verifyAndRestore(); } - // verifyCLIEngineOpts + // verifyESLintOpts /** * Returns the path inside of the fixture directory. @@ -96,30 +98,30 @@ describe("cli", () => { }); describe("execute()", () => { - it("should return error when text with incorrect quotes is passed as argument", () => { + it("should return error when text with incorrect quotes is passed as argument", async () => { const configFile = getFixturePath("configurations", "quotes-error.json"); - const result = cli.execute(`-c ${configFile}`, "var foo = 'bar';"); + const result = await cli.execute(`-c ${configFile}`, "var foo = 'bar';"); assert.strictEqual(result, 1); }); - it("should not print debug info when passed the empty string as text", () => { - const result = cli.execute(["--stdin", "--no-eslintrc"], ""); + it("should not print debug info when passed the empty string as text", async () => { + const result = await cli.execute(["--stdin", "--no-eslintrc"], ""); assert.strictEqual(result, 0); assert.isTrue(log.info.notCalled); }); - it("should return no error when --ext .js2 is specified", () => { + it("should return no error when --ext .js2 is specified", async () => { const filePath = getFixturePath("files"); - const result = cli.execute(`--ext .js2 ${filePath}`); + const result = await cli.execute(`--ext .js2 ${filePath}`); assert.strictEqual(result, 0); }); - it("should exit with console error when passed unsupported arguments", () => { + it("should exit with console error when passed unsupported arguments", async () => { const filePath = getFixturePath("files"); - const result = cli.execute(`--blah --another ${filePath}`); + const result = await cli.execute(`--blah --another ${filePath}`); assert.strictEqual(result, 2); }); @@ -127,120 +129,119 @@ describe("cli", () => { }); describe("when given a config file", () => { - it("should load the specified config file", () => { + it("should load the specified config file", async () => { const configPath = getFixturePath(".eslintrc"); const filePath = getFixturePath("passing.js"); - cli.execute(`--config ${configPath} ${filePath}`); + await cli.execute(`--config ${configPath} ${filePath}`); }); }); describe("when there is a local config file", () => { const code = "lib/cli.js"; - it("should load the local config file", () => { + it("should load the local config file", async () => { // Mock CWD process.eslintCwd = getFixturePath("configurations", "single-quotes"); - cli.execute(code); + await cli.execute(code); process.eslintCwd = null; }); }); describe("when given a config with rules with options and severity level set to error", () => { - it("should exit with an error status (1)", () => { + it("should exit with an error status (1)", async () => { const configPath = getFixturePath("configurations", "quotes-error.json"); const filePath = getFixturePath("single-quoted.js"); const code = `--no-ignore --config ${configPath} ${filePath}`; - const exitStatus = cli.execute(code); + const exitStatus = await cli.execute(code); assert.strictEqual(exitStatus, 1); }); }); describe("when given a config file and a directory of files", () => { - it("should load and execute without error", () => { + it("should load and execute without error", async () => { const configPath = getFixturePath("configurations", "semi-error.json"); const filePath = getFixturePath("formatters"); const code = `--config ${configPath} ${filePath}`; - const exitStatus = cli.execute(code); + const exitStatus = await cli.execute(code); assert.strictEqual(exitStatus, 0); }); }); describe("when given a config with environment set to browser", () => { - it("should execute without any errors", () => { + it("should execute without any errors", async () => { const configPath = getFixturePath("configurations", "env-browser.json"); const filePath = getFixturePath("globals-browser.js"); const code = `--config ${configPath} ${filePath}`; - const exit = cli.execute(code); + const exit = await cli.execute(code); assert.strictEqual(exit, 0); }); }); describe("when given a config with environment set to Node.js", () => { - it("should execute without any errors", () => { + it("should execute without any errors", async () => { const configPath = getFixturePath("configurations", "env-node.json"); const filePath = getFixturePath("globals-node.js"); const code = `--config ${configPath} ${filePath}`; - const exit = cli.execute(code); + const exit = await cli.execute(code); assert.strictEqual(exit, 0); }); }); describe("when given a config with environment set to Nashorn", () => { - it("should execute without any errors", () => { + it("should execute without any errors", async () => { const configPath = getFixturePath("configurations", "env-nashorn.json"); const filePath = getFixturePath("globals-nashorn.js"); const code = `--config ${configPath} ${filePath}`; - const exit = cli.execute(code); + const exit = await cli.execute(code); assert.strictEqual(exit, 0); }); }); describe("when given a config with environment set to WebExtensions", () => { - it("should execute without any errors", () => { + it("should execute without any errors", async () => { const configPath = getFixturePath("configurations", "env-webextensions.json"); const filePath = getFixturePath("globals-webextensions.js"); const code = `--config ${configPath} ${filePath}`; - const exit = cli.execute(code); + const exit = await cli.execute(code); assert.strictEqual(exit, 0); }); }); describe("when given a valid built-in formatter name", () => { - it("should execute without any errors", () => { + it("should execute without any errors", async () => { const filePath = getFixturePath("passing.js"); - const exit = cli.execute(`-f checkstyle ${filePath}`); + const exit = await cli.execute(`-f checkstyle ${filePath}`); assert.strictEqual(exit, 0); }); }); describe("when given a valid built-in formatter name that uses rules meta.", () => { - it("should execute without any errors", () => { + it("should execute without any errors", async () => { const filePath = getFixturePath("passing.js"); - const exit = cli.execute(`-f json-with-metadata ${filePath} --no-eslintrc`); + const exit = await cli.execute(`-f json-with-metadata ${filePath} --no-eslintrc`); assert.strictEqual(exit, 0); // Check metadata. const { metadata } = JSON.parse(log.info.args[0][0]); - const rules = new CLIEngine({ useEslintrc: false }).getRules(); - const expectedMetadata = Array.from(rules).reduce((obj, [ruleId, rule]) => { + const expectedMetadata = Array.from(BuiltinRules).reduce((obj, [ruleId, rule]) => { obj.rulesMeta[ruleId] = rule.meta; return obj; }, { rulesMeta: {} }); @@ -250,204 +251,204 @@ describe("cli", () => { }); describe("when given an invalid built-in formatter name", () => { - it("should execute with error", () => { + it("should execute with error", async () => { const filePath = getFixturePath("passing.js"); - const exit = cli.execute(`-f fakeformatter ${filePath}`); + const exit = await cli.execute(`-f fakeformatter ${filePath}`); assert.strictEqual(exit, 2); }); }); describe("when given a valid formatter path", () => { - it("should execute without any errors", () => { + it("should execute without any errors", async () => { const formatterPath = getFixturePath("formatters", "simple.js"); const filePath = getFixturePath("passing.js"); - const exit = cli.execute(`-f ${formatterPath} ${filePath}`); + const exit = await cli.execute(`-f ${formatterPath} ${filePath}`); assert.strictEqual(exit, 0); }); }); describe("when given an invalid formatter path", () => { - it("should execute with error", () => { + it("should execute with error", async () => { const formatterPath = getFixturePath("formatters", "file-does-not-exist.js"); const filePath = getFixturePath("passing.js"); - const exit = cli.execute(`-f ${formatterPath} ${filePath}`); + const exit = await cli.execute(`-f ${formatterPath} ${filePath}`); assert.strictEqual(exit, 2); }); }); describe("when executing a file with a lint error", () => { - it("should exit with error", () => { + it("should exit with error", async () => { const filePath = getFixturePath("undef.js"); const code = `--no-ignore --rule no-undef:2 ${filePath}`; - const exit = cli.execute(code); + const exit = await cli.execute(code); assert.strictEqual(exit, 1); }); }); describe("when using --fix-type without --fix or --fix-dry-run", () => { - it("should exit with error", () => { + it("should exit with error", async () => { const filePath = getFixturePath("passing.js"); const code = `--fix-type suggestion ${filePath}`; - const exit = cli.execute(code); + const exit = await cli.execute(code); assert.strictEqual(exit, 2); }); }); describe("when executing a file with a syntax error", () => { - it("should exit with error", () => { + it("should exit with error", async () => { const filePath = getFixturePath("syntax-error.js"); - const exit = cli.execute(`--no-ignore ${filePath}`); + const exit = await cli.execute(`--no-ignore ${filePath}`); assert.strictEqual(exit, 1); }); }); describe("when calling execute more than once", () => { - it("should not print the results from previous execution", () => { + it("should not print the results from previous execution", async () => { const filePath = getFixturePath("missing-semicolon.js"); const passingPath = getFixturePath("passing.js"); - cli.execute(`--no-ignore --rule semi:2 ${filePath}`); + await cli.execute(`--no-ignore --rule semi:2 ${filePath}`); assert.isTrue(log.info.called, "Log should have been called."); log.info.resetHistory(); - cli.execute(`--no-ignore --rule semi:2 ${passingPath}`); + await cli.execute(`--no-ignore --rule semi:2 ${passingPath}`); assert.isTrue(log.info.notCalled); }); }); describe("when executing with version flag", () => { - it("should print out current version", () => { - assert.strictEqual(cli.execute("-v"), 0); + it("should print out current version", async () => { + assert.strictEqual(await cli.execute("-v"), 0); assert.strictEqual(log.info.callCount, 1); }); }); describe("when executing with env-info flag", () => { - it("should print out environment information", () => { - assert.strictEqual(cli.execute("--env-info"), 0); + it("should print out environment information", async () => { + assert.strictEqual(await cli.execute("--env-info"), 0); assert.strictEqual(log.info.callCount, 1); }); - it("should print error message and return error code", () => { + it("should print error message and return error code", async () => { RuntimeInfo.environment.throws("There was an error!"); - assert.strictEqual(cli.execute("--env-info"), 2); + assert.strictEqual(await cli.execute("--env-info"), 2); assert.strictEqual(log.error.callCount, 1); }); }); describe("when executing without no-error-on-unmatched-pattern flag", () => { - it("should throw an error on unmatched glob pattern", () => { + it("should throw an error on unmatched glob pattern", async () => { const filePath = getFixturePath("unmatched-patterns"); const globPattern = "*.js3"; - assert.throws(() => { - cli.execute(`"${filePath}/${globPattern}"`); - }, `No files matching '${filePath}/${globPattern}' were found.`); + await stdAssert.rejects(async () => { + await cli.execute(`"${filePath}/${globPattern}"`); + }, new Error(`No files matching '${filePath}/${globPattern}' were found.`)); }); - it("should throw an error on unmatched --ext", () => { + it("should throw an error on unmatched --ext", async () => { const filePath = getFixturePath("unmatched-patterns"); const extension = ".js3"; - assert.throws(() => { - cli.execute(`--ext ${extension} ${filePath}`); + await stdAssert.rejects(async () => { + await cli.execute(`--ext ${extension} ${filePath}`); }, `No files matching '${filePath}' were found`); }); }); describe("when executing with no-error-on-unmatched-pattern flag", () => { - it("should not throw an error on unmatched node glob syntax patterns", () => { + it("should not throw an error on unmatched node glob syntax patterns", async () => { const filePath = getFixturePath("unmatched-patterns"); - const exit = cli.execute(`--no-error-on-unmatched-pattern "${filePath}/*.js3"`); + const exit = await cli.execute(`--no-error-on-unmatched-pattern "${filePath}/*.js3"`); assert.strictEqual(exit, 0); }); - it("should not throw an error on unmatched --ext", () => { + it("should not throw an error on unmatched --ext", async () => { const filePath = getFixturePath("unmatched-patterns"); - const exit = cli.execute(`--no-error-on-unmatched-pattern --ext .js3 ${filePath}`); + const exit = await cli.execute(`--no-error-on-unmatched-pattern --ext .js3 ${filePath}`); assert.strictEqual(exit, 0); }); }); describe("when executing with no-error-on-unmatched-pattern flag and multiple patterns", () => { - it("should not throw an error on multiple unmatched node glob syntax patterns", () => { + it("should not throw an error on multiple unmatched node glob syntax patterns", async () => { const filePath = getFixturePath("unmatched-patterns"); - const exit = cli.execute(`--no-error-on-unmatched-pattern ${filePath}/*.js3 ${filePath}/*.js4`); + const exit = await cli.execute(`--no-error-on-unmatched-pattern ${filePath}/*.js3 ${filePath}/*.js4`); assert.strictEqual(exit, 0); }); - it("should still throw an error on when a matched pattern has lint errors", () => { + it("should still throw an error on when a matched pattern has lint errors", async () => { const filePath = getFixturePath("unmatched-patterns"); - const exit = cli.execute(`--no-error-on-unmatched-pattern ${filePath}/*.js3 ${filePath}/*.js`); + const exit = await cli.execute(`--no-error-on-unmatched-pattern ${filePath}/*.js3 ${filePath}/*.js`); assert.strictEqual(exit, 1); }); }); describe("when executing with no-error-on-unmatched-pattern flag and multiple --ext arguments", () => { - it("should not throw an error on multiple unmatched --ext arguments", () => { + it("should not throw an error on multiple unmatched --ext arguments", async () => { const filePath = getFixturePath("unmatched-patterns"); - const exit = cli.execute(`--no-error-on-unmatched-pattern --ext .js3 --ext .js4 ${filePath}`); + const exit = await cli.execute(`--no-error-on-unmatched-pattern --ext .js3 --ext .js4 ${filePath}`); assert.strictEqual(exit, 0); }); - it("should still throw an error on when a matched pattern has lint errors", () => { + it("should still throw an error on when a matched pattern has lint errors", async () => { const filePath = getFixturePath("unmatched-patterns"); - const exit = cli.execute(`--no-error-on-unmatched-pattern --ext .js3 --ext .js ${filePath}`); + const exit = await cli.execute(`--no-error-on-unmatched-pattern --ext .js3 --ext .js ${filePath}`); assert.strictEqual(exit, 1); }); }); describe("when executing with help flag", () => { - it("should print out help", () => { - assert.strictEqual(cli.execute("-h"), 0); + it("should print out help", async () => { + assert.strictEqual(await cli.execute("-h"), 0); assert.strictEqual(log.info.callCount, 1); }); }); describe("when given a directory with eslint excluded files in the directory", () => { - it("should throw an error and not process any files", () => { + it("should throw an error and not process any files", async () => { const ignorePath = getFixturePath(".eslintignore"); const filePath = getFixturePath("cli"); - assert.throws(() => { - cli.execute(`--ignore-path ${ignorePath} ${filePath}`); - }, `All files matched by '${filePath}' are ignored.`); + await stdAssert.rejects(async () => { + await cli.execute(`--ignore-path ${ignorePath} ${filePath}`); + }, new Error(`All files matched by '${filePath}' are ignored.`)); }); }); describe("when given a file in excluded files list", () => { - it("should not process the file", () => { + it("should not process the file", async () => { const ignorePath = getFixturePath(".eslintignore"); const filePath = getFixturePath("passing.js"); - const exit = cli.execute(`--ignore-path ${ignorePath} ${filePath}`); + const exit = await cli.execute(`--ignore-path ${ignorePath} ${filePath}`); // a warning about the ignored file assert.isTrue(log.info.called); assert.strictEqual(exit, 0); }); - it("should process the file when forced", () => { + it("should process the file when forced", async () => { const ignorePath = getFixturePath(".eslintignore"); const filePath = getFixturePath("passing.js"); - const exit = cli.execute(`--ignore-path ${ignorePath} --no-ignore ${filePath}`); + const exit = await cli.execute(`--ignore-path ${ignorePath} --no-ignore ${filePath}`); // no warnings assert.isFalse(log.info.called); @@ -456,10 +457,10 @@ describe("cli", () => { }); describe("when given a pattern to ignore", () => { - it("should not process any files", () => { + it("should not process any files", async () => { const ignoredFile = getFixturePath("cli/syntax-error.js"); const filePath = getFixturePath("cli/passing.js"); - const exit = cli.execute(`--ignore-pattern cli/ ${ignoredFile} ${filePath}`); + const exit = await cli.execute(`--ignore-pattern cli/ ${ignoredFile} ${filePath}`); // warnings about the ignored files assert.isTrue(log.info.called); @@ -468,61 +469,63 @@ describe("cli", () => { }); describe("when given patterns to ignore", () => { - it("should not process any matching files", () => { + it("should not process any matching files", async () => { const ignorePaths = ["a", "b"]; const cmd = ignorePaths.map(ignorePath => `--ignore-pattern ${ignorePath}`).concat(".").join(" "); const opts = { - ignorePattern: ignorePaths + overrideConfig: { + ignorePatterns: ignorePaths + } }; - verifyCLIEngineOpts(cmd, opts); + await verifyESLintOpts(cmd, opts); }); }); describe("when executing a file with a shebang", () => { - it("should execute without error", () => { + it("should execute without error", async () => { const filePath = getFixturePath("shebang.js"); - const exit = cli.execute(`--no-ignore ${filePath}`); + const exit = await cli.execute(`--no-ignore ${filePath}`); assert.strictEqual(exit, 0); }); }); describe("when loading a custom rule", () => { - it("should return an error when rule isn't found", () => { + it("should return an error when rule isn't found", async () => { const rulesPath = getFixturePath("rules", "wrong"); const configPath = getFixturePath("rules", "eslint.json"); const filePath = getFixturePath("rules", "test", "test-custom-rule.js"); const code = `--rulesdir ${rulesPath} --config ${configPath} --no-ignore ${filePath}`; - assert.throws(() => { - const exit = cli.execute(code); + await stdAssert.rejects(async () => { + const exit = await cli.execute(code); assert.strictEqual(exit, 2); }, /Error while loading rule 'custom-rule': Cannot read property/u); }); - it("should return a warning when rule is matched", () => { + it("should return a warning when rule is matched", async () => { const rulesPath = getFixturePath("rules"); const configPath = getFixturePath("rules", "eslint.json"); const filePath = getFixturePath("rules", "test", "test-custom-rule.js"); const code = `--rulesdir ${rulesPath} --config ${configPath} --no-ignore ${filePath}`; - cli.execute(code); + await cli.execute(code); assert.isTrue(log.info.calledOnce); assert.isTrue(log.info.neverCalledWith("")); }); - it("should return warnings from multiple rules in different directories", () => { + it("should return warnings from multiple rules in different directories", async () => { const rulesPath = getFixturePath("rules", "dir1"); const rulesPath2 = getFixturePath("rules", "dir2"); const configPath = getFixturePath("rules", "multi-rulesdirs.json"); const filePath = getFixturePath("rules", "test-multi-rulesdirs.js"); const code = `--rulesdir ${rulesPath} --rulesdir ${rulesPath2} --config ${configPath} --no-ignore ${filePath}`; - const exit = cli.execute(code); + const exit = await cli.execute(code); const call = log.info.getCall(0); @@ -538,9 +541,9 @@ describe("cli", () => { }); describe("when executing with no-eslintrc flag", () => { - it("should ignore a local config file", () => { + it("should ignore a local config file", async () => { const filePath = getFixturePath("eslintrc", "quotes.js"); - const exit = cli.execute(`--no-eslintrc --no-ignore ${filePath}`); + const exit = await cli.execute(`--no-eslintrc --no-ignore ${filePath}`); assert.isTrue(log.info.notCalled); assert.strictEqual(exit, 0); @@ -548,9 +551,9 @@ describe("cli", () => { }); describe("when executing without no-eslintrc flag", () => { - it("should load a local config file", () => { + it("should load a local config file", async () => { const filePath = getFixturePath("eslintrc", "quotes.js"); - const exit = cli.execute(`--no-ignore ${filePath}`); + const exit = await cli.execute(`--no-ignore ${filePath}`); assert.isTrue(log.info.calledOnce); assert.strictEqual(exit, 1); @@ -558,38 +561,38 @@ describe("cli", () => { }); describe("when executing without env flag", () => { - it("should not define environment-specific globals", () => { + it("should not define environment-specific globals", async () => { const files = [ getFixturePath("globals-browser.js"), getFixturePath("globals-node.js") ]; - cli.execute(`--no-eslintrc --config ./conf/eslint-recommended.js --no-ignore ${files.join(" ")}`); + await cli.execute(`--no-eslintrc --config ./conf/eslint-recommended.js --no-ignore ${files.join(" ")}`); assert.strictEqual(log.info.args[0][0].split("\n").length, 10); }); }); describe("when executing with global flag", () => { - it("should default defined variables to read-only", () => { + it("should default defined variables to read-only", async () => { const filePath = getFixturePath("undef.js"); - const exit = cli.execute(`--global baz,bat --no-ignore --rule no-global-assign:2 ${filePath}`); + const exit = await cli.execute(`--global baz,bat --no-ignore --rule no-global-assign:2 ${filePath}`); assert.isTrue(log.info.calledOnce); assert.strictEqual(exit, 1); }); - it("should allow defining writable global variables", () => { + it("should allow defining writable global variables", async () => { const filePath = getFixturePath("undef.js"); - const exit = cli.execute(`--global baz:false,bat:true --no-ignore ${filePath}`); + const exit = await cli.execute(`--global baz:false,bat:true --no-ignore ${filePath}`); assert.isTrue(log.info.notCalled); assert.strictEqual(exit, 0); }); - it("should allow defining variables with multiple flags", () => { + it("should allow defining variables with multiple flags", async () => { const filePath = getFixturePath("undef.js"); - const exit = cli.execute(`--global baz --global bat:true --no-ignore ${filePath}`); + const exit = await cli.execute(`--global baz --global bat:true --no-ignore ${filePath}`); assert.isTrue(log.info.notCalled); assert.strictEqual(exit, 0); @@ -597,10 +600,10 @@ describe("cli", () => { }); describe("when supplied with rule flag and severity level set to error", () => { - it("should exit with an error status (2)", () => { + it("should exit with an error status (2)", async () => { const filePath = getFixturePath("single-quoted.js"); const code = `--no-ignore --rule 'quotes: [2, double]' ${filePath}`; - const exitStatus = cli.execute(code); + const exitStatus = await cli.execute(code); assert.strictEqual(exitStatus, 1); }); @@ -608,11 +611,11 @@ describe("cli", () => { describe("when the quiet option is enabled", () => { - it("should only print error", () => { + it("should only print error", async () => { const filePath = getFixturePath("single-quoted.js"); const cliArgs = `--no-ignore --quiet -f compact --rule 'quotes: [2, double]' --rule 'no-unused-vars: 1' ${filePath}`; - cli.execute(cliArgs); + await cli.execute(cliArgs); sinon.assert.calledOnce(log.info); @@ -622,11 +625,11 @@ describe("cli", () => { assert.notInclude(formattedOutput, "Warning"); }); - it("should print nothing if there are no errors", () => { + it("should print nothing if there are no errors", async () => { const filePath = getFixturePath("single-quoted.js"); const cliArgs = `--quiet -f compact --rule 'quotes: [1, double]' --rule 'no-unused-vars: 1' ${filePath}`; - cli.execute(cliArgs); + await cli.execute(cliArgs); sinon.assert.notCalled(log.info); }); @@ -637,36 +640,36 @@ describe("cli", () => { sh.rm("-rf", "tests/output"); }); - it("should write the file and create dirs if they don't exist", () => { + it("should write the file and create dirs if they don't exist", async () => { const filePath = getFixturePath("single-quoted.js"); const code = `--no-ignore --rule 'quotes: [1, double]' --o tests/output/eslint-output.txt ${filePath}`; - cli.execute(code); + await cli.execute(code); assert.include(fs.readFileSync("tests/output/eslint-output.txt", "utf8"), filePath); assert.isTrue(log.info.notCalled); }); - it("should return an error if the path is a directory", () => { + it("should return an error if the path is a directory", async () => { const filePath = getFixturePath("single-quoted.js"); const code = `--no-ignore --rule 'quotes: [1, double]' --o tests/output ${filePath}`; fs.mkdirSync("tests/output"); - const exit = cli.execute(code); + const exit = await cli.execute(code); assert.strictEqual(exit, 2); assert.isTrue(log.info.notCalled); assert.isTrue(log.error.calledOnce); }); - it("should return an error if the path could not be written to", () => { + it("should return an error if the path could not be written to", async () => { const filePath = getFixturePath("single-quoted.js"); const code = `--no-ignore --rule 'quotes: [1, double]' --o tests/output/eslint-output.txt ${filePath}`; fs.writeFileSync("tests/output", "foo"); - const exit = cli.execute(code); + const exit = await cli.execute(code); assert.strictEqual(exit, 2); assert.isTrue(log.info.notCalled); @@ -675,106 +678,108 @@ describe("cli", () => { }); describe("when supplied with a plugin", () => { - it("should pass plugins to CLIEngine", () => { + it("should pass plugins to ESLint", async () => { const examplePluginName = "eslint-plugin-example"; - verifyCLIEngineOpts(`--no-ignore --plugin ${examplePluginName} foo.js`, { - plugins: [examplePluginName] + await verifyESLintOpts(`--no-ignore --plugin ${examplePluginName} foo.js`, { + overrideConfig: { + plugins: [examplePluginName] + } }); }); }); describe("when supplied with a plugin-loading path", () => { - it("should pass the option to CLIEngine", () => { + it("should pass the option to ESLint", async () => { const examplePluginDirPath = "foo/bar"; - verifyCLIEngineOpts(`--resolve-plugins-relative-to ${examplePluginDirPath} foo.js`, { + await verifyESLintOpts(`--resolve-plugins-relative-to ${examplePluginDirPath} foo.js`, { resolvePluginsRelativeTo: examplePluginDirPath }); }); }); describe("when given an parser name", () => { - it("should exit with a fatal error if parser is invalid", () => { + it("should exit with a fatal error if parser is invalid", async () => { const filePath = getFixturePath("passing.js"); - assert.throws(() => cli.execute(`--no-ignore --parser test111 ${filePath}`), "Cannot find module 'test111'"); + await stdAssert.rejects(async () => await cli.execute(`--no-ignore --parser test111 ${filePath}`), "Cannot find module 'test111'"); }); - it("should exit with no error if parser is valid", () => { + it("should exit with no error if parser is valid", async () => { const filePath = getFixturePath("passing.js"); - const exit = cli.execute(`--no-ignore --parser espree ${filePath}`); + const exit = await cli.execute(`--no-ignore --parser espree ${filePath}`); assert.strictEqual(exit, 0); }); }); describe("when given parser options", () => { - it("should exit with error if parser options are invalid", () => { + it("should exit with error if parser options are invalid", async () => { const filePath = getFixturePath("passing.js"); - const exit = cli.execute(`--no-ignore --parser-options test111 ${filePath}`); + const exit = await cli.execute(`--no-ignore --parser-options test111 ${filePath}`); assert.strictEqual(exit, 2); }); - it("should exit with no error if parser is valid", () => { + it("should exit with no error if parser is valid", async () => { const filePath = getFixturePath("passing.js"); - const exit = cli.execute(`--no-ignore --parser-options=ecmaVersion:6 ${filePath}`); + const exit = await cli.execute(`--no-ignore --parser-options=ecmaVersion:6 ${filePath}`); assert.strictEqual(exit, 0); }); - it("should exit with an error on ecmaVersion 7 feature in ecmaVersion 6", () => { + it("should exit with an error on ecmaVersion 7 feature in ecmaVersion 6", async () => { const filePath = getFixturePath("passing-es7.js"); - const exit = cli.execute(`--no-ignore --parser-options=ecmaVersion:6 ${filePath}`); + const exit = await cli.execute(`--no-ignore --parser-options=ecmaVersion:6 ${filePath}`); assert.strictEqual(exit, 1); }); - it("should exit with no error on ecmaVersion 7 feature in ecmaVersion 7", () => { + it("should exit with no error on ecmaVersion 7 feature in ecmaVersion 7", async () => { const filePath = getFixturePath("passing-es7.js"); - const exit = cli.execute(`--no-ignore --parser-options=ecmaVersion:7 ${filePath}`); + const exit = await cli.execute(`--no-ignore --parser-options=ecmaVersion:7 ${filePath}`); assert.strictEqual(exit, 0); }); - it("should exit with no error on ecmaVersion 7 feature with config ecmaVersion 6 and command line ecmaVersion 7", () => { + it("should exit with no error on ecmaVersion 7 feature with config ecmaVersion 6 and command line ecmaVersion 7", async () => { const configPath = getFixturePath("configurations", "es6.json"); const filePath = getFixturePath("passing-es7.js"); - const exit = cli.execute(`--no-ignore --config ${configPath} --parser-options=ecmaVersion:7 ${filePath}`); + const exit = await cli.execute(`--no-ignore --config ${configPath} --parser-options=ecmaVersion:7 ${filePath}`); assert.strictEqual(exit, 0); }); }); describe("when given the max-warnings flag", () => { - it("should not change exit code if warning count under threshold", () => { + it("should not change exit code if warning count under threshold", async () => { const filePath = getFixturePath("max-warnings"); - const exitCode = cli.execute(`--no-ignore --max-warnings 10 ${filePath}`); + const exitCode = await cli.execute(`--no-ignore --max-warnings 10 ${filePath}`); assert.strictEqual(exitCode, 0); }); - it("should exit with exit code 1 if warning count exceeds threshold", () => { + it("should exit with exit code 1 if warning count exceeds threshold", async () => { const filePath = getFixturePath("max-warnings"); - const exitCode = cli.execute(`--no-ignore --max-warnings 5 ${filePath}`); + const exitCode = await cli.execute(`--no-ignore --max-warnings 5 ${filePath}`); assert.strictEqual(exitCode, 1); assert.ok(log.error.calledOnce); assert.include(log.error.getCall(0).args[0], "ESLint found too many warnings"); }); - it("should not change exit code if warning count equals threshold", () => { + it("should not change exit code if warning count equals threshold", async () => { const filePath = getFixturePath("max-warnings"); - const exitCode = cli.execute(`--no-ignore --max-warnings 6 ${filePath}`); + const exitCode = await cli.execute(`--no-ignore --max-warnings 6 ${filePath}`); assert.strictEqual(exitCode, 0); }); - it("should not change exit code if flag is not specified and there are warnings", () => { + it("should not change exit code if flag is not specified and there are warnings", async () => { const filePath = getFixturePath("max-warnings"); - const exitCode = cli.execute(filePath); + const exitCode = await cli.execute(filePath); assert.strictEqual(exitCode, 0); }); @@ -787,58 +792,51 @@ describe("cli", () => { sinon.verifyAndRestore(); }); - it("should pass allowInlineConfig:true to CLIEngine when --no-inline-config is used", () => { + it("should pass allowInlineConfig:false to ESLint when --no-inline-config is used", async () => { - // create a fake CLIEngine to test with - const fakeCLIEngine = sinon.mock().withExactArgs(sinon.match({ allowInlineConfig: false })); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ allowInlineConfig: false })); - Object.defineProperties(fakeCLIEngine.prototype, Object.getOwnPropertyDescriptors(CLIEngine.prototype)); - sinon.stub(fakeCLIEngine.prototype, "executeOnFiles").returns({ + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns([{ + filePath: "./foo.js", + output: "bar", + messages: [ + { + severity: 2, + message: "Fake message" + } + ], errorCount: 1, - warningCount: 0, - results: [{ - filePath: "./foo.js", - output: "bar", - messages: [ - { - severity: 2, - message: "Fake message" - } - ] - }] - }); - sinon.stub(fakeCLIEngine.prototype, "getFormatter").returns(() => "done"); - fakeCLIEngine.outputFixes = sinon.stub(); + warningCount: 0 + }]); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.stub(); localCLI = proxyquire("../../lib/cli", { - "./cli-engine/index": { CLIEngine: fakeCLIEngine }, + "./eslint": { ESLint: fakeESLint }, "./shared/logging": log }); - localCLI.execute("--no-inline-config ."); + await localCLI.execute("--no-inline-config ."); }); - it("should not error and allowInlineConfig should be true by default", () => { + it("should not error and allowInlineConfig should be true by default", async () => { - // create a fake CLIEngine to test with - const fakeCLIEngine = sinon.mock().withExactArgs(sinon.match({ allowInlineConfig: true })); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ allowInlineConfig: true })); - Object.defineProperties(fakeCLIEngine.prototype, Object.getOwnPropertyDescriptors(CLIEngine.prototype)); - sinon.stub(fakeCLIEngine.prototype, "executeOnFiles").returns({ - errorCount: 0, - warningCount: 0, - results: [] - }); - sinon.stub(fakeCLIEngine.prototype, "getFormatter").returns(() => "done"); - sinon.stub(fakeCLIEngine.prototype, "getRules").returns(new Map()); - fakeCLIEngine.outputFixes = sinon.stub(); + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.stub(); localCLI = proxyquire("../../lib/cli", { - "./cli-engine/index": { CLIEngine: fakeCLIEngine }, + "./eslint": { ESLint: fakeESLint }, "./shared/logging": log }); - const exitCode = localCLI.execute("."); + const exitCode = await localCLI.execute("."); assert.strictEqual(exitCode, 0); @@ -853,119 +851,108 @@ describe("cli", () => { sinon.verifyAndRestore(); }); - it("should pass fix:true to CLIEngine when executing on files", () => { + it("should pass fix:true to ESLint when executing on files", async () => { - // create a fake CLIEngine to test with - const fakeCLIEngine = sinon.mock().withExactArgs(sinon.match({ fix: true })); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); - Object.defineProperties(fakeCLIEngine.prototype, Object.getOwnPropertyDescriptors(CLIEngine.prototype)); - sinon.stub(fakeCLIEngine.prototype, "executeOnFiles").returns({ - errorCount: 0, - warningCount: 0, - results: [] - }); - sinon.stub(fakeCLIEngine.prototype, "getFormatter").returns(() => "done"); - sinon.stub(fakeCLIEngine.prototype, "getRules").returns(new Map()); - fakeCLIEngine.outputFixes = sinon.mock().once(); + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.mock().once(); localCLI = proxyquire("../../lib/cli", { - "./cli-engine/index": { CLIEngine: fakeCLIEngine }, + "./eslint": { ESLint: fakeESLint }, "./shared/logging": log }); - const exitCode = localCLI.execute("--fix ."); + const exitCode = await localCLI.execute("--fix ."); assert.strictEqual(exitCode, 0); }); - it("should rewrite files when in fix mode", () => { + it("should rewrite files when in fix mode", async () => { - const report = { + const report = [{ + filePath: "./foo.js", + output: "bar", + messages: [ + { + severity: 2, + message: "Fake message" + } + ], errorCount: 1, - warningCount: 0, - results: [{ - filePath: "./foo.js", - output: "bar", - messages: [ - { - severity: 2, - message: "Fake message" - } - ] - }] - }; + warningCount: 0 + }]; - // create a fake CLIEngine to test with - const fakeCLIEngine = sinon.mock().withExactArgs(sinon.match({ fix: true })); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); - Object.defineProperties(fakeCLIEngine.prototype, Object.getOwnPropertyDescriptors(CLIEngine.prototype)); - sinon.stub(fakeCLIEngine.prototype, "executeOnFiles").returns(report); - sinon.stub(fakeCLIEngine.prototype, "getFormatter").returns(() => "done"); - sinon.stub(fakeCLIEngine.prototype, "getRules").returns(new Map()); - fakeCLIEngine.outputFixes = sinon.mock().withExactArgs(report); + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.mock().withExactArgs(report); localCLI = proxyquire("../../lib/cli", { - "./cli-engine/index": { CLIEngine: fakeCLIEngine }, + "./eslint": { ESLint: fakeESLint }, "./shared/logging": log }); - const exitCode = localCLI.execute("--fix ."); + const exitCode = await localCLI.execute("--fix ."); assert.strictEqual(exitCode, 1); }); - it("should provide fix predicate and rewrite files when in fix mode and quiet mode", () => { + it("should provide fix predicate and rewrite files when in fix mode and quiet mode", async () => { - const report = { + const report = [{ + filePath: "./foo.js", + output: "bar", + messages: [ + { + severity: 1, + message: "Fake message" + } + ], errorCount: 0, - warningCount: 1, - results: [{ - filePath: "./foo.js", - output: "bar", - messages: [ - { - severity: 1, - message: "Fake message" - } - ] - }] - }; + warningCount: 1 + }]; - // create a fake CLIEngine to test with - const fakeCLIEngine = sinon.mock().withExactArgs(sinon.match({ fix: sinon.match.func })); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: sinon.match.func })); - Object.defineProperties(fakeCLIEngine.prototype, Object.getOwnPropertyDescriptors(CLIEngine.prototype)); - sinon.stub(fakeCLIEngine.prototype, "executeOnFiles").returns(report); - sinon.stub(fakeCLIEngine.prototype, "getFormatter").returns(() => "done"); - sinon.stub(fakeCLIEngine.prototype, "getRules").returns(new Map()); - fakeCLIEngine.getErrorResults = sinon.stub().returns([]); - fakeCLIEngine.outputFixes = sinon.mock().withExactArgs(report); + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.getErrorResults = sinon.stub().returns([]); + fakeESLint.outputFixes = sinon.mock().withExactArgs(report); localCLI = proxyquire("../../lib/cli", { - "./cli-engine/index": { CLIEngine: fakeCLIEngine }, + "./eslint": { ESLint: fakeESLint }, "./shared/logging": log }); - const exitCode = localCLI.execute("--fix --quiet ."); + const exitCode = await localCLI.execute("--fix --quiet ."); assert.strictEqual(exitCode, 0); }); - it("should not call CLIEngine and return 1 when executing on text", () => { + it("should not call ESLint and return 2 when executing on text", async () => { - // create a fake CLIEngine to test with - const fakeCLIEngine = sinon.mock().never(); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().never(); localCLI = proxyquire("../../lib/cli", { - "./cli-engine/index": { CLIEngine: fakeCLIEngine }, + "./eslint": { ESLint: fakeESLint }, "./shared/logging": log }); - const exitCode = localCLI.execute("--fix .", "foo = bar;"); + const exitCode = await localCLI.execute("--fix .", "foo = bar;"); assert.strictEqual(exitCode, 2); }); @@ -979,212 +966,193 @@ describe("cli", () => { sinon.verifyAndRestore(); }); - it("should pass fix:true to CLIEngine when executing on files", () => { + it("should pass fix:true to ESLint when executing on files", async () => { - // create a fake CLIEngine to test with - const fakeCLIEngine = sinon.mock().withExactArgs(sinon.match({ fix: true })); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); - Object.defineProperties(fakeCLIEngine.prototype, Object.getOwnPropertyDescriptors(CLIEngine.prototype)); - sinon.stub(fakeCLIEngine.prototype, "executeOnFiles").returns({ - errorCount: 0, - warningCount: 0, - results: [] - }); - sinon.stub(fakeCLIEngine.prototype, "getFormatter").returns(() => "done"); - sinon.stub(fakeCLIEngine.prototype, "getRules").returns(new Map()); - fakeCLIEngine.outputFixes = sinon.mock().never(); + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.mock().never(); localCLI = proxyquire("../../lib/cli", { - "./cli-engine/index": { CLIEngine: fakeCLIEngine }, + "./eslint": { ESLint: fakeESLint }, "./shared/logging": log }); - const exitCode = localCLI.execute("--fix-dry-run ."); + const exitCode = await localCLI.execute("--fix-dry-run ."); assert.strictEqual(exitCode, 0); }); - it("should pass fixTypes to CLIEngine when --fix-type is passed", () => { + it("should pass fixTypes to ESLint when --fix-type is passed", async () => { - const expectedCLIEngineOptions = { + const expectedESLintOptions = { fix: true, fixTypes: ["suggestion"] }; - // create a fake CLIEngine to test with - const fakeCLIEngine = sinon.mock().withExactArgs(sinon.match(expectedCLIEngineOptions)); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match(expectedESLintOptions)); - Object.defineProperties(fakeCLIEngine.prototype, Object.getOwnPropertyDescriptors(CLIEngine.prototype)); - sinon.stub(fakeCLIEngine.prototype, "executeOnFiles").returns({ - errorCount: 0, - warningCount: 0, - results: [] - }); - sinon.stub(fakeCLIEngine.prototype, "getFormatter").returns(() => "done"); - sinon.stub(fakeCLIEngine.prototype, "getRules").returns(new Map()); - fakeCLIEngine.outputFixes = sinon.stub(); + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns([]); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.stub(); localCLI = proxyquire("../../lib/cli", { - "./cli-engine/index": { CLIEngine: fakeCLIEngine }, + "./eslint": { ESLint: fakeESLint }, "./shared/logging": log }); - const exitCode = localCLI.execute("--fix-dry-run --fix-type suggestion ."); + const exitCode = await localCLI.execute("--fix-dry-run --fix-type suggestion ."); assert.strictEqual(exitCode, 0); }); - it("should not rewrite files when in fix-dry-run mode", () => { + it("should not rewrite files when in fix-dry-run mode", async () => { - const report = { + const report = [{ + filePath: "./foo.js", + output: "bar", + messages: [ + { + severity: 2, + message: "Fake message" + } + ], errorCount: 1, - warningCount: 0, - results: [{ - filePath: "./foo.js", - output: "bar", - messages: [ - { - severity: 2, - message: "Fake message" - } - ] - }] - }; + warningCount: 0 + }]; - // create a fake CLIEngine to test with - const fakeCLIEngine = sinon.mock().withExactArgs(sinon.match({ fix: true })); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); - Object.defineProperties(fakeCLIEngine.prototype, Object.getOwnPropertyDescriptors(CLIEngine.prototype)); - sinon.stub(fakeCLIEngine.prototype, "executeOnFiles").returns(report); - sinon.stub(fakeCLIEngine.prototype, "getFormatter").returns(() => "done"); - sinon.stub(fakeCLIEngine.prototype, "getRules").returns(new Map()); - fakeCLIEngine.outputFixes = sinon.mock().never(); + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.mock().never(); localCLI = proxyquire("../../lib/cli", { - "./cli-engine/index": { CLIEngine: fakeCLIEngine }, + "./eslint": { ESLint: fakeESLint }, "./shared/logging": log }); - const exitCode = localCLI.execute("--fix-dry-run ."); + const exitCode = await localCLI.execute("--fix-dry-run ."); assert.strictEqual(exitCode, 1); }); - it("should provide fix predicate when in fix-dry-run mode and quiet mode", () => { + it("should provide fix predicate when in fix-dry-run mode and quiet mode", async () => { - const report = { + const report = [{ + filePath: "./foo.js", + output: "bar", + messages: [ + { + severity: 1, + message: "Fake message" + } + ], errorCount: 0, - warningCount: 1, - results: [{ - filePath: "./foo.js", - output: "bar", - messages: [ - { - severity: 1, - message: "Fake message" - } - ] - }] - }; + warningCount: 1 + }]; - // create a fake CLIEngine to test with - const fakeCLIEngine = sinon.mock().withExactArgs(sinon.match({ fix: sinon.match.func })); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: sinon.match.func })); - Object.defineProperties(fakeCLIEngine.prototype, Object.getOwnPropertyDescriptors(CLIEngine.prototype)); - sinon.stub(fakeCLIEngine.prototype, "executeOnFiles").returns(report); - sinon.stub(fakeCLIEngine.prototype, "getFormatter").returns(() => "done"); - sinon.stub(fakeCLIEngine.prototype, "getRules").returns(new Map()); - fakeCLIEngine.getErrorResults = sinon.stub().returns([]); - fakeCLIEngine.outputFixes = sinon.mock().never(); + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintFiles").returns(report); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.getErrorResults = sinon.stub().returns([]); + fakeESLint.outputFixes = sinon.mock().never(); localCLI = proxyquire("../../lib/cli", { - "./cli-engine/index": { CLIEngine: fakeCLIEngine }, + "./eslint": { ESLint: fakeESLint }, "./shared/logging": log }); - const exitCode = localCLI.execute("--fix-dry-run --quiet ."); + const exitCode = await localCLI.execute("--fix-dry-run --quiet ."); assert.strictEqual(exitCode, 0); }); - it("should allow executing on text", () => { + it("should allow executing on text", async () => { - const report = { + const report = [{ + filePath: "./foo.js", + output: "bar", + messages: [ + { + severity: 2, + message: "Fake message" + } + ], errorCount: 1, - warningCount: 0, - results: [{ - filePath: "./foo.js", - output: "bar", - messages: [ - { - severity: 2, - message: "Fake message" - } - ] - }] - }; + warningCount: 0 + }]; - // create a fake CLIEngine to test with - const fakeCLIEngine = sinon.mock().withExactArgs(sinon.match({ fix: true })); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().withExactArgs(sinon.match({ fix: true })); - Object.defineProperties(fakeCLIEngine.prototype, Object.getOwnPropertyDescriptors(CLIEngine.prototype)); - sinon.stub(fakeCLIEngine.prototype, "executeOnText").returns(report); - sinon.stub(fakeCLIEngine.prototype, "getFormatter").returns(() => "done"); - sinon.stub(fakeCLIEngine.prototype, "getRules").returns(new Map()); - fakeCLIEngine.outputFixes = sinon.mock().never(); + Object.defineProperties(fakeESLint.prototype, Object.getOwnPropertyDescriptors(ESLint.prototype)); + sinon.stub(fakeESLint.prototype, "lintText").returns(report); + sinon.stub(fakeESLint.prototype, "loadFormatter").returns({ format: () => "done" }); + fakeESLint.outputFixes = sinon.mock().never(); localCLI = proxyquire("../../lib/cli", { - "./cli-engine/index": { CLIEngine: fakeCLIEngine }, + "./eslint": { ESLint: fakeESLint }, "./shared/logging": log }); - const exitCode = localCLI.execute("--fix-dry-run .", "foo = bar;"); + const exitCode = await localCLI.execute("--fix-dry-run .", "foo = bar;"); assert.strictEqual(exitCode, 1); }); - it("should not call CLIEngine and return 1 when used with --fix", () => { + it("should not call ESLint and return 2 when used with --fix", async () => { - // create a fake CLIEngine to test with - const fakeCLIEngine = sinon.mock().never(); + // create a fake ESLint class to test with + const fakeESLint = sinon.mock().never(); localCLI = proxyquire("../../lib/cli", { - "./cli-engine/index": { CLIEngine: fakeCLIEngine }, + "./eslint": { ESLint: fakeESLint }, "./shared/logging": log }); - const exitCode = localCLI.execute("--fix --fix-dry-run .", "foo = bar;"); + const exitCode = await localCLI.execute("--fix --fix-dry-run .", "foo = bar;"); assert.strictEqual(exitCode, 2); }); }); describe("when passing --print-config", () => { - it("should print out the configuration", () => { + it("should print out the configuration", async () => { const filePath = getFixturePath("xxxx"); - const exitCode = cli.execute(`--print-config ${filePath}`); + const exitCode = await cli.execute(`--print-config ${filePath}`); assert.isTrue(log.info.calledOnce); assert.strictEqual(exitCode, 0); }); - it("should error if any positional file arguments are passed", () => { + it("should error if any positional file arguments are passed", async () => { const filePath1 = getFixturePath("files", "bar.js"); const filePath2 = getFixturePath("files", "foo.js"); - const exitCode = cli.execute(`--print-config ${filePath1} ${filePath2}`); + const exitCode = await cli.execute(`--print-config ${filePath1} ${filePath2}`); assert.isTrue(log.info.notCalled); assert.isTrue(log.error.calledOnce); assert.strictEqual(exitCode, 2); }); - it("should error out when executing on text", () => { - const exitCode = cli.execute("--print-config=myFile.js", "foo = bar;"); + it("should error out when executing on text", async () => { + const exitCode = await cli.execute("--print-config=myFile.js", "foo = bar;"); assert.isTrue(log.info.notCalled); assert.isTrue(log.error.calledOnce); diff --git a/eslint/tests/lib/eslint/eslint.js b/eslint/tests/lib/eslint/eslint.js new file mode 100644 index 0000000..fc86a78 --- /dev/null +++ b/eslint/tests/lib/eslint/eslint.js @@ -0,0 +1,6089 @@ +/** + * @fileoverview Tests for the ESLint class. + * @author Kai Cataldo + * @author Toru Nagashima + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const assert = require("assert"); +const fs = require("fs"); +const os = require("os"); +const path = require("path"); +const escapeStringRegExp = require("escape-string-regexp"); +const fCache = require("file-entry-cache"); +const leche = require("leche"); +const sinon = require("sinon"); +const proxyquire = require("proxyquire").noCallThru().noPreserveCache(); +const shell = require("shelljs"); +const { CascadingConfigArrayFactory } = require("../../../lib/cli-engine/cascading-config-array-factory"); +const hash = require("../../../lib/cli-engine/hash"); +const { unIndent, defineESLintWithInMemoryFileSystem } = require("../../_utils"); + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +describe("ESLint", () => { + const examplePluginName = "eslint-plugin-example"; + const examplePluginNameWithNamespace = "@eslint/eslint-plugin-example"; + const examplePlugin = { + rules: { + "example-rule": require("../../fixtures/rules/custom-rule"), + "make-syntax-error": require("../../fixtures/rules/make-syntax-error-rule") + } + }; + const examplePreprocessorName = "eslint-plugin-processor"; + const originalDir = process.cwd(); + const fixtureDir = path.resolve(fs.realpathSync(os.tmpdir()), "eslint/fixtures"); + + /** @type {import("../../../lib/eslint")["ESLint"]} */ + let ESLint; + + /** + * Returns the path inside of the fixture directory. + * @param {...string} args file path segments. + * @returns {string} The path inside the fixture directory. + * @private + */ + function getFixturePath(...args) { + const filepath = path.join(fixtureDir, ...args); + + try { + return fs.realpathSync(filepath); + } catch (e) { + return filepath; + } + } + + /** + * Create the ESLint object by mocking some of the plugins + * @param {Object} options options for ESLint + * @returns {ESLint} engine object + * @private + */ + function eslintWithPlugins(options) { + return new ESLint({ + ...options, + plugins: { + [examplePluginName]: examplePlugin, + [examplePluginNameWithNamespace]: examplePlugin, + [examplePreprocessorName]: require("../../fixtures/processors/custom-processor") + } + }); + } + + /** + * Call the last argument. + * @param {any[]} args Arguments + * @returns {void} + */ + function callLastArgument(...args) { + process.nextTick(args[args.length - 1], null); + } + + // copy into clean area so as not to get "infected" by this project's .eslintrc files + before(() => { + shell.mkdir("-p", fixtureDir); + shell.cp("-r", "./tests/fixtures/.", fixtureDir); + }); + + beforeEach(() => { + ({ ESLint } = require("../../../lib/eslint/eslint")); + }); + + after(() => { + shell.rm("-r", fixtureDir); + }); + + describe("ESLint constructor function", () => { + it("the default value of 'options.cwd' should be the current working directory.", async () => { + process.chdir(__dirname); + try { + const engine = new ESLint(); + const results = await engine.lintFiles("eslint.js"); + + assert.strictEqual(path.dirname(results[0].filePath), __dirname); + } finally { + process.chdir(originalDir); + } + }); + + it("should report one fatal message when given a path by --ignore-path that is not a file when ignore is true.", () => { + assert.throws(() => { + // eslint-disable-next-line no-new + new ESLint({ ignorePath: fixtureDir }); + }, new RegExp(escapeStringRegExp(`Cannot read .eslintignore file: ${fixtureDir}\nError: EISDIR: illegal operation on a directory, read`), "u")); + }); + + // https://github.com/eslint/eslint/issues/2380 + it("should not modify baseConfig when format is specified", () => { + const customBaseConfig = { root: true }; + + new ESLint({ baseConfig: customBaseConfig }); // eslint-disable-line no-new + + assert.deepStrictEqual(customBaseConfig, { root: true }); + }); + + it("should throw readable messages if removed options are present", () => { + assert.throws( + () => new ESLint({ + cacheFile: "", + configFile: "", + envs: [], + globals: [], + ignorePattern: [], + parser: "", + parserOptions: {}, + rules: {}, + plugins: [] + }), + new RegExp(escapeStringRegExp([ + "Invalid Options:", + "- Unknown options: cacheFile, configFile, envs, globals, ignorePattern, parser, parserOptions, rules", + "- 'cacheFile' has been removed. Please use the 'cacheLocation' option instead.", + "- 'configFile' has been removed. Please use the 'overrideConfigFile' option instead.", + "- 'envs' has been removed. Please use the 'overrideConfig.env' option instead.", + "- 'globals' has been removed. Please use the 'overrideConfig.globals' option instead.", + "- 'ignorePattern' has been removed. Please use the 'overrideConfig.ignorePatterns' option instead.", + "- 'parser' has been removed. Please use the 'overrideConfig.parser' option instead.", + "- 'parserOptions' has been removed. Please use the 'overrideConfig.parserOptions' option instead.", + "- 'rules' has been removed. Please use the 'overrideConfig.rules' option instead.", + "- 'plugins' doesn't add plugins to configuration to load. Please use the 'overrideConfig.plugins' option instead." + ].join("\n")), "u") + ); + }); + + it("should throw readable messages if wrong type values are given to options", () => { + assert.throws( + () => new ESLint({ + allowInlineConfig: "", + baseConfig: "", + cache: "", + cacheLocation: "", + cwd: "foo", + errorOnUnmatchedPattern: "", + extensions: "", + fix: "", + fixTypes: ["xyz"], + globInputPaths: "", + ignore: "", + ignorePath: "", + overrideConfig: "", + overrideConfigFile: "", + plugins: "", + reportUnusedDisableDirectives: "", + resolvePluginsRelativeTo: "", + rulePaths: "", + useEslintrc: "" + }), + new RegExp(escapeStringRegExp([ + "Invalid Options:", + "- 'allowInlineConfig' must be a boolean.", + "- 'baseConfig' must be an object or null.", + "- 'cache' must be a boolean.", + "- 'cacheLocation' must be a non-empty string.", + "- 'cwd' must be an absolute path.", + "- 'errorOnUnmatchedPattern' must be a boolean.", + "- 'extensions' must be an array of non-empty strings or null.", + "- 'fix' must be a boolean or a function.", + "- 'fixTypes' must be an array of any of \"problem\", \"suggestion\", and \"layout\".", + "- 'globInputPaths' must be a boolean.", + "- 'ignore' must be a boolean.", + "- 'ignorePath' must be a non-empty string or null.", + "- 'overrideConfig' must be an object or null.", + "- 'overrideConfigFile' must be a non-empty string or null.", + "- 'plugins' must be an object or null.", + "- 'reportUnusedDisableDirectives' must be any of \"error\", \"warn\", \"off\", and null.", + "- 'resolvePluginsRelativeTo' must be a non-empty string or null.", + "- 'rulePaths' must be an array of non-empty strings.", + "- 'useElintrc' must be a boolean." + ].join("\n")), "u") + ); + }); + + it("should throw readable messages if 'plugins' option contains empty key", () => { + assert.throws( + () => new ESLint({ + plugins: { + "eslint-plugin-foo": {}, + "eslint-plugin-bar": {}, + "": {} + } + }), + new RegExp(escapeStringRegExp([ + "Invalid Options:", + "- 'plugins' must not include an empty string." + ].join("\n")), "u") + ); + }); + }); + + describe("lintText()", () => { + let eslint; + + it("should report the total and per file errors when using local cwd .eslintrc", async () => { + eslint = new ESLint(); + const results = await eslint.lintText("var foo = 'bar';"); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 5); + assert.strictEqual(results[0].messages[0].ruleId, "strict"); + assert.strictEqual(results[0].messages[1].ruleId, "no-var"); + assert.strictEqual(results[0].messages[2].ruleId, "no-unused-vars"); + assert.strictEqual(results[0].messages[3].ruleId, "quotes"); + assert.strictEqual(results[0].messages[4].ruleId, "eol-last"); + assert.strictEqual(results[0].fixableErrorCount, 3); + assert.strictEqual(results[0].fixableWarningCount, 0); + assert.strictEqual(results[0].usedDeprecatedRules.length, 0); + }); + + it("should report the total and per file warnings when using local cwd .eslintrc", async () => { + eslint = new ESLint({ + overrideConfig: { + rules: { + quotes: 1, + "no-var": 1, + "eol-last": 1, + strict: 1, + "no-unused-vars": 1 + } + } + }); + const results = await eslint.lintText("var foo = 'bar';"); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 5); + assert.strictEqual(results[0].messages[0].ruleId, "strict"); + assert.strictEqual(results[0].messages[1].ruleId, "no-var"); + assert.strictEqual(results[0].messages[2].ruleId, "no-unused-vars"); + assert.strictEqual(results[0].messages[3].ruleId, "quotes"); + assert.strictEqual(results[0].messages[4].ruleId, "eol-last"); + assert.strictEqual(results[0].fixableErrorCount, 0); + assert.strictEqual(results[0].fixableWarningCount, 3); + assert.strictEqual(results[0].usedDeprecatedRules.length, 0); + }); + + it("should report one message when using specific config file", async () => { + eslint = new ESLint({ + overrideConfigFile: "fixtures/configurations/quotes-error.json", + useEslintrc: false, + cwd: getFixturePath("..") + }); + const results = await eslint.lintText("var foo = 'bar';"); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "quotes"); + assert.strictEqual(results[0].messages[0].output, void 0); + assert.strictEqual(results[0].errorCount, 1); + assert.strictEqual(results[0].fixableErrorCount, 1); + assert.strictEqual(results[0].warningCount, 0); + assert.strictEqual(results[0].usedDeprecatedRules.length, 0); + }); + + it("should report the filename when passed in", async () => { + eslint = new ESLint({ + ignore: false, + cwd: getFixturePath() + }); + const options = { filePath: "test.js" }; + const results = await eslint.lintText("var foo = 'bar';", options); + + assert.strictEqual(results[0].filePath, getFixturePath("test.js")); + }); + + it("should return a warning when given a filename by --stdin-filename in excluded files list if warnIgnored is true", async () => { + eslint = new ESLint({ + ignorePath: getFixturePath(".eslintignore"), + cwd: getFixturePath("..") + }); + const options = { filePath: "fixtures/passing.js", warnIgnored: true }; + const results = await eslint.lintText("var bar = foo;", options); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, getFixturePath("passing.js")); + assert.strictEqual(results[0].messages[0].severity, 1); + assert.strictEqual(results[0].messages[0].message, "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override."); + assert.strictEqual(results[0].messages[0].output, void 0); + assert.strictEqual(results[0].errorCount, 0); + assert.strictEqual(results[0].warningCount, 1); + assert.strictEqual(results[0].fixableErrorCount, 0); + assert.strictEqual(results[0].fixableWarningCount, 0); + assert.strictEqual(results[0].usedDeprecatedRules.length, 0); + }); + + it("should not return a warning when given a filename by --stdin-filename in excluded files list if warnIgnored is false", async () => { + eslint = new ESLint({ + ignorePath: getFixturePath(".eslintignore"), + cwd: getFixturePath("..") + }); + const options = { + filePath: "fixtures/passing.js", + warnIgnored: false + }; + + // intentional parsing error + const results = await eslint.lintText("va r bar = foo;", options); + + // should not report anything because the file is ignored + assert.strictEqual(results.length, 0); + }); + + it("should suppress excluded file warnings by default", async () => { + eslint = new ESLint({ + ignorePath: getFixturePath(".eslintignore"), + cwd: getFixturePath("..") + }); + const options = { filePath: "fixtures/passing.js" }; + const results = await eslint.lintText("var bar = foo;", options); + + // should not report anything because there are no errors + assert.strictEqual(results.length, 0); + }); + + it("should return a message when given a filename by --stdin-filename in excluded files list and ignore is off", async () => { + eslint = new ESLint({ + ignorePath: "fixtures/.eslintignore", + cwd: getFixturePath(".."), + ignore: false, + useEslintrc: false, + overrideConfig: { + rules: { + "no-undef": 2 + } + } + }); + const options = { filePath: "fixtures/passing.js" }; + const results = await eslint.lintText("var bar = foo;", options); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, getFixturePath("passing.js")); + assert.strictEqual(results[0].messages[0].ruleId, "no-undef"); + assert.strictEqual(results[0].messages[0].severity, 2); + assert.strictEqual(results[0].messages[0].output, void 0); + }); + + it("should return a message and fixed text when in fix mode", async () => { + eslint = new ESLint({ + useEslintrc: false, + fix: true, + overrideConfig: { + rules: { + semi: 2 + } + }, + ignore: false, + cwd: getFixturePath() + }); + const options = { filePath: "passing.js" }; + const results = await eslint.lintText("var bar = foo", options); + + assert.deepStrictEqual(results, [ + { + filePath: getFixturePath("passing.js"), + messages: [], + errorCount: 0, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + output: "var bar = foo;", + usedDeprecatedRules: [] + } + ]); + }); + + it("correctly autofixes semicolon-conflicting-fixes", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + fix: true + }); + const inputPath = getFixturePath("autofix/semicolon-conflicting-fixes.js"); + const outputPath = getFixturePath("autofix/semicolon-conflicting-fixes.expected.js"); + const results = await eslint.lintFiles([inputPath]); + const expectedOutput = fs.readFileSync(outputPath, "utf8"); + + assert.strictEqual(results[0].output, expectedOutput); + }); + + it("correctly autofixes return-conflicting-fixes", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + fix: true + }); + const inputPath = getFixturePath("autofix/return-conflicting-fixes.js"); + const outputPath = getFixturePath("autofix/return-conflicting-fixes.expected.js"); + const results = await eslint.lintFiles([inputPath]); + const expectedOutput = fs.readFileSync(outputPath, "utf8"); + + assert.strictEqual(results[0].output, expectedOutput); + }); + + describe("Fix Types", () => { + it("should throw an error when an invalid fix type is specified", () => { + assert.throws(() => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + fix: true, + fixTypes: ["layou"] + }); + }, /'fixTypes' must be an array of any of "problem", "suggestion", and "layout"\./iu); + }); + + it("should not fix any rules when fixTypes is used without fix", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + fix: false, + fixTypes: ["layout"] + }); + const inputPath = getFixturePath("fix-types/fix-only-semi.js"); + const results = await eslint.lintFiles([inputPath]); + + assert.strictEqual(results[0].output, void 0); + }); + + it("should not fix non-style rules when fixTypes has only 'layout'", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + fix: true, + fixTypes: ["layout"] + }); + const inputPath = getFixturePath("fix-types/fix-only-semi.js"); + const outputPath = getFixturePath("fix-types/fix-only-semi.expected.js"); + const results = await eslint.lintFiles([inputPath]); + const expectedOutput = fs.readFileSync(outputPath, "utf8"); + + assert.strictEqual(results[0].output, expectedOutput); + }); + + it("should not fix style or problem rules when fixTypes has only 'suggestion'", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + fix: true, + fixTypes: ["suggestion"] + }); + const inputPath = getFixturePath("fix-types/fix-only-prefer-arrow-callback.js"); + const outputPath = getFixturePath("fix-types/fix-only-prefer-arrow-callback.expected.js"); + const results = await eslint.lintFiles([inputPath]); + const expectedOutput = fs.readFileSync(outputPath, "utf8"); + + assert.strictEqual(results[0].output, expectedOutput); + }); + + it("should fix both style and problem rules when fixTypes has 'suggestion' and 'layout'", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + fix: true, + fixTypes: ["suggestion", "layout"] + }); + const inputPath = getFixturePath("fix-types/fix-both-semi-and-prefer-arrow-callback.js"); + const outputPath = getFixturePath("fix-types/fix-both-semi-and-prefer-arrow-callback.expected.js"); + const results = await eslint.lintFiles([inputPath]); + const expectedOutput = fs.readFileSync(outputPath, "utf8"); + + assert.strictEqual(results[0].output, expectedOutput); + }); + + it("should not throw an error when a rule doesn't have a 'meta' property", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + fix: true, + fixTypes: ["layout"], + rulePaths: [getFixturePath("rules", "fix-types-test")] + }); + const inputPath = getFixturePath("fix-types/ignore-missing-meta.js"); + const outputPath = getFixturePath("fix-types/ignore-missing-meta.expected.js"); + const results = await eslint.lintFiles([inputPath]); + const expectedOutput = fs.readFileSync(outputPath, "utf8"); + + assert.strictEqual(results[0].output, expectedOutput); + }); + + it("should not throw an error when a rule is loaded after initialization with lintFiles()", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + fix: true, + fixTypes: ["layout"], + plugins: { + test: { + rules: { + "no-program": require(getFixturePath("rules", "fix-types-test", "no-program.js")) + } + } + } + }); + const inputPath = getFixturePath("fix-types/ignore-missing-meta.js"); + const outputPath = getFixturePath("fix-types/ignore-missing-meta.expected.js"); + const results = await eslint.lintFiles([inputPath]); + const expectedOutput = fs.readFileSync(outputPath, "utf8"); + + assert.strictEqual(results[0].output, expectedOutput); + }); + + it("should not throw an error when a rule is loaded after initialization with lintText()", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + fix: true, + fixTypes: ["layout"], + plugins: { + test: { + rules: { + "no-program": require(getFixturePath("rules", "fix-types-test", "no-program.js")) + } + } + } + }); + const inputPath = getFixturePath("fix-types/ignore-missing-meta.js"); + const outputPath = getFixturePath("fix-types/ignore-missing-meta.expected.js"); + const results = await eslint.lintText(fs.readFileSync(inputPath, { encoding: "utf8" }), { filePath: inputPath }); + const expectedOutput = fs.readFileSync(outputPath, "utf8"); + + assert.strictEqual(results[0].output, expectedOutput); + }); + }); + + it("should return a message and omit fixed text when in fix mode and fixes aren't done", async () => { + eslint = new ESLint({ + useEslintrc: false, + fix: true, + overrideConfig: { + rules: { + "no-undef": 2 + } + }, + ignore: false, + cwd: getFixturePath() + }); + const options = { filePath: "passing.js" }; + const results = await eslint.lintText("var bar = foo", options); + + assert.deepStrictEqual(results, [ + { + filePath: getFixturePath("passing.js"), + messages: [ + { + ruleId: "no-undef", + severity: 2, + messageId: "undef", + message: "'foo' is not defined.", + line: 1, + column: 11, + endLine: 1, + endColumn: 14, + nodeType: "Identifier" + } + ], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + source: "var bar = foo", + usedDeprecatedRules: [] + } + ]); + }); + + it("should not delete code if there is a syntax error after trying to autofix.", async () => { + eslint = eslintWithPlugins({ + useEslintrc: false, + fix: true, + overrideConfig: { + plugins: ["example"], + rules: { + "example/make-syntax-error": "error" + } + }, + ignore: false, + cwd: getFixturePath() + }); + const options = { filePath: "test.js" }; + const results = await eslint.lintText("var bar = foo", options); + + assert.deepStrictEqual(results, [ + { + filePath: getFixturePath("test.js"), + messages: [ + { + ruleId: null, + fatal: true, + severity: 2, + message: "Parsing error: Unexpected token is", + line: 1, + column: 19 + } + ], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + output: "var bar = foothis is a syntax error.", + usedDeprecatedRules: [] + } + ]); + }); + + it("should not crash even if there are any syntax error since the first time.", async () => { + eslint = new ESLint({ + useEslintrc: false, + fix: true, + overrideConfig: { + rules: { + "example/make-syntax-error": "error" + } + }, + ignore: false, + cwd: getFixturePath() + }); + const options = { filePath: "test.js" }; + const results = await eslint.lintText("var bar =", options); + + assert.deepStrictEqual(results, [ + { + filePath: getFixturePath("test.js"), + messages: [ + { + ruleId: null, + fatal: true, + severity: 2, + message: "Parsing error: Unexpected token", + line: 1, + column: 10 + } + ], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + source: "var bar =", + usedDeprecatedRules: [] + } + ]); + }); + + it("should return source code of file in `source` property when errors are present", async () => { + eslint = new ESLint({ + useEslintrc: false, + overrideConfig: { + rules: { semi: 2 } + } + }); + const results = await eslint.lintText("var foo = 'bar'"); + + assert.strictEqual(results[0].source, "var foo = 'bar'"); + }); + + it("should return source code of file in `source` property when warnings are present", async () => { + eslint = new ESLint({ + useEslintrc: false, + overrideConfig: { + rules: { semi: 1 } + } + }); + const results = await eslint.lintText("var foo = 'bar'"); + + assert.strictEqual(results[0].source, "var foo = 'bar'"); + }); + + + it("should not return a `source` property when no errors or warnings are present", async () => { + eslint = new ESLint({ + useEslintrc: false, + overrideConfig: { + rules: { semi: 2 } + } + }); + const results = await eslint.lintText("var foo = 'bar';"); + + assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[0].source, void 0); + }); + + it("should not return a `source` property when fixes are applied", async () => { + eslint = new ESLint({ + useEslintrc: false, + fix: true, + overrideConfig: { + rules: { + semi: 2, + "no-unused-vars": 2 + } + } + }); + const results = await eslint.lintText("var msg = 'hi' + foo\n"); + + assert.strictEqual(results[0].source, void 0); + assert.strictEqual(results[0].output, "var msg = 'hi' + foo;\n"); + }); + + it("should return a `source` property when a parsing error has occurred", async () => { + eslint = new ESLint({ + useEslintrc: false, + overrideConfig: { + rules: { semi: 2 } + } + }); + const results = await eslint.lintText("var bar = foothis is a syntax error.\n return bar;"); + + assert.deepStrictEqual(results, [ + { + filePath: "", + messages: [ + { + ruleId: null, + fatal: true, + severity: 2, + message: "Parsing error: Unexpected token is", + line: 1, + column: 19 + } + ], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + source: "var bar = foothis is a syntax error.\n return bar;", + usedDeprecatedRules: [] + } + ]); + }); + + // https://github.com/eslint/eslint/issues/5547 + it("should respect default ignore rules, even with --no-ignore", async () => { + eslint = new ESLint({ + cwd: getFixturePath(), + ignore: false + }); + const results = await eslint.lintText("var bar = foo;", { filePath: "node_modules/passing.js", warnIgnored: true }); + const expectedMsg = "File ignored by default. Use \"--ignore-pattern '!node_modules/*'\" to override."; + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, getFixturePath("node_modules/passing.js")); + assert.strictEqual(results[0].messages[0].message, expectedMsg); + }); + + describe('plugin shorthand notation ("@scope" for "@scope/eslint-plugin")', () => { + const Module = require("module"); + let originalFindPath = null; + + /* eslint-disable no-underscore-dangle */ + before(() => { + originalFindPath = Module._findPath; + Module._findPath = function(id, ...otherArgs) { + if (id === "@scope/eslint-plugin") { + return path.resolve(__dirname, "../../fixtures/plugin-shorthand/basic/node_modules/@scope/eslint-plugin/index.js"); + } + return originalFindPath.call(this, id, ...otherArgs); + }; + }); + after(() => { + Module._findPath = originalFindPath; + }); + /* eslint-enable no-underscore-dangle */ + + it("should resolve 'plugins:[\"@scope\"]' to 'node_modules/@scope/eslint-plugin'.", async () => { + eslint = new ESLint({ cwd: getFixturePath("plugin-shorthand/basic") }); + const [result] = await eslint.lintText("var x = 0", { filePath: "index.js" }); + + assert.strictEqual(result.filePath, getFixturePath("plugin-shorthand/basic/index.js")); + assert.strictEqual(result.messages[0].ruleId, "@scope/rule"); + assert.strictEqual(result.messages[0].message, "OK"); + }); + + it("should resolve 'extends:[\"plugin:@scope/recommended\"]' to 'node_modules/@scope/eslint-plugin'.", async () => { + eslint = new ESLint({ cwd: getFixturePath("plugin-shorthand/extends") }); + const [result] = await eslint.lintText("var x = 0", { filePath: "index.js" }); + + assert.strictEqual(result.filePath, getFixturePath("plugin-shorthand/extends/index.js")); + assert.strictEqual(result.messages[0].ruleId, "@scope/rule"); + assert.strictEqual(result.messages[0].message, "OK"); + }); + }); + + it("should warn when deprecated rules are found in a config", async () => { + eslint = new ESLint({ + cwd: originalDir, + useEslintrc: false, + overrideConfigFile: "tests/fixtures/cli-engine/deprecated-rule-config/.eslintrc.yml" + }); + const [result] = await eslint.lintText("foo"); + + assert.deepStrictEqual( + result.usedDeprecatedRules, + [{ ruleId: "indent-legacy", replacedBy: ["indent"] }] + ); + }); + + it("should throw if non-string value is given to 'code' parameter", async () => { + eslint = new ESLint(); + await assert.rejects(() => eslint.lintText(100), /'code' must be a string/u); + }); + + it("should throw if non-object value is given to 'options' parameter", async () => { + eslint = new ESLint(); + await assert.rejects(() => eslint.lintText("var a = 0", "foo.js"), /'options' must be an object, null, or undefined/u); + }); + + it("should throw if 'options' argument contains unknown key", async () => { + eslint = new ESLint(); + await assert.rejects(() => eslint.lintText("var a = 0", { filename: "foo.js" }), /'options' must not include the unknown option 'filename'/u); + }); + + it("should throw if non-string value is given to 'options.filePath' option", async () => { + eslint = new ESLint(); + await assert.rejects(() => eslint.lintText("var a = 0", { filePath: "" }), /'options.filePath' must be a non-empty string or undefined/u); + }); + + it("should throw if non-boolean value is given to 'options.warnIgnored' option", async () => { + eslint = new ESLint(); + await assert.rejects(() => eslint.lintText("var a = 0", { warnIgnored: "" }), /'options.warnIgnored' must be a boolean or undefined/u); + }); + }); + + describe("lintFiles()", () => { + + /** @type {InstanceType} */ + let eslint; + + it("should use correct parser when custom parser is specified", async () => { + eslint = new ESLint({ + cwd: originalDir, + ignore: false + }); + const filePath = path.resolve(__dirname, "../../fixtures/configurations/parser/custom.js"); + const results = await eslint.lintFiles([filePath]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].message, "Parsing error: Boom!"); + }); + + it("should report zero messages when given a config file and a valid file", async () => { + eslint = new ESLint({ + cwd: originalDir, + overrideConfigFile: ".eslintrc.js" + }); + const results = await eslint.lintFiles(["lib/**/cli*.js"]); + + assert.strictEqual(results.length, 2); + assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[1].messages.length, 0); + }); + + it("should handle multiple patterns with overlapping files", async () => { + eslint = new ESLint({ + cwd: originalDir, + overrideConfigFile: ".eslintrc.js" + }); + const results = await eslint.lintFiles(["lib/**/cli*.js", "lib/cli.?s", "lib/{cli,cli-engine/cli-engine}.js"]); + + assert.strictEqual(results.length, 2); + assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[1].messages.length, 0); + }); + + it("should report zero messages when given a config file and a valid file and espree as parser", async () => { + eslint = new ESLint({ + overrideConfig: { + parser: "espree", + parserOptions: { + ecmaVersion: 2020 + } + }, + useEslintrc: false + }); + const results = await eslint.lintFiles(["lib/cli.js"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should report zero messages when given a config file and a valid file and esprima as parser", async () => { + eslint = new ESLint({ + overrideConfig: { + parser: "esprima" + }, + useEslintrc: false, + ignore: false + }); + const results = await eslint.lintFiles(["tests/fixtures/passing.js"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should throw an error when given a config file and a valid file and invalid parser", async () => { + eslint = new ESLint({ + overrideConfig: { + parser: "test11" + }, + useEslintrc: false + }); + + await assert.rejects(async () => await eslint.lintFiles(["lib/cli.js"]), /Cannot find module 'test11'/u); + }); + + it("should report zero messages when given a directory with a .js2 file", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + extensions: [".js2"] + }); + const results = await eslint.lintFiles([getFixturePath("files/foo.js2")]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should fall back to defaults when extensions is set to an empty array", async () => { + eslint = new ESLint({ + cwd: getFixturePath("configurations"), + overrideConfigFile: getFixturePath("configurations", "quotes-error.json"), + extensions: [] + }); + const results = await eslint.lintFiles([getFixturePath("single-quoted.js")]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "quotes"); + assert.strictEqual(results[0].messages[0].severity, 2); + assert.strictEqual(results[0].errorCount, 1); + assert.strictEqual(results[0].warningCount, 0); + assert.strictEqual(results[0].fixableErrorCount, 1); + assert.strictEqual(results[0].fixableWarningCount, 0); + }); + + it("should report zero messages when given a directory with a .js and a .js2 file", async () => { + eslint = new ESLint({ + extensions: [".js", ".js2"], + ignore: false, + cwd: getFixturePath("..") + }); + const results = await eslint.lintFiles(["fixtures/files/"]); + + assert.strictEqual(results.length, 2); + assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[1].messages.length, 0); + }); + + it("should report zero messages when given a '**' pattern with a .js and a .js2 file", async () => { + eslint = new ESLint({ + extensions: [".js", ".js2"], + ignore: false, + cwd: path.join(fixtureDir, "..") + }); + const results = await eslint.lintFiles(["fixtures/files/*"]); + + assert.strictEqual(results.length, 2); + assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[1].messages.length, 0); + }); + + it("should resolve globs when 'globInputPaths' option is true", async () => { + eslint = new ESLint({ + extensions: [".js", ".js2"], + ignore: false, + cwd: getFixturePath("..") + }); + const results = await eslint.lintFiles(["fixtures/files/*"]); + + assert.strictEqual(results.length, 2); + assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[1].messages.length, 0); + }); + + it("should not resolve globs when 'globInputPaths' option is false", async () => { + eslint = new ESLint({ + extensions: [".js", ".js2"], + ignore: false, + cwd: getFixturePath(".."), + globInputPaths: false + }); + + await assert.rejects(async () => { + await eslint.lintFiles(["fixtures/files/*"]); + }, /No files matching 'fixtures\/files\/\*' were found \(glob was disabled\)\./u); + }); + + it("should report on all files passed explicitly, even if ignored by default", async () => { + eslint = new ESLint({ + cwd: getFixturePath("cli-engine") + }); + const results = await eslint.lintFiles(["node_modules/foo.js"]); + const expectedMsg = "File ignored by default. Use \"--ignore-pattern '!node_modules/*'\" to override."; + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].errorCount, 0); + assert.strictEqual(results[0].warningCount, 1); + assert.strictEqual(results[0].fixableErrorCount, 0); + assert.strictEqual(results[0].fixableWarningCount, 0); + assert.strictEqual(results[0].messages[0].message, expectedMsg); + }); + + it("should report on globs with explicit inclusion of dotfiles, even though ignored by default", async () => { + eslint = new ESLint({ + cwd: getFixturePath("cli-engine"), + overrideConfig: { + rules: { + quotes: [2, "single"] + } + } + }); + const results = await eslint.lintFiles(["hidden/.hiddenfolder/*.js"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].errorCount, 1); + assert.strictEqual(results[0].warningCount, 0); + assert.strictEqual(results[0].fixableErrorCount, 1); + assert.strictEqual(results[0].fixableWarningCount, 0); + }); + + it("should not check default ignored files without --no-ignore flag", async () => { + eslint = new ESLint({ + cwd: getFixturePath("cli-engine") + }); + + await assert.rejects(async () => { + await eslint.lintFiles(["node_modules"]); + }, /All files matched by 'node_modules' are ignored\./u); + }); + + // https://github.com/eslint/eslint/issues/5547 + it("should not check node_modules files even with --no-ignore flag", async () => { + eslint = new ESLint({ + cwd: getFixturePath("cli-engine"), + ignore: false + }); + + await assert.rejects(async () => { + await eslint.lintFiles(["node_modules"]); + }, /All files matched by 'node_modules' are ignored\./u); + }); + + it("should not check .hidden files if they are passed explicitly without --no-ignore flag", async () => { + eslint = new ESLint({ + cwd: getFixturePath(".."), + useEslintrc: false, + overrideConfig: { + rules: { + quotes: [2, "single"] + } + } + }); + const results = await eslint.lintFiles(["fixtures/files/.bar.js"]); + const expectedMsg = "File ignored by default. Use a negated ignore pattern (like \"--ignore-pattern '!'\") to override."; + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].errorCount, 0); + assert.strictEqual(results[0].warningCount, 1); + assert.strictEqual(results[0].fixableErrorCount, 0); + assert.strictEqual(results[0].fixableWarningCount, 0); + assert.strictEqual(results[0].messages[0].message, expectedMsg); + }); + + // https://github.com/eslint/eslint/issues/12873 + it("should not check files within a .hidden folder if they are passed explicitly without the --no-ignore flag", async () => { + eslint = new ESLint({ + cwd: getFixturePath("cli-engine"), + useEslintrc: false, + overrideConfig: { + rules: { + quotes: [2, "single"] + } + } + }); + const results = await eslint.lintFiles(["hidden/.hiddenfolder/double-quotes.js"]); + const expectedMsg = "File ignored by default. Use a negated ignore pattern (like \"--ignore-pattern '!'\") to override."; + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].errorCount, 0); + assert.strictEqual(results[0].warningCount, 1); + assert.strictEqual(results[0].fixableErrorCount, 0); + assert.strictEqual(results[0].fixableWarningCount, 0); + assert.strictEqual(results[0].messages[0].message, expectedMsg); + }); + + it("should check .hidden files if they are passed explicitly with --no-ignore flag", async () => { + eslint = new ESLint({ + cwd: getFixturePath(".."), + ignore: false, + useEslintrc: false, + overrideConfig: { + rules: { + quotes: [2, "single"] + } + } + }); + const results = await eslint.lintFiles(["fixtures/files/.bar.js"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].warningCount, 0); + assert.strictEqual(results[0].errorCount, 1); + assert.strictEqual(results[0].fixableErrorCount, 1); + assert.strictEqual(results[0].fixableWarningCount, 0); + assert.strictEqual(results[0].messages[0].ruleId, "quotes"); + }); + + it("should check .hidden files if they are unignored with an --ignore-pattern", async () => { + eslint = new ESLint({ + cwd: getFixturePath("cli-engine"), + ignore: true, + useEslintrc: false, + overrideConfig: { + ignorePatterns: "!.hidden*", + rules: { + quotes: [2, "single"] + } + } + }); + const results = await eslint.lintFiles(["hidden/"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].warningCount, 0); + assert.strictEqual(results[0].errorCount, 1); + assert.strictEqual(results[0].fixableErrorCount, 1); + assert.strictEqual(results[0].fixableWarningCount, 0); + assert.strictEqual(results[0].messages[0].ruleId, "quotes"); + }); + + it("should report zero messages when given a pattern with a .js and a .js2 file", async () => { + eslint = new ESLint({ + extensions: [".js", ".js2"], + ignore: false, + cwd: path.join(fixtureDir, "..") + }); + const results = await eslint.lintFiles(["fixtures/files/*.?s*"]); + + assert.strictEqual(results.length, 2); + assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[1].messages.length, 0); + }); + + it("should return one error message when given a config with rules with options and severity level set to error", async () => { + eslint = new ESLint({ + cwd: getFixturePath("configurations"), + overrideConfigFile: getFixturePath("configurations", "quotes-error.json") + }); + const results = await eslint.lintFiles([getFixturePath("single-quoted.js")]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "quotes"); + assert.strictEqual(results[0].messages[0].severity, 2); + assert.strictEqual(results[0].errorCount, 1); + assert.strictEqual(results[0].warningCount, 0); + assert.strictEqual(results[0].fixableErrorCount, 1); + assert.strictEqual(results[0].fixableWarningCount, 0); + }); + + it("should return 3 messages when given a config file and a directory of 3 valid files", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + overrideConfigFile: getFixturePath("configurations", "semi-error.json") + }); + const results = await eslint.lintFiles([getFixturePath("formatters")]); + + assert.strictEqual(results.length, 3); + assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[1].messages.length, 0); + assert.strictEqual(results[2].messages.length, 0); + assert.strictEqual(results[0].errorCount, 0); + assert.strictEqual(results[0].warningCount, 0); + assert.strictEqual(results[0].fixableErrorCount, 0); + assert.strictEqual(results[0].fixableWarningCount, 0); + assert.strictEqual(results[1].errorCount, 0); + assert.strictEqual(results[1].warningCount, 0); + assert.strictEqual(results[1].fixableErrorCount, 0); + assert.strictEqual(results[1].fixableWarningCount, 0); + assert.strictEqual(results[2].errorCount, 0); + assert.strictEqual(results[2].warningCount, 0); + assert.strictEqual(results[2].fixableErrorCount, 0); + assert.strictEqual(results[2].fixableWarningCount, 0); + }); + + it("should process when file is given by not specifying extensions", async () => { + eslint = new ESLint({ + ignore: false, + cwd: path.join(fixtureDir, "..") + }); + const results = await eslint.lintFiles(["fixtures/files/foo.js2"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should return zero messages when given a config with environment set to browser", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + overrideConfigFile: getFixturePath("configurations", "env-browser.json") + }); + const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("globals-browser.js"))]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should return zero messages when given an option to set environment to browser", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + overrideConfig: { + env: { browser: true }, + rules: { + "no-alert": 0, + "no-undef": 2 + } + } + }); + const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("globals-browser.js"))]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should return zero messages when given a config with environment set to Node.js", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + overrideConfigFile: getFixturePath("configurations", "env-node.json") + }); + const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("globals-node.js"))]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should not return results from previous call when calling more than once", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + ignore: false, + overrideConfig: { + rules: { + semi: 2 + } + } + }); + const failFilePath = fs.realpathSync(getFixturePath("missing-semicolon.js")); + const passFilePath = fs.realpathSync(getFixturePath("passing.js")); + + let results = await eslint.lintFiles([failFilePath]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, failFilePath); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "semi"); + assert.strictEqual(results[0].messages[0].severity, 2); + + results = await eslint.lintFiles([passFilePath]); + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, passFilePath); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should throw an error when given a directory with all eslint excluded files in the directory", async () => { + eslint = new ESLint({ + ignorePath: getFixturePath(".eslintignore") + }); + + await assert.rejects(async () => { + await eslint.lintFiles([getFixturePath("./cli-engine/")]); + }, new RegExp(escapeStringRegExp(`All files matched by '${getFixturePath("./cli-engine/")}' are ignored.`), "u")); + }); + + it("should throw an error when all given files are ignored", async () => { + await assert.rejects(async () => { + await eslint.lintFiles(["tests/fixtures/cli-engine/"]); + }, /All files matched by 'tests\/fixtures\/cli-engine\/' are ignored\./u); + }); + + it("should throw an error when all given files are ignored even with a `./` prefix", async () => { + eslint = new ESLint({ + ignorePath: getFixturePath(".eslintignore") + }); + + await assert.rejects(async () => { + await eslint.lintFiles(["./tests/fixtures/cli-engine/"]); + }, /All files matched by '\.\/tests\/fixtures\/cli-engine\/' are ignored\./u); + }); + + // https://github.com/eslint/eslint/issues/3788 + it("should ignore one-level down node_modules when ignore file has 'node_modules/' in it", async () => { + eslint = new ESLint({ + ignorePath: getFixturePath("cli-engine", "nested_node_modules", ".eslintignore"), + useEslintrc: false, + overrideConfig: { + rules: { + quotes: [2, "double"] + } + }, + cwd: getFixturePath("cli-engine", "nested_node_modules") + }); + const results = await eslint.lintFiles(["."]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].errorCount, 0); + assert.strictEqual(results[0].warningCount, 0); + assert.strictEqual(results[0].fixableErrorCount, 0); + assert.strictEqual(results[0].fixableWarningCount, 0); + }); + + // https://github.com/eslint/eslint/issues/3812 + it("should ignore all files and throw an error when tests/fixtures/ is in ignore file", async () => { + eslint = new ESLint({ + ignorePath: getFixturePath("cli-engine/.eslintignore2"), + useEslintrc: false, + overrideConfig: { + rules: { + quotes: [2, "double"] + } + } + }); + + await assert.rejects(async () => { + await eslint.lintFiles(["./tests/fixtures/cli-engine/"]); + }, /All files matched by '\.\/tests\/fixtures\/cli-engine\/' are ignored\./u); + }); + + it("should throw an error when all given files are ignored via ignore-pattern", async () => { + eslint = new ESLint({ + overrideConfig: { + ignorePatterns: "tests/fixtures/single-quoted.js" + } + }); + + await assert.rejects(async () => { + await eslint.lintFiles(["tests/fixtures/*-quoted.js"]); + }, /All files matched by 'tests\/fixtures\/\*-quoted\.js' are ignored\./u); + }); + + it("should return a warning when an explicitly given file is ignored", async () => { + eslint = new ESLint({ + ignorePath: getFixturePath(".eslintignore"), + cwd: getFixturePath() + }); + const filePath = getFixturePath("passing.js"); + const results = await eslint.lintFiles([filePath]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, filePath); + assert.strictEqual(results[0].messages[0].severity, 1); + assert.strictEqual(results[0].messages[0].message, "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to override."); + assert.strictEqual(results[0].errorCount, 0); + assert.strictEqual(results[0].warningCount, 1); + assert.strictEqual(results[0].fixableErrorCount, 0); + assert.strictEqual(results[0].fixableWarningCount, 0); + }); + + it("should return two messages when given a file in excluded files list while ignore is off", async () => { + eslint = new ESLint({ + ignorePath: getFixturePath(".eslintignore"), + ignore: false, + overrideConfig: { + rules: { + "no-undef": 2 + } + } + }); + const filePath = fs.realpathSync(getFixturePath("undef.js")); + const results = await eslint.lintFiles([filePath]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, filePath); + assert.strictEqual(results[0].messages[0].ruleId, "no-undef"); + assert.strictEqual(results[0].messages[0].severity, 2); + assert.strictEqual(results[0].messages[1].ruleId, "no-undef"); + assert.strictEqual(results[0].messages[1].severity, 2); + }); + + it("should return zero messages when executing a file with a shebang", async () => { + eslint = new ESLint({ + ignore: false + }); + const results = await eslint.lintFiles([getFixturePath("shebang.js")]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should give a warning when loading a custom rule that doesn't exist", async () => { + eslint = new ESLint({ + ignore: false, + rulePaths: [getFixturePath("rules", "dir1")], + overrideConfigFile: getFixturePath("rules", "missing-rule.json") + }); + const results = await eslint.lintFiles([getFixturePath("rules", "test", "test-custom-rule.js")]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "missing-rule"); + assert.strictEqual(results[0].messages[0].severity, 2); + assert.strictEqual(results[0].messages[0].message, "Definition for rule 'missing-rule' was not found."); + }); + + it("should throw an error when loading a bad custom rule", async () => { + eslint = new ESLint({ + ignore: false, + rulePaths: [getFixturePath("rules", "wrong")], + overrideConfigFile: getFixturePath("rules", "eslint.json") + }); + + + await assert.rejects(async () => { + await eslint.lintFiles([getFixturePath("rules", "test", "test-custom-rule.js")]); + }, /Error while loading rule 'custom-rule'/u); + }); + + it("should return one message when a custom rule matches a file", async () => { + eslint = new ESLint({ + ignore: false, + useEslintrc: false, + rulePaths: [getFixturePath("rules/")], + overrideConfigFile: getFixturePath("rules", "eslint.json") + }); + const filePath = fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js")); + const results = await eslint.lintFiles([filePath]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, filePath); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "custom-rule"); + assert.strictEqual(results[0].messages[0].severity, 1); + }); + + it("should load custom rule from the provided cwd", async () => { + const cwd = path.resolve(getFixturePath("rules")); + + eslint = new ESLint({ + ignore: false, + cwd, + rulePaths: ["./"], + overrideConfigFile: "eslint.json" + }); + const filePath = fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js")); + const results = await eslint.lintFiles([filePath]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, filePath); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "custom-rule"); + assert.strictEqual(results[0].messages[0].severity, 1); + }); + + it("should return messages when multiple custom rules match a file", async () => { + eslint = new ESLint({ + ignore: false, + rulePaths: [ + getFixturePath("rules", "dir1"), + getFixturePath("rules", "dir2") + ], + overrideConfigFile: getFixturePath("rules", "multi-rulesdirs.json") + }); + const filePath = fs.realpathSync(getFixturePath("rules", "test-multi-rulesdirs.js")); + const results = await eslint.lintFiles([filePath]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, filePath); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "no-literals"); + assert.strictEqual(results[0].messages[0].severity, 2); + assert.strictEqual(results[0].messages[1].ruleId, "no-strings"); + assert.strictEqual(results[0].messages[1].severity, 2); + }); + + it("should return zero messages when executing without useEslintrc flag", async () => { + eslint = new ESLint({ + ignore: false, + useEslintrc: false + }); + const filePath = fs.realpathSync(getFixturePath("missing-semicolon.js")); + const results = await eslint.lintFiles([filePath]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, filePath); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should return zero messages when executing without useEslintrc flag in Node.js environment", async () => { + eslint = new ESLint({ + ignore: false, + useEslintrc: false, + overrideConfig: { + env: { node: true } + } + }); + const filePath = fs.realpathSync(getFixturePath("process-exit.js")); + const results = await eslint.lintFiles([filePath]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, filePath); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should return zero messages and ignore .eslintrc files when executing with no-eslintrc flag", async () => { + eslint = new ESLint({ + ignore: false, + useEslintrc: false, + overrideConfig: { + env: { node: true } + } + }); + const filePath = fs.realpathSync(getFixturePath("eslintrc", "quotes.js")); + const results = await eslint.lintFiles([filePath]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, filePath); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should return zero messages and ignore package.json files when executing with no-eslintrc flag", async () => { + eslint = new ESLint({ + ignore: false, + useEslintrc: false, + overrideConfig: { + env: { node: true } + } + }); + const filePath = fs.realpathSync(getFixturePath("packagejson", "quotes.js")); + const results = await eslint.lintFiles([filePath]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].filePath, filePath); + assert.strictEqual(results[0].messages.length, 0); + }); + + it("should warn when deprecated rules are configured", async () => { + eslint = new ESLint({ + cwd: originalDir, + overrideConfigFile: ".eslintrc.js", + overrideConfig: { + rules: { + "indent-legacy": 1, + "require-jsdoc": 1, + "valid-jsdoc": 1 + } + } + }); + const results = await eslint.lintFiles(["lib/cli*.js"]); + + assert.deepStrictEqual( + results[0].usedDeprecatedRules, + [ + { ruleId: "indent-legacy", replacedBy: ["indent"] }, + { ruleId: "require-jsdoc", replacedBy: [] }, + { ruleId: "valid-jsdoc", replacedBy: [] } + ] + ); + }); + + it("should not warn when deprecated rules are not configured", async () => { + eslint = new ESLint({ + cwd: originalDir, + overrideConfigFile: ".eslintrc.js", + overrideConfig: { + rules: { indent: 1, "valid-jsdoc": 0, "require-jsdoc": 0 } + } + }); + const results = await eslint.lintFiles(["lib/cli*.js"]); + + assert.deepStrictEqual(results[0].usedDeprecatedRules, []); + }); + + it("should warn when deprecated rules are found in a config", async () => { + eslint = new ESLint({ + cwd: originalDir, + overrideConfigFile: "tests/fixtures/cli-engine/deprecated-rule-config/.eslintrc.yml", + useEslintrc: false + }); + const results = await eslint.lintFiles(["lib/cli*.js"]); + + assert.deepStrictEqual( + results[0].usedDeprecatedRules, + [{ ruleId: "indent-legacy", replacedBy: ["indent"] }] + ); + }); + + describe("Fix Mode", () => { + it("should return fixed text on multiple files when in fix mode", async () => { + + /** + * Converts CRLF to LF in output. + * This is a workaround for git's autocrlf option on Windows. + * @param {Object} result A result object to convert. + * @returns {void} + */ + function convertCRLF(result) { + if (result && result.output) { + result.output = result.output.replace(/\r\n/gu, "\n"); + } + } + + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + fix: true, + overrideConfig: { + rules: { + semi: 2, + quotes: [2, "double"], + eqeqeq: 2, + "no-undef": 2, + "space-infix-ops": 2 + } + } + }); + const results = await eslint.lintFiles([path.resolve(fixtureDir, `${fixtureDir}/fixmode`)]); + + results.forEach(convertCRLF); + assert.deepStrictEqual(results, [ + { + filePath: fs.realpathSync(path.resolve(fixtureDir, "fixmode/multipass.js")), + messages: [], + errorCount: 0, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + output: "true ? \"yes\" : \"no\";\n", + usedDeprecatedRules: [] + }, + { + filePath: fs.realpathSync(path.resolve(fixtureDir, "fixmode/ok.js")), + messages: [], + errorCount: 0, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + usedDeprecatedRules: [] + }, + { + filePath: fs.realpathSync(path.resolve(fixtureDir, "fixmode/quotes-semi-eqeqeq.js")), + messages: [ + { + column: 9, + line: 2, + endColumn: 11, + endLine: 2, + message: "Expected '===' and instead saw '=='.", + messageId: "unexpected", + nodeType: "BinaryExpression", + ruleId: "eqeqeq", + severity: 2 + } + ], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + output: "var msg = \"hi\";\nif (msg == \"hi\") {\n\n}\n", + usedDeprecatedRules: [] + }, + { + filePath: fs.realpathSync(path.resolve(fixtureDir, "fixmode/quotes.js")), + messages: [ + { + column: 18, + line: 1, + endColumn: 21, + endLine: 1, + messageId: "undef", + message: "'foo' is not defined.", + nodeType: "Identifier", + ruleId: "no-undef", + severity: 2 + } + ], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + output: "var msg = \"hi\" + foo;\n", + usedDeprecatedRules: [] + } + ]); + }); + + it("should run autofix even if files are cached without autofix results", async () => { + const baseOptions = { + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + overrideConfig: { + rules: { + semi: 2, + quotes: [2, "double"], + eqeqeq: 2, + "no-undef": 2, + "space-infix-ops": 2 + } + } + }; + + eslint = new ESLint(Object.assign({}, baseOptions, { cache: true, fix: false })); + + // Do initial lint run and populate the cache file + await eslint.lintFiles([path.resolve(fixtureDir, `${fixtureDir}/fixmode`)]); + + eslint = new ESLint(Object.assign({}, baseOptions, { cache: true, fix: true })); + const results = await eslint.lintFiles([path.resolve(fixtureDir, `${fixtureDir}/fixmode`)]); + + assert(results.some(result => result.output)); + }); + }); + + // These tests have to do with https://github.com/eslint/eslint/issues/963 + + describe("configuration hierarchy", () => { + + // Default configuration - blank + it("should return zero messages when executing with no .eslintrc", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/console-wrong-quotes.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + // No default configuration rules - conf/environments.js (/*eslint-env node*/) + it("should return zero messages when executing with no .eslintrc in the Node.js environment", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/console-wrong-quotes-node.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + // Project configuration - first level .eslintrc + it("should return zero messages when executing with .eslintrc in the Node.js environment", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, "..") + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/process-exit.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + // Project configuration - first level .eslintrc + it("should return zero messages when executing with .eslintrc in the Node.js environment", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, "..") + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/process-exit.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + // Project configuration - first level .eslintrc + it("should return one message when executing with .eslintrc", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, "..") + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/console-wrong-quotes.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "quotes"); + assert.strictEqual(results[0].messages[0].severity, 2); + }); + + // Project configuration - second level .eslintrc + it("should return one message when executing with local .eslintrc that overrides parent .eslintrc", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, "..") + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/subbroken/console-wrong-quotes.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "no-console"); + assert.strictEqual(results[0].messages[0].severity, 1); + }); + + // Project configuration - third level .eslintrc + it("should return one message when executing with local .eslintrc that overrides parent and grandparent .eslintrc", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, "..") + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/subbroken/subsubbroken/console-wrong-quotes.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "quotes"); + assert.strictEqual(results[0].messages[0].severity, 1); + }); + + // Project configuration - first level package.json + it("should return one message when executing with package.json", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, "..") + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/packagejson/subdir/wrong-quotes.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "quotes"); + assert.strictEqual(results[0].messages[0].severity, 1); + }); + + // Project configuration - second level package.json + it("should return zero messages when executing with local package.json that overrides parent package.json", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, "..") + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/packagejson/subdir/subsubdir/wrong-quotes.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + // Project configuration - third level package.json + it("should return one message when executing with local package.json that overrides parent and grandparent package.json", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, "..") + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/packagejson/subdir/subsubdir/subsubsubdir/wrong-quotes.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "quotes"); + assert.strictEqual(results[0].messages[0].severity, 2); + }); + + // Project configuration - .eslintrc overrides package.json in same directory + it("should return one message when executing with .eslintrc that overrides a package.json in the same directory", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, "..") + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/packagejson/wrong-quotes.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "quotes"); + assert.strictEqual(results[0].messages[0].severity, 2); + }); + + // Command line configuration - --config with first level .eslintrc + it("should return two messages when executing with config file that adds to local .eslintrc", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + overrideConfigFile: `${fixtureDir}/config-hierarchy/broken/add-conf.yaml` + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/console-wrong-quotes.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "quotes"); + assert.strictEqual(results[0].messages[0].severity, 2); + assert.strictEqual(results[0].messages[1].ruleId, "semi"); + assert.strictEqual(results[0].messages[1].severity, 1); + }); + + // Command line configuration - --config with first level .eslintrc + it("should return no messages when executing with config file that overrides local .eslintrc", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + overrideConfigFile: `${fixtureDir}/config-hierarchy/broken/override-conf.yaml` + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/console-wrong-quotes.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + // Command line configuration - --config with second level .eslintrc + it("should return two messages when executing with config file that adds to local and parent .eslintrc", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + overrideConfigFile: `${fixtureDir}/config-hierarchy/broken/add-conf.yaml` + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/subbroken/console-wrong-quotes.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "no-console"); + assert.strictEqual(results[0].messages[0].severity, 1); + assert.strictEqual(results[0].messages[1].ruleId, "semi"); + assert.strictEqual(results[0].messages[1].severity, 1); + }); + + // Command line configuration - --config with second level .eslintrc + it("should return one message when executing with config file that overrides local and parent .eslintrc", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + overrideConfigFile: getFixturePath("config-hierarchy/broken/override-conf.yaml") + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/subbroken/console-wrong-quotes.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "no-console"); + assert.strictEqual(results[0].messages[0].severity, 1); + }); + + // Command line configuration - --config with first level .eslintrc + it("should return no messages when executing with config file that overrides local .eslintrc", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + overrideConfigFile: `${fixtureDir}/config-hierarchy/broken/override-conf.yaml` + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/console-wrong-quotes.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + }); + + // Command line configuration - --rule with --config and first level .eslintrc + it("should return one message when executing with command line rule and config file that overrides local .eslintrc", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + overrideConfigFile: getFixturePath("config-hierarchy/broken/override-conf.yaml"), + overrideConfig: { + rules: { + quotes: [1, "double"] + } + } + }); + const results = await eslint.lintFiles([fs.realpathSync(`${fixtureDir}/config-hierarchy/broken/console-wrong-quotes.js`)]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "quotes"); + assert.strictEqual(results[0].messages[0].severity, 1); + }); + + // Command line configuration - --rule with --config and first level .eslintrc + it("should return one message when executing with command line rule and config file that overrides local .eslintrc", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + overrideConfigFile: getFixturePath("/config-hierarchy/broken/override-conf.yaml"), + overrideConfig: { + rules: { + quotes: [1, "double"] + } + } + }); + const results = await eslint.lintFiles([getFixturePath("config-hierarchy/broken/console-wrong-quotes.js")]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "quotes"); + assert.strictEqual(results[0].messages[0].severity, 1); + }); + }); + + describe("plugins", () => { + it("should return two messages when executing with config file that specifies a plugin", async () => { + eslint = eslintWithPlugins({ + cwd: path.join(fixtureDir, ".."), + overrideConfigFile: getFixturePath("configurations", "plugins-with-prefix.json"), + useEslintrc: false + }); + const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test/test-custom-rule.js"))]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "example/example-rule"); + }); + + it("should return two messages when executing with config file that specifies a plugin with namespace", async () => { + eslint = eslintWithPlugins({ + cwd: path.join(fixtureDir, ".."), + overrideConfigFile: getFixturePath("configurations", "plugins-with-prefix-and-namespace.json"), + useEslintrc: false + }); + const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "@eslint/example/example-rule"); + }); + + it("should return two messages when executing with config file that specifies a plugin without prefix", async () => { + eslint = eslintWithPlugins({ + cwd: path.join(fixtureDir, ".."), + overrideConfigFile: getFixturePath("configurations", "plugins-without-prefix.json"), + useEslintrc: false + }); + const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "example/example-rule"); + }); + + it("should return two messages when executing with config file that specifies a plugin without prefix and with namespace", async () => { + eslint = eslintWithPlugins({ + cwd: path.join(fixtureDir, ".."), + overrideConfigFile: getFixturePath("configurations", "plugins-without-prefix-with-namespace.json"), + useEslintrc: false + }); + const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "@eslint/example/example-rule"); + }); + + it("should return two messages when executing with cli option that specifies a plugin", async () => { + eslint = eslintWithPlugins({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + overrideConfig: { + plugins: ["example"], + rules: { "example/example-rule": 1 } + } + }); + const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "example/example-rule"); + }); + + it("should return two messages when executing with cli option that specifies preloaded plugin", async () => { + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + overrideConfig: { + plugins: ["test"], + rules: { "test/example-rule": 1 } + }, + plugins: { + "eslint-plugin-test": { rules: { "example-rule": require("../../fixtures/rules/custom-rule") } } + } + }); + const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("rules", "test", "test-custom-rule.js"))]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "test/example-rule"); + }); + + it("should load plugins from the `loadPluginsRelativeTo` directory, if specified", async () => { + eslint = new ESLint({ + resolvePluginsRelativeTo: getFixturePath("plugins"), + baseConfig: { + plugins: ["with-rules"], + rules: { "with-rules/rule1": "error" } + }, + useEslintrc: false + }); + const results = await eslint.lintText("foo"); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "with-rules/rule1"); + assert.strictEqual(results[0].messages[0].message, "Rule report from plugin"); + }); + }); + + describe("cache", () => { + + /** + * helper method to delete a file without caring about exceptions + * @param {string} filePath The file path + * @returns {void} + */ + function doDelete(filePath) { + try { + fs.unlinkSync(filePath); + } catch (ex) { + + /* + * we don't care if the file didn't exist, since our + * intention was to remove the file + */ + } + } + + /** + * helper method to delete the cache files created during testing + * @returns {void} + */ + function deleteCache() { + doDelete(path.resolve(".eslintcache")); + doDelete(path.resolve(".cache/custom-cache")); + } + + beforeEach(() => { + deleteCache(); + }); + + afterEach(() => { + sinon.restore(); + deleteCache(); + }); + + describe("when the cacheFile is a directory or looks like a directory", () => { + + /** + * helper method to delete the cache files created during testing + * @returns {void} + */ + function deleteCacheDir() { + try { + fs.unlinkSync("./tmp/.cacheFileDir/.cache_hashOfCurrentWorkingDirectory"); + } catch (ex) { + + /* + * we don't care if the file didn't exist, since our + * intention was to remove the file + */ + } + } + beforeEach(() => { + deleteCacheDir(); + }); + + afterEach(() => { + deleteCacheDir(); + }); + + it("should create the cache file inside the provided directory", async () => { + assert(!shell.test("-d", path.resolve("./tmp/.cacheFileDir/.cache_hashOfCurrentWorkingDirectory")), "the cache for eslint does not exist"); + + eslint = new ESLint({ + useEslintrc: false, + + // specifying cache true the cache will be created + cache: true, + cacheLocation: "./tmp/.cacheFileDir/", + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"], + ignore: false + }); + const file = getFixturePath("cache/src", "test-file.js"); + + await eslint.lintFiles([file]); + + assert(shell.test("-f", path.resolve(`./tmp/.cacheFileDir/.cache_${hash(process.cwd())}`)), "the cache for eslint was created"); + + sinon.restore(); + }); + }); + + it("should create the cache file inside the provided directory using the cacheLocation option", async () => { + assert(!shell.test("-d", path.resolve("./tmp/.cacheFileDir/.cache_hashOfCurrentWorkingDirectory")), "the cache for eslint does not exist"); + + eslint = new ESLint({ + useEslintrc: false, + + // specifying cache true the cache will be created + cache: true, + cacheLocation: "./tmp/.cacheFileDir/", + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"], + ignore: false + }); + const file = getFixturePath("cache/src", "test-file.js"); + + await eslint.lintFiles([file]); + + assert(shell.test("-f", path.resolve(`./tmp/.cacheFileDir/.cache_${hash(process.cwd())}`)), "the cache for eslint was created"); + + sinon.restore(); + }); + + it("should create the cache file inside cwd when no cacheLocation provided", async () => { + const cwd = path.resolve(getFixturePath("cli-engine")); + + eslint = new ESLint({ + useEslintrc: false, + cache: true, + cwd, + overrideConfig: { + rules: { + "no-console": 0 + } + }, + extensions: ["js"], + ignore: false + }); + const file = getFixturePath("cli-engine", "console.js"); + + await eslint.lintFiles([file]); + + assert(shell.test("-f", path.resolve(cwd, ".eslintcache")), "the cache for eslint was created at provided cwd"); + }); + + it("should invalidate the cache if the configuration changed between executions", async () => { + assert(!shell.test("-f", path.resolve(".eslintcache")), "the cache for eslint does not exist"); + + eslint = new ESLint({ + useEslintrc: false, + + // specifying cache true the cache will be created + cache: true, + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"], + ignore: false + }); + + let spy = sinon.spy(fs, "readFileSync"); + + let file = getFixturePath("cache/src", "test-file.js"); + + file = fs.realpathSync(file); + const results = await eslint.lintFiles([file]); + + for (const { errorCount, warningCount } of results) { + assert.strictEqual(errorCount + warningCount, 0, "the file passed without errors or warnings"); + } + assert.strictEqual(spy.getCall(0).args[0], file, "the module read the file because is considered changed"); + assert(shell.test("-f", path.resolve(".eslintcache")), "the cache for eslint was created"); + + // destroy the spy + sinon.restore(); + + eslint = new ESLint({ + useEslintrc: false, + + // specifying cache true the cache will be created + cache: true, + overrideConfig: { + rules: { + "no-console": 2, + "no-unused-vars": 2 + } + }, + extensions: ["js"], + ignore: false + }); + + // create a new spy + spy = sinon.spy(fs, "readFileSync"); + + const [cachedResult] = await eslint.lintFiles([file]); + + assert.strictEqual(spy.getCall(0).args[0], file, "the module read the file because is considered changed because the config changed"); + assert.strictEqual(cachedResult.errorCount, 1, "since configuration changed the cache was not used an one error was reported"); + assert(shell.test("-f", path.resolve(".eslintcache")), "the cache for eslint was created"); + }); + + it("should remember the files from a previous run and do not operate on them if not changed", async () => { + assert(!shell.test("-f", path.resolve(".eslintcache")), "the cache for eslint does not exist"); + + eslint = new ESLint({ + useEslintrc: false, + + // specifying cache true the cache will be created + cache: true, + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"], + ignore: false + }); + + let spy = sinon.spy(fs, "readFileSync"); + + let file = getFixturePath("cache/src", "test-file.js"); + + file = fs.realpathSync(file); + + const result = await eslint.lintFiles([file]); + + assert.strictEqual(spy.getCall(0).args[0], file, "the module read the file because is considered changed"); + assert(shell.test("-f", path.resolve(".eslintcache")), "the cache for eslint was created"); + + // destroy the spy + sinon.restore(); + + eslint = new ESLint({ + useEslintrc: false, + + // specifying cache true the cache will be created + cache: true, + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"], + ignore: false + }); + + // create a new spy + spy = sinon.spy(fs, "readFileSync"); + + const cachedResult = await eslint.lintFiles([file]); + + assert.deepStrictEqual(result, cachedResult, "the result is the same regardless of using cache or not"); + + // assert the file was not processed because the cache was used + assert(!spy.calledWith(file), "the file was not loaded because it used the cache"); + }); + + it("should remember the files from a previous run and do not operate on then if not changed", async () => { + const cacheLocation = getFixturePath(".eslintcache"); + const eslintOptions = { + useEslintrc: false, + + // specifying cache true the cache will be created + cache: true, + cacheLocation, + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"], + cwd: path.join(fixtureDir, "..") + }; + + assert(!shell.test("-f", cacheLocation), "the cache for eslint does not exist"); + + eslint = new ESLint(eslintOptions); + + let file = getFixturePath("cache/src", "test-file.js"); + + file = fs.realpathSync(file); + + await eslint.lintFiles([file]); + + assert(shell.test("-f", cacheLocation), "the cache for eslint was created"); + + eslintOptions.cache = false; + eslint = new ESLint(eslintOptions); + + await eslint.lintFiles([file]); + + assert(!shell.test("-f", cacheLocation), "the cache for eslint was deleted since last run did not used the cache"); + }); + + it("should store in the cache a file that failed the test", async () => { + const cacheLocation = getFixturePath(".eslintcache"); + + assert(!shell.test("-f", cacheLocation), "the cache for eslint does not exist"); + + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + + // specifying cache true the cache will be created + cache: true, + cacheLocation, + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"] + }); + const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); + const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); + const result = await eslint.lintFiles([badFile, goodFile]); + + assert(shell.test("-f", cacheLocation), "the cache for eslint was created"); + const fileCache = fCache.createFromFile(cacheLocation); + const { cache } = fileCache; + + assert.strictEqual(typeof cache.getKey(goodFile), "object", "the entry for the good file is in the cache"); + assert.strictEqual(typeof cache.getKey(badFile), "object", "the entry for the bad file is in the cache"); + const cachedResult = await eslint.lintFiles([badFile, goodFile]); + + assert.deepStrictEqual(result, cachedResult, "result is the same with or without cache"); + }); + + it("should not contain in the cache a file that was deleted", async () => { + const cacheLocation = getFixturePath(".eslintcache"); + + doDelete(cacheLocation); + + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + + // specifying cache true the cache will be created + cache: true, + cacheLocation, + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"] + }); + const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); + const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); + const toBeDeletedFile = fs.realpathSync(getFixturePath("cache/src", "file-to-delete.js")); + + await eslint.lintFiles([badFile, goodFile, toBeDeletedFile]); + const fileCache = fCache.createFromFile(cacheLocation); + let { cache } = fileCache; + + assert.strictEqual(typeof cache.getKey(toBeDeletedFile), "object", "the entry for the file to be deleted is in the cache"); + + // delete the file from the file system + fs.unlinkSync(toBeDeletedFile); + + /* + * file-entry-cache@2.0.0 will remove from the cache deleted files + * even when they were not part of the array of files to be analyzed + */ + await eslint.lintFiles([badFile, goodFile]); + + cache = JSON.parse(fs.readFileSync(cacheLocation)); + + assert.strictEqual(typeof cache[toBeDeletedFile], "undefined", "the entry for the file to be deleted is not in the cache"); + }); + + it("should contain files that were not visited in the cache provided they still exist", async () => { + const cacheLocation = getFixturePath(".eslintcache"); + + doDelete(cacheLocation); + + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + + // specifying cache true the cache will be created + cache: true, + cacheLocation, + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"] + }); + const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); + const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); + const testFile2 = fs.realpathSync(getFixturePath("cache/src", "test-file2.js")); + + await eslint.lintFiles([badFile, goodFile, testFile2]); + + let fileCache = fCache.createFromFile(cacheLocation); + let { cache } = fileCache; + + assert.strictEqual(typeof cache.getKey(testFile2), "object", "the entry for the test-file2 is in the cache"); + + /* + * we pass a different set of files minus test-file2 + * previous version of file-entry-cache would remove the non visited + * entries. 2.0.0 version will keep them unless they don't exist + */ + await eslint.lintFiles([badFile, goodFile]); + + fileCache = fCache.createFromFile(cacheLocation); + cache = fileCache.cache; + + assert.strictEqual(typeof cache.getKey(testFile2), "object", "the entry for the test-file2 is in the cache"); + }); + + it("should not delete cache when executing on text", async () => { + const cacheLocation = getFixturePath(".eslintcache"); + + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + cacheLocation, + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"] + }); + + assert(shell.test("-f", cacheLocation), "the cache for eslint exists"); + + await eslint.lintText("var foo = 'bar';"); + + assert(shell.test("-f", cacheLocation), "the cache for eslint still exists"); + }); + + it("should not delete cache when executing on text with a provided filename", async () => { + const cacheLocation = getFixturePath(".eslintcache"); + + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + cacheLocation, + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"] + }); + + assert(shell.test("-f", cacheLocation), "the cache for eslint exists"); + + await eslint.lintText("var bar = foo;", { filePath: "fixtures/passing.js" }); + + assert(shell.test("-f", cacheLocation), "the cache for eslint still exists"); + }); + + it("should not delete cache when executing on files with --cache flag", async () => { + const cacheLocation = getFixturePath(".eslintcache"); + + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + cache: true, + cacheLocation, + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"] + }); + const file = getFixturePath("cli-engine", "console.js"); + + assert(shell.test("-f", cacheLocation), "the cache for eslint exists"); + + await eslint.lintFiles([file]); + + assert(shell.test("-f", cacheLocation), "the cache for eslint still exists"); + }); + + it("should delete cache when executing on files without --cache flag", async () => { + const cacheLocation = getFixturePath(".eslintcache"); + + eslint = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + cacheLocation, + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"] + }); + const file = getFixturePath("cli-engine", "console.js"); + + assert(shell.test("-f", cacheLocation), "the cache for eslint exists"); + + await eslint.lintFiles([file]); + + assert(!shell.test("-f", cacheLocation), "the cache for eslint has been deleted"); + }); + + describe("cacheFile", () => { + it("should use the specified cache file", async () => { + const customCacheFile = path.resolve(".cache/custom-cache"); + + assert(!shell.test("-f", customCacheFile), "the cache for eslint does not exist"); + + eslint = new ESLint({ + useEslintrc: false, + + // specify a custom cache file + cacheLocation: customCacheFile, + + // specifying cache true the cache will be created + cache: true, + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"], + cwd: path.join(fixtureDir, "..") + }); + const badFile = fs.realpathSync(getFixturePath("cache/src", "fail-file.js")); + const goodFile = fs.realpathSync(getFixturePath("cache/src", "test-file.js")); + const result = await eslint.lintFiles([badFile, goodFile]); + + assert(shell.test("-f", customCacheFile), "the cache for eslint was created"); + const fileCache = fCache.createFromFile(customCacheFile); + const { cache } = fileCache; + + assert(typeof cache.getKey(goodFile) === "object", "the entry for the good file is in the cache"); + + assert(typeof cache.getKey(badFile) === "object", "the entry for the bad file is in the cache"); + const cachedResult = await eslint.lintFiles([badFile, goodFile]); + + assert.deepStrictEqual(result, cachedResult, "result is the same with or without cache"); + }); + }); + }); + + describe("processors", () => { + it("should return two messages when executing with config file that specifies a processor", async () => { + eslint = eslintWithPlugins({ + overrideConfigFile: getFixturePath("configurations", "processors.json"), + useEslintrc: false, + extensions: ["js", "txt"], + cwd: path.join(fixtureDir, "..") + }); + const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("processors", "test", "test-processor.txt"))]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 2); + }); + + it("should return two messages when executing with config file that specifies preloaded processor", async () => { + eslint = new ESLint({ + useEslintrc: false, + overrideConfig: { + plugins: ["test-processor"], + rules: { + "no-console": 2, + "no-unused-vars": 2 + } + }, + extensions: ["js", "txt"], + cwd: path.join(fixtureDir, ".."), + plugins: { + "test-processor": { + processors: { + ".txt": { + preprocess(text) { + return [text]; + }, + postprocess(messages) { + return messages[0]; + } + } + } + } + } + }); + const results = await eslint.lintFiles([fs.realpathSync(getFixturePath("processors", "test", "test-processor.txt"))]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 2); + }); + + it("should run processors when calling lintFiles with config file that specifies a processor", async () => { + eslint = eslintWithPlugins({ + overrideConfigFile: getFixturePath("configurations", "processors.json"), + useEslintrc: false, + extensions: ["js", "txt"], + cwd: path.join(fixtureDir, "..") + }); + const results = await eslint.lintFiles([getFixturePath("processors", "test", "test-processor.txt")]); + + assert.strictEqual(results[0].messages[0].message, "'b' is defined but never used."); + assert.strictEqual(results[0].messages[0].ruleId, "post-processed"); + }); + + it("should run processors when calling lintFiles with config file that specifies preloaded processor", async () => { + eslint = new ESLint({ + useEslintrc: false, + overrideConfig: { + plugins: ["test-processor"], + rules: { + "no-console": 2, + "no-unused-vars": 2 + } + }, + extensions: ["js", "txt"], + cwd: path.join(fixtureDir, ".."), + plugins: { + "test-processor": { + processors: { + ".txt": { + preprocess(text) { + return [text.replace("a()", "b()")]; + }, + postprocess(messages) { + messages[0][0].ruleId = "post-processed"; + return messages[0]; + } + } + } + } + } + }); + const results = await eslint.lintFiles([getFixturePath("processors", "test", "test-processor.txt")]); + + assert.strictEqual(results[0].messages[0].message, "'b' is defined but never used."); + assert.strictEqual(results[0].messages[0].ruleId, "post-processed"); + }); + + it("should run processors when calling lintText with config file that specifies a processor", async () => { + eslint = eslintWithPlugins({ + overrideConfigFile: getFixturePath("configurations", "processors.json"), + useEslintrc: false, + extensions: ["js", "txt"], + ignore: false + }); + const results = await eslint.lintText("function a() {console.log(\"Test\");}", { filePath: "tests/fixtures/processors/test/test-processor.txt" }); + + assert.strictEqual(results[0].messages[0].message, "'b' is defined but never used."); + assert.strictEqual(results[0].messages[0].ruleId, "post-processed"); + }); + + it("should run processors when calling lintText with config file that specifies preloaded processor", async () => { + eslint = new ESLint({ + useEslintrc: false, + overrideConfig: { + plugins: ["test-processor"], + rules: { + "no-console": 2, + "no-unused-vars": 2 + } + }, + extensions: ["js", "txt"], + ignore: false, + plugins: { + "test-processor": { + processors: { + ".txt": { + preprocess(text) { + return [text.replace("a()", "b()")]; + }, + postprocess(messages) { + messages[0][0].ruleId = "post-processed"; + return messages[0]; + } + } + } + } + } + }); + const results = await eslint.lintText("function a() {console.log(\"Test\");}", { filePath: "tests/fixtures/processors/test/test-processor.txt" }); + + assert.strictEqual(results[0].messages[0].message, "'b' is defined but never used."); + assert.strictEqual(results[0].messages[0].ruleId, "post-processed"); + }); + + describe("autofixing with processors", () => { + const HTML_PROCESSOR = Object.freeze({ + preprocess(text) { + return [text.replace(/^", { filePath: "foo.html" }); + + assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[0].output, ""); + }); + + it("should not run in autofix mode when using a processor that does not support autofixing", async () => { + eslint = new ESLint({ + useEslintrc: false, + overrideConfig: { + plugins: ["test-processor"], + rules: { + semi: 2 + } + }, + extensions: ["js", "txt"], + ignore: false, + fix: true, + plugins: { + "test-processor": { processors: { ".html": HTML_PROCESSOR } } + } + }); + const results = await eslint.lintText("", { filePath: "foo.html" }); + + assert.strictEqual(results[0].messages.length, 1); + assert(!Object.prototype.hasOwnProperty.call(results[0], "output")); + }); + + it("should not run in autofix mode when `fix: true` is not provided, even if the processor supports autofixing", async () => { + eslint = new ESLint({ + useEslintrc: false, + overrideConfig: { + plugins: ["test-processor"], + rules: { + semi: 2 + } + }, + extensions: ["js", "txt"], + ignore: false, + plugins: { + "test-processor": { + processors: { + ".html": Object.assign({ supportsAutofix: true }, HTML_PROCESSOR) + } + } + } + }); + const results = await eslint.lintText("", { filePath: "foo.html" }); + + assert.strictEqual(results[0].messages.length, 1); + assert(!Object.prototype.hasOwnProperty.call(results[0], "output")); + }); + }); + }); + + describe("Patterns which match no file should throw errors.", () => { + beforeEach(() => { + eslint = new ESLint({ + cwd: getFixturePath("cli-engine"), + useEslintrc: false + }); + }); + + it("one file", async () => { + await assert.rejects(async () => { + await eslint.lintFiles(["non-exist.js"]); + }, /No files matching 'non-exist\.js' were found\./u); + }); + + it("should throw if the directory exists and is empty", async () => { + await assert.rejects(async () => { + await eslint.lintFiles(["empty"]); + }, /No files matching 'empty' were found\./u); + }); + + it("one glob pattern", async () => { + await assert.rejects(async () => { + await eslint.lintFiles(["non-exist/**/*.js"]); + }, /No files matching 'non-exist\/\*\*\/\*\.js' were found\./u); + }); + + it("two files", async () => { + await assert.rejects(async () => { + await eslint.lintFiles(["aaa.js", "bbb.js"]); + }, /No files matching 'aaa\.js' were found\./u); + }); + + it("a mix of an existing file and a non-existing file", async () => { + await assert.rejects(async () => { + await eslint.lintFiles(["console.js", "non-exist.js"]); + }, /No files matching 'non-exist\.js' were found\./u); + }); + }); + + describe("overrides", () => { + beforeEach(() => { + eslint = new ESLint({ + cwd: getFixturePath("cli-engine/overrides-with-dot"), + ignore: false + }); + }); + + it("should recognize dotfiles", async () => { + const ret = await eslint.lintFiles([".test-target.js"]); + + assert.strictEqual(ret.length, 1); + assert.strictEqual(ret[0].messages.length, 1); + assert.strictEqual(ret[0].messages[0].ruleId, "no-unused-vars"); + }); + }); + + describe("a config file setting should have higher priority than a shareable config file's settings always; https://github.com/eslint/eslint/issues/11510", () => { + beforeEach(() => { + ({ ESLint } = defineESLintWithInMemoryFileSystem({ + cwd: () => path.join(os.tmpdir(), "eslint/11510"), + files: { + "no-console-error-in-overrides.json": JSON.stringify({ + overrides: [{ + files: ["*.js"], + rules: { "no-console": "error" } + }] + }), + ".eslintrc.json": JSON.stringify({ + extends: "./no-console-error-in-overrides.json", + rules: { "no-console": "off" } + }), + "a.js": "console.log();" + } + })); + eslint = new ESLint(); + }); + + it("should not report 'no-console' error.", async () => { + const results = await eslint.lintFiles("a.js"); + + assert.strictEqual(results.length, 1); + assert.deepStrictEqual(results[0].messages, []); + }); + }); + + describe("configs of plugin rules should be validated even if 'plugins' key doesn't exist; https://github.com/eslint/eslint/issues/11559", () => { + beforeEach(() => { + ({ ESLint } = defineESLintWithInMemoryFileSystem({ + cwd: () => path.join(os.tmpdir(), "eslint/11559"), + files: { + "node_modules/eslint-plugin-test/index.js": ` + exports.configs = { + recommended: { plugins: ["test"] } + }; + exports.rules = { + foo: { + meta: { schema: [{ type: "number" }] }, + create() { return {}; } + } + }; + `, + ".eslintrc.json": JSON.stringify({ + + // Import via the recommended config. + extends: "plugin:test/recommended", + + // Has invalid option. + rules: { "test/foo": ["error", "invalid-option"] } + }), + "a.js": "console.log();" + } + })); + eslint = new ESLint(); + }); + + it("should throw fatal error.", async () => { + await assert.rejects(async () => { + await eslint.lintFiles("a.js"); + }, /invalid-option/u); + }); + }); + + describe("'--fix-type' should not crash even if plugin rules exist; https://github.com/eslint/eslint/issues/11586", () => { + beforeEach(() => { + ({ ESLint } = defineESLintWithInMemoryFileSystem({ + cwd: () => path.join(os.tmpdir(), "eslint/11586"), + files: { + "node_modules/eslint-plugin-test/index.js": ` + exports.rules = { + "no-example": { + meta: { type: "problem", fixable: "code" }, + create(context) { + return { + Identifier(node) { + if (node.name === "example") { + context.report({ + node, + message: "fix", + fix: fixer => fixer.replaceText(node, "fixed") + }) + } + } + }; + } + } + }; + `, + ".eslintrc.json": JSON.stringify({ + plugins: ["test"], + rules: { "test/no-example": "error" } + }), + "a.js": "example;" + } + })); + eslint = new ESLint({ fix: true, fixTypes: ["problem"] }); + }); + + it("should not crash.", async () => { + const results = await eslint.lintFiles("a.js"); + + assert.strictEqual(results.length, 1); + assert.deepStrictEqual(results[0].messages, []); + assert.deepStrictEqual(results[0].output, "fixed;"); + }); + }); + + describe("multiple processors", () => { + const root = path.join(os.tmpdir(), "eslint/eslint/multiple-processors"); + const commonFiles = { + "node_modules/pattern-processor/index.js": fs.readFileSync( + require.resolve("../../fixtures/processors/pattern-processor"), + "utf8" + ), + "node_modules/eslint-plugin-markdown/index.js": ` + const { defineProcessor } = require("pattern-processor"); + const processor = defineProcessor(${/```(\w+)\n([\s\S]+?)\n```/gu}); + exports.processors = { + ".md": { ...processor, supportsAutofix: true }, + "non-fixable": processor + }; + `, + "node_modules/eslint-plugin-html/index.js": ` + const { defineProcessor } = require("pattern-processor"); + const processor = defineProcessor(${/ + + \`\`\` + ` + }; + + it("should lint only JavaScript blocks if '--ext' was not given.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ...commonFiles, + ".eslintrc.json": JSON.stringify({ + plugins: ["markdown", "html"], + rules: { semi: "error" } + }) + } + }).ESLint; + eslint = new ESLint({ cwd: root }); + const results = await eslint.lintFiles(["test.md"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "semi"); + assert.strictEqual(results[0].messages[0].line, 2); + }); + + it("should fix only JavaScript blocks if '--ext' was not given.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ...commonFiles, + ".eslintrc.json": JSON.stringify({ + plugins: ["markdown", "html"], + rules: { semi: "error" } + }) + } + }).ESLint; + eslint = new ESLint({ cwd: root, fix: true }); + const results = await eslint.lintFiles(["test.md"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[0].output, unIndent` + \`\`\`js + console.log("hello");${/* ← fixed */""} + \`\`\` + \`\`\`html +
Hello
+ + + \`\`\` + `); + }); + + it("should lint HTML blocks as well with multiple processors if '--ext' option was given.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ...commonFiles, + ".eslintrc.json": JSON.stringify({ + plugins: ["markdown", "html"], + rules: { semi: "error" } + }) + } + }).ESLint; + eslint = new ESLint({ cwd: root, extensions: ["js", "html"] }); + const results = await eslint.lintFiles(["test.md"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "semi"); // JS block + assert.strictEqual(results[0].messages[0].line, 2); + assert.strictEqual(results[0].messages[1].ruleId, "semi"); // JS block in HTML block + assert.strictEqual(results[0].messages[1].line, 7); + }); + + it("should fix HTML blocks as well with multiple processors if '--ext' option was given.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ...commonFiles, + ".eslintrc.json": JSON.stringify({ + plugins: ["markdown", "html"], + rules: { semi: "error" } + }) + } + }).ESLint; + eslint = new ESLint({ cwd: root, extensions: ["js", "html"], fix: true }); + const results = await eslint.lintFiles(["test.md"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 0); + assert.strictEqual(results[0].output, unIndent` + \`\`\`js + console.log("hello");${/* ← fixed */""} + \`\`\` + \`\`\`html +
Hello
+ + + \`\`\` + `); + }); + + it("should use overriden processor; should report HTML blocks but not fix HTML blocks if the processor for '*.html' didn't support autofix.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ...commonFiles, + ".eslintrc.json": JSON.stringify({ + plugins: ["markdown", "html"], + rules: { semi: "error" }, + overrides: [ + { + files: "*.html", + processor: "html/non-fixable" // supportsAutofix: false + } + ] + }) + } + }).ESLint; + eslint = new ESLint({ cwd: root, extensions: ["js", "html"], fix: true }); + const results = await eslint.lintFiles(["test.md"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].ruleId, "semi"); // JS Block in HTML Block + assert.strictEqual(results[0].messages[0].line, 7); + assert.strictEqual(results[0].messages[0].fix, void 0); + assert.strictEqual(results[0].output, unIndent` + \`\`\`js + console.log("hello");${/* ← fixed */""} + \`\`\` + \`\`\`html +
Hello
+ + + \`\`\` + `); + }); + + it("should use the config '**/*.html/*.js' to lint JavaScript blocks in HTML.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ...commonFiles, + ".eslintrc.json": JSON.stringify({ + plugins: ["markdown", "html"], + rules: { semi: "error" }, + overrides: [ + { + files: "*.html", + + // this rules are not used because ESLint re-resolve configs if a code block had a different file extension. + rules: { + semi: "error", + "no-console": "off" + } + }, + { + files: "**/*.html/*.js", + rules: { + semi: "off", + "no-console": "error" + } + } + ] + }) + } + }).ESLint; + eslint = new ESLint({ cwd: root, extensions: ["js", "html"] }); + const results = await eslint.lintFiles(["test.md"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "semi"); + assert.strictEqual(results[0].messages[0].line, 2); + assert.strictEqual(results[0].messages[1].ruleId, "no-console"); + assert.strictEqual(results[0].messages[1].line, 7); + }); + + it("should use the same config as one which has 'processor' property in order to lint blocks in HTML if the processor was legacy style.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ...commonFiles, + ".eslintrc.json": JSON.stringify({ + plugins: ["markdown", "html"], + rules: { semi: "error" }, + overrides: [ + { + files: "*.html", + processor: "html/legacy", // this processor returns strings rather than `{text, filename}` + rules: { + semi: "off", + "no-console": "error" + } + }, + { + files: "**/*.html/*.js", + rules: { + semi: "error", + "no-console": "off" + } + } + ] + }) + } + }).ESLint; + eslint = new ESLint({ cwd: root, extensions: ["js", "html"] }); + const results = await eslint.lintFiles(["test.md"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 3); + assert.strictEqual(results[0].messages[0].ruleId, "semi"); + assert.strictEqual(results[0].messages[0].line, 2); + assert.strictEqual(results[0].messages[1].ruleId, "no-console"); + assert.strictEqual(results[0].messages[1].line, 7); + assert.strictEqual(results[0].messages[2].ruleId, "no-console"); + assert.strictEqual(results[0].messages[2].line, 10); + }); + + it("should throw an error if invalid processor was specified.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ...commonFiles, + ".eslintrc.json": JSON.stringify({ + plugins: ["markdown", "html"], + processor: "markdown/unknown" + }) + } + }).ESLint; + eslint = new ESLint({ cwd: root }); + + await assert.rejects(async () => { + await eslint.lintFiles(["test.md"]); + }, /ESLint configuration of processor in '\.eslintrc\.json' is invalid: 'markdown\/unknown' was not found\./u); + }); + + it("should lint HTML blocks as well with multiple processors if 'overrides[].files' is present.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ...commonFiles, + ".eslintrc.json": JSON.stringify({ + plugins: ["markdown", "html"], + rules: { semi: "error" }, + overrides: [ + { + files: "*.html", + processor: "html/.html" + }, + { + files: "*.md", + processor: "markdown/.md" + } + ] + }) + } + }).ESLint; + eslint = new ESLint({ cwd: root }); + const results = await eslint.lintFiles(["test.md"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 2); + assert.strictEqual(results[0].messages[0].ruleId, "semi"); // JS block + assert.strictEqual(results[0].messages[0].line, 2); + assert.strictEqual(results[0].messages[1].ruleId, "semi"); // JS block in HTML block + assert.strictEqual(results[0].messages[1].line, 7); + }); + }); + + describe("MODULE_NOT_FOUND error handling", () => { + const cwd = getFixturePath("module-not-found"); + + beforeEach(() => { + eslint = new ESLint({ cwd }); + }); + + it("should throw an error with a message template when 'extends' property has a non-existence JavaScript config.", async () => { + try { + await eslint.lintText("test", { filePath: "extends-js/test.js" }); + } catch (err) { + assert.strictEqual(err.messageTemplate, "extend-config-missing"); + assert.deepStrictEqual(err.messageData, { + configName: "nonexistent-config", + importerName: getFixturePath("module-not-found", "extends-js", ".eslintrc.yml") + }); + return; + } + assert.fail("Expected to throw an error"); + }); + + it("should throw an error with a message template when 'extends' property has a non-existence plugin config.", async () => { + try { + await eslint.lintText("test", { filePath: "extends-plugin/test.js" }); + } catch (err) { + assert.strictEqual(err.code, "MODULE_NOT_FOUND"); + assert.strictEqual(err.messageTemplate, "plugin-missing"); + assert.deepStrictEqual(err.messageData, { + importerName: `extends-plugin${path.sep}.eslintrc.yml`, + pluginName: "eslint-plugin-nonexistent-plugin", + resolvePluginsRelativeTo: path.join(cwd, "extends-plugin") // the directory of the config file. + }); + return; + } + assert.fail("Expected to throw an error"); + }); + + it("should throw an error with a message template when 'plugins' property has a non-existence plugin.", async () => { + try { + await eslint.lintText("test", { filePath: "plugins/test.js" }); + } catch (err) { + assert.strictEqual(err.code, "MODULE_NOT_FOUND"); + assert.strictEqual(err.messageTemplate, "plugin-missing"); + assert.deepStrictEqual(err.messageData, { + importerName: `plugins${path.sep}.eslintrc.yml`, + pluginName: "eslint-plugin-nonexistent-plugin", + resolvePluginsRelativeTo: path.join(cwd, "plugins") // the directory of the config file. + }); + return; + } + assert.fail("Expected to throw an error"); + }); + + it("should throw an error with no message template when a JavaScript config threw a 'MODULE_NOT_FOUND' error.", async () => { + try { + await eslint.lintText("test", { filePath: "throw-in-config-itself/test.js" }); + } catch (err) { + assert.strictEqual(err.code, "MODULE_NOT_FOUND"); + assert.strictEqual(err.messageTemplate, void 0); + return; + } + assert.fail("Expected to throw an error"); + }); + + it("should throw an error with no message template when 'extends' property has a JavaScript config that throws a 'MODULE_NOT_FOUND' error.", async () => { + try { + await eslint.lintText("test", { filePath: "throw-in-extends-js/test.js" }); + } catch (err) { + assert.strictEqual(err.code, "MODULE_NOT_FOUND"); + assert.strictEqual(err.messageTemplate, void 0); + return; + } + assert.fail("Expected to throw an error"); + }); + + it("should throw an error with no message template when 'extends' property has a plugin config that throws a 'MODULE_NOT_FOUND' error.", async () => { + try { + await eslint.lintText("test", { filePath: "throw-in-extends-plugin/test.js" }); + } catch (err) { + assert.strictEqual(err.code, "MODULE_NOT_FOUND"); + assert.strictEqual(err.messageTemplate, void 0); + return; + } + assert.fail("Expected to throw an error"); + }); + + it("should throw an error with no message template when 'plugins' property has a plugin config that throws a 'MODULE_NOT_FOUND' error.", async () => { + try { + await eslint.lintText("test", { filePath: "throw-in-plugins/test.js" }); + } catch (err) { + assert.strictEqual(err.code, "MODULE_NOT_FOUND"); + assert.strictEqual(err.messageTemplate, void 0); + return; + } + assert.fail("Expected to throw an error"); + }); + }); + + describe("with '--rulesdir' option", () => { + it("should use the configured rules which are defined by '--rulesdir' option.", async () => { + const rootPath = getFixturePath("cli-engine/with-rulesdir"); + const StubbedESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => rootPath, + files: { + "internal-rules/test.js": ` + module.exports = context => ({ + ExpressionStatement(node) { + context.report({ node, message: "ok" }) + } + }) + `, + ".eslintrc.json": JSON.stringify({ + root: true, + rules: { test: "error" } + }), + "test.js": "console.log('hello')" + } + }).ESLint; + + eslint = new StubbedESLint({ + rulePaths: ["internal-rules"] + }); + const results = await eslint.lintFiles(["test.js"]); + + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].messages.length, 1); + assert.strictEqual(results[0].messages[0].message, "ok"); + }); + }); + + describe("glob pattern '[ab].js'", () => { + const root = getFixturePath("cli-engine/unmatched-glob"); + + it("should match '[ab].js' if existed.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "a.js": "", + "b.js": "", + "ab.js": "", + "[ab].js": "", + ".eslintrc.yml": "root: true" + } + }).ESLint; + eslint = new ESLint(); + const results = await eslint.lintFiles(["[ab].js"]); + const filenames = results.map(r => path.basename(r.filePath)); + + assert.deepStrictEqual(filenames, ["[ab].js"]); + }); + + it("should match 'a.js' and 'b.js' if '[ab].js' didn't existed.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "a.js": "", + "b.js": "", + "ab.js": "", + ".eslintrc.yml": "root: true" + } + }).ESLint; + eslint = new ESLint(); + const results = await eslint.lintFiles(["[ab].js"]); + const filenames = results.map(r => path.basename(r.filePath)); + + assert.deepStrictEqual(filenames, ["a.js", "b.js"]); + }); + }); + + describe("with 'noInlineConfig' setting", () => { + const root = getFixturePath("cli-engine/noInlineConfig"); + + it("should warn directive comments if 'noInlineConfig' was given.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "test.js": "/* globals foo */", + ".eslintrc.yml": "noInlineConfig: true" + } + }).ESLint; + eslint = new ESLint(); + const results = await eslint.lintFiles(["test.js"]); + const messages = results[0].messages; + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].message, "'/*globals*/' has no effect because you have 'noInlineConfig' setting in your config (.eslintrc.yml)."); + }); + + it("should show the config file what the 'noInlineConfig' came from.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-config-foo/index.js": "module.exports = {noInlineConfig: true}", + "test.js": "/* globals foo */", + ".eslintrc.yml": "extends: foo" + } + }).ESLint; + eslint = new ESLint(); + const results = await eslint.lintFiles(["test.js"]); + const messages = results[0].messages; + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].message, "'/*globals*/' has no effect because you have 'noInlineConfig' setting in your config (.eslintrc.yml » eslint-config-foo)."); + }); + }); + + describe("with 'reportUnusedDisableDirectives' setting", () => { + const root = getFixturePath("cli-engine/reportUnusedDisableDirectives"); + + it("should warn unused 'eslint-disable' comments if 'reportUnusedDisableDirectives' was given.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "test.js": "/* eslint-disable eqeqeq */", + ".eslintrc.yml": "reportUnusedDisableDirectives: true" + } + }).ESLint; + eslint = new ESLint(); + const results = await eslint.lintFiles(["test.js"]); + const messages = results[0].messages; + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].severity, 1); + assert.strictEqual(messages[0].message, "Unused eslint-disable directive (no problems were reported from 'eqeqeq')."); + }); + + describe("the runtime option overrides config files.", () => { + it("should not warn unused 'eslint-disable' comments if 'reportUnusedDisableDirectives=off' was given in runtime.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "test.js": "/* eslint-disable eqeqeq */", + ".eslintrc.yml": "reportUnusedDisableDirectives: true" + } + }).ESLint; + eslint = new ESLint({ reportUnusedDisableDirectives: "off" }); + const results = await eslint.lintFiles(["test.js"]); + const messages = results[0].messages; + + assert.strictEqual(messages.length, 0); + }); + + it("should warn unused 'eslint-disable' comments as error if 'reportUnusedDisableDirectives=error' was given in runtime.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "test.js": "/* eslint-disable eqeqeq */", + ".eslintrc.yml": "reportUnusedDisableDirectives: true" + } + }).ESLint; + eslint = new ESLint({ reportUnusedDisableDirectives: "error" }); + const results = await eslint.lintFiles(["test.js"]); + const messages = results[0].messages; + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].severity, 2); + assert.strictEqual(messages[0].message, "Unused eslint-disable directive (no problems were reported from 'eqeqeq')."); + }); + }); + }); + + describe("with 'overrides[*].extends' setting on deep locations", () => { + const root = getFixturePath("cli-engine/deeply-overrides-i-extends"); + + it("should not throw.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + overrides: [{ files: ["*test*"], extends: "two" }] + })}`, + "node_modules/eslint-config-two/index.js": `module.exports = ${JSON.stringify({ + overrides: [{ files: ["*.js"], extends: "three" }] + })}`, + "node_modules/eslint-config-three/index.js": `module.exports = ${JSON.stringify({ + rules: { "no-console": "error" } + })}`, + "test.js": "console.log('hello')", + ".eslintrc.yml": "extends: one" + } + }).ESLint; + eslint = new ESLint(); + const results = await eslint.lintFiles(["test.js"]); + const messages = results[0].messages; + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "no-console"); + }); + }); + + describe("don't ignore the entry directory.", () => { + const root = getFixturePath("cli-engine/dont-ignore-entry-dir"); + + it("'lintFiles(\".\")' should not load config files from outside of \".\".", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "../.eslintrc.json": "BROKEN FILE", + ".eslintrc.json": JSON.stringify({ root: true }), + "index.js": "console.log(\"hello\")" + } + }).ESLint; + eslint = new ESLint(); + + // Don't throw "failed to load config file" error. + await eslint.lintFiles("."); + }); + + it("'lintFiles(\".\")' should not ignore '.' even if 'ignorePatterns' contains it.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "../.eslintrc.json": JSON.stringify({ ignorePatterns: ["/dont-ignore-entry-dir"] }), + ".eslintrc.json": JSON.stringify({ root: true }), + "index.js": "console.log(\"hello\")" + } + }).ESLint; + eslint = new ESLint(); + + // Don't throw "file not found" error. + await eslint.lintFiles("."); + }); + + it("'lintFiles(\"subdir\")' should not ignore './subdir' even if 'ignorePatterns' contains it.", async () => { + ESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ ignorePatterns: ["/subdir"] }), + "subdir/.eslintrc.json": JSON.stringify({ root: true }), + "subdir/index.js": "console.log(\"hello\")" + } + }).ESLint; + eslint = new ESLint(); + + // Don't throw "file not found" error. + await eslint.lintFiles("subdir"); + }); + }); + + it("should throw if non-boolean value is given to 'options.warnIgnored' option", async () => { + eslint = new ESLint(); + await assert.rejects(() => eslint.lintFiles(777), /'patterns' must be a non-empty string or an array of non-empty strings/u); + await assert.rejects(() => eslint.lintFiles([null]), /'patterns' must be a non-empty string or an array of non-empty strings/u); + }); + }); + + describe("calculateConfigForFile", () => { + it("should return the info from Config#getConfig when called", async () => { + const options = { + overrideConfigFile: getFixturePath("configurations", "quotes-error.json") + }; + const engine = new ESLint(options); + const filePath = getFixturePath("single-quoted.js"); + const actualConfig = await engine.calculateConfigForFile(filePath); + const expectedConfig = new CascadingConfigArrayFactory({ specificConfigPath: options.overrideConfigFile }) + .getConfigArrayForFile(filePath) + .extractConfig(filePath) + .toCompatibleObjectAsConfigFileContent(); + + assert.deepStrictEqual(actualConfig, expectedConfig); + }); + + + it("should return the config when run from within a subdir", async () => { + const options = { + cwd: getFixturePath("config-hierarchy", "root-true", "parent", "root", "subdir") + }; + const engine = new ESLint(options); + const filePath = getFixturePath("config-hierarchy", "root-true", "parent", "root", ".eslintrc"); + const actualConfig = await engine.calculateConfigForFile("./.eslintrc"); + const expectedConfig = new CascadingConfigArrayFactory(options) + .getConfigArrayForFile(filePath) + .extractConfig(filePath) + .toCompatibleObjectAsConfigFileContent(); + + assert.deepStrictEqual(actualConfig, expectedConfig); + }); + + it("should throw an error if a directory path was given.", async () => { + const engine = new ESLint(); + + try { + await engine.calculateConfigForFile("."); + } catch (error) { + assert.strictEqual(error.messageTemplate, "print-config-with-directory-path"); + return; + } + assert.fail("should throw an error"); + }); + + it("should throw if non-string value is given to 'filePath' parameter", async () => { + const eslint = new ESLint(); + + await assert.rejects(() => eslint.calculateConfigForFile(null), /'filePath' must be a non-empty string/u); + }); + }); + + describe("isPathIgnored", () => { + it("should check if the given path is ignored", async () => { + const engine = new ESLint({ + ignorePath: getFixturePath(".eslintignore2"), + cwd: getFixturePath() + }); + + assert(await engine.isPathIgnored("undef.js")); + assert(!await engine.isPathIgnored("passing.js")); + }); + + it("should return false if ignoring is disabled", async () => { + const engine = new ESLint({ + ignore: false, + ignorePath: getFixturePath(".eslintignore2"), + cwd: getFixturePath() + }); + + assert(!await engine.isPathIgnored("undef.js")); + }); + + // https://github.com/eslint/eslint/issues/5547 + it("should return true for default ignores even if ignoring is disabled", async () => { + const engine = new ESLint({ + ignore: false, + cwd: getFixturePath("cli-engine") + }); + + assert(await engine.isPathIgnored("node_modules/foo.js")); + }); + + describe("about the default ignore patterns", () => { + it("should always apply defaultPatterns if ignore option is true", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules/package/file.js"))); + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/node_modules/package/file.js"))); + }); + + it("should still apply defaultPatterns if ignore option is is false", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ ignore: false, cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules/package/file.js"))); + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/node_modules/package/file.js"))); + }); + + it("should allow subfolders of defaultPatterns to be unignored by ignorePattern", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ + cwd, + overrideConfig: { + ignorePatterns: "!/node_modules/package" + } + }); + + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules", "package", "file.js"))); + }); + + it("should allow subfolders of defaultPatterns to be unignored by ignorePath", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ cwd, ignorePath: getFixturePath("ignored-paths", ".eslintignoreWithUnignoredDefaults") }); + + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules", "package", "file.js"))); + }); + + it("should ignore dotfiles", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", ".foo"))); + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/.bar"))); + }); + + it("should ignore directories beginning with a dot", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", ".foo/bar"))); + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/.bar/baz"))); + }); + + it("should still ignore dotfiles when ignore option disabled", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ ignore: false, cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", ".foo"))); + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/.bar"))); + }); + + it("should still ignore directories beginning with a dot when ignore option disabled", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ ignore: false, cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", ".foo/bar"))); + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/.bar/baz"))); + }); + + it("should not ignore absolute paths containing '..'", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ cwd }); + + assert(!await engine.isPathIgnored(`${getFixturePath("ignored-paths", "foo")}/../unignored.js`)); + }); + + it("should ignore /node_modules/ relative to .eslintignore when loaded", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ ignorePath: getFixturePath("ignored-paths", ".eslintignore"), cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules", "existing.js"))); + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo", "node_modules", "existing.js"))); + }); + + it("should ignore /node_modules/ relative to cwd without an .eslintignore", async () => { + const cwd = getFixturePath("ignored-paths", "no-ignore-file"); + const engine = new ESLint({ cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "no-ignore-file", "node_modules", "existing.js"))); + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "no-ignore-file", "foo", "node_modules", "existing.js"))); + }); + }); + + describe("with no .eslintignore file", () => { + it("should not travel to parent directories to find .eslintignore when it's missing and cwd is provided", async () => { + const cwd = getFixturePath("ignored-paths", "configurations"); + const engine = new ESLint({ cwd }); + + // a .eslintignore in parent directories includes `*.js`, but don't load it. + assert(!await engine.isPathIgnored("foo.js")); + assert(await engine.isPathIgnored("node_modules/foo.js")); + }); + + it("should return false for files outside of the cwd (with no ignore file provided)", async () => { + + // Default ignore patterns should not inadvertently ignore files in parent directories + const engine = new ESLint({ cwd: getFixturePath("ignored-paths", "no-ignore-file") }); + + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); + }); + }); + + describe("with .eslintignore file or package.json file", () => { + it("should load .eslintignore from cwd when explicitly passed", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ cwd }); + + // `${cwd}/.eslintignore` includes `sampleignorepattern`. + assert(await engine.isPathIgnored("sampleignorepattern")); + }); + + it("should use package.json's eslintIgnore files if no specified .eslintignore file", async () => { + const cwd = getFixturePath("ignored-paths", "package-json-ignore"); + const engine = new ESLint({ cwd }); + + assert(await engine.isPathIgnored("hello.js")); + assert(await engine.isPathIgnored("world.js")); + }); + + it("should use correct message template if failed to parse package.json", () => { + const cwd = getFixturePath("ignored-paths", "broken-package-json"); + + assert.throws(() => { + try { + // eslint-disable-next-line no-new + new ESLint({ cwd }); + } catch (error) { + assert.strictEqual(error.messageTemplate, "failed-to-read-json"); + throw error; + } + }); + }); + + it("should not use package.json's eslintIgnore files if specified .eslintignore file", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ cwd }); + + /* + * package.json includes `hello.js` and `world.js`. + * .eslintignore includes `sampleignorepattern`. + */ + assert(!await engine.isPathIgnored("hello.js")); + assert(!await engine.isPathIgnored("world.js")); + assert(await engine.isPathIgnored("sampleignorepattern")); + }); + + it("should error if package.json's eslintIgnore is not an array of file paths", () => { + const cwd = getFixturePath("ignored-paths", "bad-package-json-ignore"); + + assert.throws(() => { + // eslint-disable-next-line no-new + new ESLint({ cwd }); + }, /Package\.json eslintIgnore property requires an array of paths/u); + }); + }); + + describe("with --ignore-pattern option", () => { + it("should accept a string for options.ignorePattern", async () => { + const cwd = getFixturePath("ignored-paths", "ignore-pattern"); + const engine = new ESLint({ + overrideConfig: { + ignorePatterns: "ignore-me.txt" + }, + cwd + }); + + assert(await engine.isPathIgnored("ignore-me.txt")); + }); + + it("should accept an array for options.ignorePattern", async () => { + const engine = new ESLint({ + overrideConfig: { + ignorePatterns: ["a", "b"] + }, + useEslintrc: false + }); + + assert(await engine.isPathIgnored("a")); + assert(await engine.isPathIgnored("b")); + assert(!await engine.isPathIgnored("c")); + }); + + it("should return true for files which match an ignorePattern even if they do not exist on the filesystem", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ + overrideConfig: { + ignorePatterns: "not-a-file" + }, + cwd + }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "not-a-file"))); + }); + + it("should return true for file matching an ignore pattern exactly", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ overrideConfig: { ignorePatterns: "undef.js" }, cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); + }); + + it("should return false for file matching an invalid ignore pattern with leading './'", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ overrideConfig: { ignorePatterns: "./undef.js" }, cwd }); + + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); + }); + + it("should return false for file in subfolder of cwd matching an ignore pattern with leading '/'", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ overrideConfig: { ignorePatterns: "/undef.js" }, cwd }); + + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "subdir", "undef.js"))); + }); + + it("should return true for file matching a child of an ignore pattern", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ overrideConfig: { ignorePatterns: "ignore-pattern" }, cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "ignore-pattern", "ignore-me.txt"))); + }); + + it("should return true for file matching a grandchild of an ignore pattern", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ overrideConfig: { ignorePatterns: "ignore-pattern" }, cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "ignore-pattern", "subdir", "ignore-me.txt"))); + }); + + it("should return false for file not matching any ignore pattern", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ overrideConfig: { ignorePatterns: "failing.js" }, cwd }); + + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "unignored.js"))); + }); + + it("two globstar '**' ignore pattern should ignore files in nested directories", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ overrideConfig: { ignorePatterns: "**/*.js" }, cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo.js"))); + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/bar.js"))); + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/bar/baz.js"))); + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "foo.j2"))); + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/bar.j2"))); + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "foo/bar/baz.j2"))); + }); + }); + + describe("with --ignore-path option", () => { + it("initialization with ignorePath should work when cwd is a parent directory", async () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", "custom-name", "ignore-file"); + const engine = new ESLint({ ignorePath, cwd }); + + assert(await engine.isPathIgnored("custom-name/foo.js")); + }); + + it("initialization with ignorePath should work when the file is in the cwd", async () => { + const cwd = getFixturePath("ignored-paths", "custom-name"); + const ignorePath = getFixturePath("ignored-paths", "custom-name", "ignore-file"); + const engine = new ESLint({ ignorePath, cwd }); + + assert(await engine.isPathIgnored("foo.js")); + }); + + it("initialization with ignorePath should work when cwd is a subdirectory", async () => { + const cwd = getFixturePath("ignored-paths", "custom-name", "subdirectory"); + const ignorePath = getFixturePath("ignored-paths", "custom-name", "ignore-file"); + const engine = new ESLint({ ignorePath, cwd }); + + assert(await engine.isPathIgnored("../custom-name/foo.js")); + }); + + it("initialization with invalid file should throw error", () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", "not-a-directory", ".foobaz"); + + assert.throws(() => { + // eslint-disable-next-line no-new + new ESLint({ ignorePath, cwd }); + }, /Cannot read \.eslintignore file/u); + }); + + it("should return false for files outside of ignorePath's directory", async () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", "custom-name", "ignore-file"); + const engine = new ESLint({ ignorePath, cwd }); + + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); + }); + + it("should resolve relative paths from CWD", async () => { + const cwd = getFixturePath("ignored-paths", "subdir"); + const ignorePath = getFixturePath("ignored-paths", ".eslintignoreForDifferentCwd"); + const engine = new ESLint({ ignorePath, cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/undef.js"))); + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); + }); + + it("should resolve relative paths from CWD when it's in a child directory", async () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", "subdir/.eslintignoreInChildDir"); + const engine = new ESLint({ ignorePath, cwd }); + + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/undef.js"))); + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "undef.js"))); + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "foo.js"))); + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "subdir/foo.js"))); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "node_modules/bar.js"))); + }); + + it("should resolve relative paths from CWD when it contains negated globs", async () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", "subdir/.eslintignoreInChildDir"); + const engine = new ESLint({ ignorePath, cwd }); + + assert(await engine.isPathIgnored("subdir/blah.txt")); + assert(await engine.isPathIgnored("blah.txt")); + assert(await engine.isPathIgnored("subdir/bar.txt")); + assert(!await engine.isPathIgnored("bar.txt")); + assert(!await engine.isPathIgnored("subdir/baz.txt")); + assert(!await engine.isPathIgnored("baz.txt")); + }); + + it("should resolve default ignore patterns from the CWD even when the ignorePath is in a subdirectory", async () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", "subdir/.eslintignoreInChildDir"); + const engine = new ESLint({ ignorePath, cwd }); + + assert(await engine.isPathIgnored("node_modules/blah.js")); + }); + + it("should resolve default ignore patterns from the CWD even when the ignorePath is in a parent directory", async () => { + const cwd = getFixturePath("ignored-paths", "subdir"); + const ignorePath = getFixturePath("ignored-paths", ".eslintignoreForDifferentCwd"); + const engine = new ESLint({ ignorePath, cwd }); + + assert(await engine.isPathIgnored("node_modules/blah.js")); + }); + + it("should handle .eslintignore which contains CRLF correctly.", async () => { + const ignoreFileContent = fs.readFileSync(getFixturePath("ignored-paths", "crlf/.eslintignore"), "utf8"); + + assert(ignoreFileContent.includes("\r"), "crlf/.eslintignore should contains CR."); + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", "crlf/.eslintignore"); + const engine = new ESLint({ ignorePath, cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "crlf/hide1/a.js"))); + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "crlf/hide2/a.js"))); + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "crlf/hide3/a.js"))); + }); + + it("should not include comments in ignore rules", async () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", ".eslintignoreWithComments"); + const engine = new ESLint({ ignorePath, cwd }); + + assert(!await engine.isPathIgnored("# should be ignored")); + assert(await engine.isPathIgnored("this_one_not")); + }); + + it("should ignore a non-negated pattern", async () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", ".eslintignoreWithNegation"); + const engine = new ESLint({ ignorePath, cwd }); + + assert(await engine.isPathIgnored(getFixturePath("ignored-paths", "negation", "ignore.js"))); + }); + + it("should not ignore a negated pattern", async () => { + const cwd = getFixturePath("ignored-paths"); + const ignorePath = getFixturePath("ignored-paths", ".eslintignoreWithNegation"); + const engine = new ESLint({ ignorePath, cwd }); + + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "negation", "unignore.js"))); + }); + }); + + describe("with --ignore-path option and --ignore-pattern option", () => { + it("should return false for ignored file when unignored with ignore pattern", async () => { + const cwd = getFixturePath("ignored-paths"); + const engine = new ESLint({ + ignorePath: getFixturePath("ignored-paths", ".eslintignore"), + overrideConfig: { + ignorePatterns: "!sampleignorepattern" + }, + cwd + }); + + assert(!await engine.isPathIgnored(getFixturePath("ignored-paths", "sampleignorepattern"))); + }); + }); + + it("should throw if non-string value is given to 'filePath' parameter", async () => { + const eslint = new ESLint(); + + await assert.rejects(() => eslint.isPathIgnored(null), /'filePath' must be a non-empty string/u); + }); + }); + + describe("loadFormatter()", () => { + it("should return a formatter object when a bundled formatter is requested", async () => { + const engine = new ESLint(); + const formatter = await engine.loadFormatter("compact"); + + assert.strictEqual(typeof formatter, "object"); + assert.strictEqual(typeof formatter.format, "function"); + }); + + it("should return a formatter object when no argument is passed", async () => { + const engine = new ESLint(); + const formatter = await engine.loadFormatter(); + + assert.strictEqual(typeof formatter, "object"); + assert.strictEqual(typeof formatter.format, "function"); + }); + + it("should return a formatter object when a custom formatter is requested", async () => { + const engine = new ESLint(); + const formatter = await engine.loadFormatter(getFixturePath("formatters", "simple.js")); + + assert.strictEqual(typeof formatter, "object"); + assert.strictEqual(typeof formatter.format, "function"); + }); + + it("should return a formatter object when a custom formatter is requested, also if the path has backslashes", async () => { + const engine = new ESLint({ + cwd: path.join(fixtureDir, "..") + }); + const formatter = await engine.loadFormatter(".\\fixtures\\formatters\\simple.js"); + + assert.strictEqual(typeof formatter, "object"); + assert.strictEqual(typeof formatter.format, "function"); + }); + + it("should return a formatter object when a formatter prefixed with eslint-formatter is requested", async () => { + const engine = new ESLint({ + cwd: getFixturePath("cli-engine") + }); + const formatter = await engine.loadFormatter("bar"); + + assert.strictEqual(typeof formatter, "object"); + assert.strictEqual(typeof formatter.format, "function"); + }); + + it("should return a formatter object when a formatter is requested, also when the eslint-formatter prefix is included in the format argument", async () => { + const engine = new ESLint({ + cwd: getFixturePath("cli-engine") + }); + const formatter = await engine.loadFormatter("eslint-formatter-bar"); + + assert.strictEqual(typeof formatter, "object"); + assert.strictEqual(typeof formatter.format, "function"); + }); + + it("should return a formatter object when a formatter is requested within a scoped npm package", async () => { + const engine = new ESLint({ + cwd: getFixturePath("cli-engine") + }); + const formatter = await engine.loadFormatter("@somenamespace/foo"); + + assert.strictEqual(typeof formatter, "object"); + assert.strictEqual(typeof formatter.format, "function"); + }); + + it("should return a formatter object when a formatter is requested within a scoped npm package, also when the eslint-formatter prefix is included in the format argument", async () => { + const engine = new ESLint({ + cwd: getFixturePath("cli-engine") + }); + const formatter = await engine.loadFormatter("@somenamespace/eslint-formatter-foo"); + + assert.strictEqual(typeof formatter, "object"); + assert.strictEqual(typeof formatter.format, "function"); + }); + + it("should throw if a customer formatter doesn't exist", async () => { + const engine = new ESLint(); + const formatterPath = getFixturePath("formatters", "doesntexist.js"); + const fullFormatterPath = path.resolve(formatterPath); + + await assert.rejects(async () => { + await engine.loadFormatter(formatterPath); + }, new RegExp(escapeStringRegExp(`There was a problem loading formatter: ${fullFormatterPath}\nError: Cannot find module '${fullFormatterPath}'`), "u")); + }); + + it("should throw if a built-in formatter doesn't exist", async () => { + const engine = new ESLint(); + const fullFormatterPath = path.resolve(__dirname, "../../../lib/cli-engine/formatters/special"); + + await assert.rejects(async () => { + await engine.loadFormatter("special"); + }, new RegExp(escapeStringRegExp(`There was a problem loading formatter: ${fullFormatterPath}\nError: Cannot find module '${fullFormatterPath}'`), "u")); + }); + + it("should throw if the required formatter exists but has an error", async () => { + const engine = new ESLint(); + const formatterPath = getFixturePath("formatters", "broken.js"); + + await assert.rejects(async () => { + await engine.loadFormatter(formatterPath); + }, new RegExp(escapeStringRegExp(`There was a problem loading formatter: ${formatterPath}\nError: Cannot find module 'this-module-does-not-exist'`), "u")); + }); + + it("should throw if a non-string formatter name is passed", async () => { + const engine = new ESLint(); + + await assert.rejects(async () => { + await engine.loadFormatter(5); + }, /'name' must be a string/u); + }); + }); + + describe("getErrorResults()", () => { + it("should report 5 error messages when looking for errors only", async () => { + process.chdir(originalDir); + const engine = new ESLint(); + const results = await engine.lintText("var foo = 'bar';"); + const errorResults = ESLint.getErrorResults(results); + + assert.strictEqual(errorResults[0].messages.length, 5); + assert.strictEqual(errorResults[0].errorCount, 5); + assert.strictEqual(errorResults[0].fixableErrorCount, 3); + assert.strictEqual(errorResults[0].fixableWarningCount, 0); + assert.strictEqual(errorResults[0].messages[0].ruleId, "strict"); + assert.strictEqual(errorResults[0].messages[0].severity, 2); + assert.strictEqual(errorResults[0].messages[1].ruleId, "no-var"); + assert.strictEqual(errorResults[0].messages[1].severity, 2); + assert.strictEqual(errorResults[0].messages[2].ruleId, "no-unused-vars"); + assert.strictEqual(errorResults[0].messages[2].severity, 2); + assert.strictEqual(errorResults[0].messages[3].ruleId, "quotes"); + assert.strictEqual(errorResults[0].messages[3].severity, 2); + assert.strictEqual(errorResults[0].messages[4].ruleId, "eol-last"); + assert.strictEqual(errorResults[0].messages[4].severity, 2); + }); + + it("should not mutate passed report parameter", async () => { + process.chdir(originalDir); + const engine = new ESLint({ + overrideConfig: { + rules: { quotes: [1, "double"] } + } + }); + const results = await engine.lintText("var foo = 'bar';"); + const reportResultsLength = results[0].messages.length; + + ESLint.getErrorResults(results); + + assert.strictEqual(results[0].messages.length, reportResultsLength); + }); + + it("should report a warningCount of 0 when looking for errors only", async () => { + process.chdir(originalDir); + const engine = new ESLint(); + const results = await engine.lintText("var foo = 'bar';"); + const errorResults = ESLint.getErrorResults(results); + + assert.strictEqual(errorResults[0].warningCount, 0); + assert.strictEqual(errorResults[0].fixableWarningCount, 0); + }); + + it("should return 0 error or warning messages even when the file has warnings", async () => { + const engine = new ESLint({ + ignorePath: path.join(fixtureDir, ".eslintignore"), + cwd: path.join(fixtureDir, "..") + }); + const options = { + filePath: "fixtures/passing.js", + warnIgnored: true + }; + const results = await engine.lintText("var bar = foo;", options); + const errorReport = ESLint.getErrorResults(results); + + assert.strictEqual(errorReport.length, 0); + assert.strictEqual(results.length, 1); + assert.strictEqual(results[0].errorCount, 0); + assert.strictEqual(results[0].warningCount, 1); + }); + + it("should return source code of file in the `source` property", async () => { + process.chdir(originalDir); + const engine = new ESLint({ + useEslintrc: false, + overrideConfig: { + rules: { quotes: [2, "double"] } + } + }); + const results = await engine.lintText("var foo = 'bar';"); + const errorResults = ESLint.getErrorResults(results); + + assert.strictEqual(errorResults[0].messages.length, 1); + assert.strictEqual(errorResults[0].source, "var foo = 'bar';"); + }); + + it("should contain `output` property after fixes", async () => { + process.chdir(originalDir); + const engine = new ESLint({ + useEslintrc: false, + fix: true, + overrideConfig: { + rules: { + semi: 2, + "no-console": 2 + } + } + }); + const results = await engine.lintText("console.log('foo')"); + const errorResults = ESLint.getErrorResults(results); + + assert.strictEqual(errorResults[0].messages.length, 1); + assert.strictEqual(errorResults[0].output, "console.log('foo');"); + }); + }); + + describe("outputFixes()", () => { + afterEach(() => { + sinon.verifyAndRestore(); + }); + + it("should call fs.writeFile() for each result with output", async () => { + const fakeFS = leche.fake(fs); + const spy = fakeFS.writeFile = sinon.spy(callLastArgument); + const localESLint = proxyquire("../../../lib/eslint/eslint", { + fs: fakeFS + }).ESLint; + const results = [ + { + filePath: path.resolve("foo.js"), + output: "bar" + }, + { + filePath: path.resolve("bar.js"), + output: "baz" + } + ]; + + await localESLint.outputFixes(results); + + assert.strictEqual(spy.callCount, 2); + assert(spy.firstCall.calledWithExactly(path.resolve("foo.js"), "bar", sinon.match.func), "First call was incorrect."); + assert(spy.secondCall.calledWithExactly(path.resolve("bar.js"), "baz", sinon.match.func), "Second call was incorrect."); + }); + + it("should call fs.writeFile() for each result with output and not at all for a result without output", async () => { + const fakeFS = leche.fake(fs); + const spy = fakeFS.writeFile = sinon.spy(callLastArgument); + const localESLint = proxyquire("../../../lib/eslint/eslint", { + fs: fakeFS + }).ESLint; + const results = [ + { + filePath: path.resolve("foo.js"), + output: "bar" + }, + { + filePath: path.resolve("abc.js") + }, + { + filePath: path.resolve("bar.js"), + output: "baz" + } + ]; + + await localESLint.outputFixes(results); + + assert.strictEqual(spy.callCount, 2); + assert(spy.firstCall.calledWithExactly(path.resolve("foo.js"), "bar", sinon.match.func), "First call was incorrect."); + assert(spy.secondCall.calledWithExactly(path.resolve("bar.js"), "baz", sinon.match.func), "Second call was incorrect."); + }); + + it("should throw if non object array is given to 'results' parameter", async () => { + await assert.rejects(() => ESLint.outputFixes(null), /'results' must be an array/u); + await assert.rejects(() => ESLint.outputFixes([null]), /'results' must include only objects/u); + }); + }); + + describe("when evaluating code with comments to change config when allowInlineConfig is disabled", () => { + it("should report a violation for disabling rules", async () => { + const code = [ + "alert('test'); // eslint-disable-line no-alert" + ].join("\n"); + const config = { + ignore: true, + allowInlineConfig: false, + overrideConfig: { + env: { browser: true }, + rules: { + "eol-last": 0, + "no-alert": 1, + "no-trailing-spaces": 0, + strict: 0, + quotes: 0 + } + } + }; + const eslintCLI = new ESLint(config); + const results = await eslintCLI.lintText(code); + const messages = results[0].messages; + + assert.strictEqual(messages.length, 1); + assert.strictEqual(messages[0].ruleId, "no-alert"); + }); + + it("should not report a violation by default", async () => { + const code = [ + "alert('test'); // eslint-disable-line no-alert" + ].join("\n"); + const config = { + ignore: true, + allowInlineConfig: true, + overrideConfig: { + env: { browser: true }, + rules: { + "eol-last": 0, + "no-alert": 1, + "no-trailing-spaces": 0, + strict: 0, + quotes: 0 + } + } + }; + const eslintCLI = new ESLint(config); + const results = await eslintCLI.lintText(code); + const messages = results[0].messages; + + assert.strictEqual(messages.length, 0); + }); + }); + + describe("when evaluating code when reportUnusedDisableDirectives is enabled", () => { + it("should report problems for unused eslint-disable directives", async () => { + const eslint = new ESLint({ useEslintrc: false, reportUnusedDisableDirectives: "error" }); + + assert.deepStrictEqual( + await eslint.lintText("/* eslint-disable */"), + [ + { + filePath: "", + messages: [ + { + ruleId: null, + message: "Unused eslint-disable directive (no problems were reported).", + line: 1, + column: 1, + severity: 2, + nodeType: null + } + ], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + source: "/* eslint-disable */", + usedDeprecatedRules: [] + } + ] + ); + }); + }); + + describe("when retreiving version number", () => { + it("should return current version number", () => { + const eslintCLI = require("../../../lib/eslint").ESLint; + const version = eslintCLI.version; + + assert.strictEqual(typeof version, "string"); + assert(parseInt(version[0], 10) >= 3); + }); + }); + + describe("mutability", () => { + describe("plugins", () => { + it("Loading plugin in one instance doesn't mutate to another instance", async () => { + const filePath = getFixturePath("single-quoted.js"); + const engine1 = eslintWithPlugins({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + overrideConfig: { + plugins: ["example"], + rules: { "example/example-rule": 1 } + } + }); + const engine2 = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false + }); + const fileConfig1 = await engine1.calculateConfigForFile(filePath); + const fileConfig2 = await engine2.calculateConfigForFile(filePath); + + // plugin + assert.deepStrictEqual(fileConfig1.plugins, ["example"], "Plugin is present for engine 1"); + assert.deepStrictEqual(fileConfig2.plugins, [], "Plugin is not present for engine 2"); + }); + }); + + describe("rules", () => { + it("Loading rules in one instance doesn't mutate to another instance", async () => { + const filePath = getFixturePath("single-quoted.js"); + const engine1 = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false, + overrideConfig: { rules: { "example/example-rule": 1 } } + }); + const engine2 = new ESLint({ + cwd: path.join(fixtureDir, ".."), + useEslintrc: false + }); + const fileConfig1 = await engine1.calculateConfigForFile(filePath); + const fileConfig2 = await engine2.calculateConfigForFile(filePath); + + // plugin + assert.deepStrictEqual(fileConfig1.rules["example/example-rule"], [1], "example is present for engine 1"); + assert.strictEqual(fileConfig2.rules["example/example-rule"], void 0, "example is not present for engine 2"); + }); + }); + }); + + describe("with ignorePatterns config", () => { + const root = getFixturePath("cli-engine/ignore-patterns"); + + /** @type {typeof ESLint} */ + let InMemoryESLint; + + describe("ignorePatterns can add an ignore pattern ('foo.js').", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: "foo.js" + }), + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("foo.js"), true); + assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'bar.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("bar.js"), false); + assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), false); + }); + + it("'lintFiles()' should not verify 'foo.js'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar.js"), + path.join(root, "subdir/bar.js") + ]); + }); + }); + + describe("ignorePatterns can add ignore patterns ('foo.js', '/bar.js').", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: ["foo.js", "/bar.js"] + }), + "foo.js": "", + "bar.js": "", + "baz.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "", + "subdir/baz.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("foo.js"), true); + assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), true); + }); + + it("'isPathIgnored()' should return 'true' for '/bar.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("bar.js"), true); + assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), false); + }); + + it("'lintFiles()' should not verify 'foo.js' and '/bar.js'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "baz.js"), + path.join(root, "subdir/bar.js"), + path.join(root, "subdir/baz.js") + ]); + }); + }); + + describe("ignorePatterns can unignore '/node_modules/foo'.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: "!/node_modules/foo" + }), + "node_modules/foo/index.js": "", + "node_modules/foo/.dot.js": "", + "node_modules/bar/index.js": "", + "foo.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'false' for 'node_modules/foo/index.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("node_modules/foo/index.js"), false); + }); + + it("'isPathIgnored()' should return 'true' for 'node_modules/foo/.dot.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("node_modules/foo/.dot.js"), true); + }); + + it("'isPathIgnored()' should return 'true' for 'node_modules/bar/index.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("node_modules/bar/index.js"), true); + }); + + it("'lintFiles()' should verify 'node_modules/foo/index.js'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "foo.js"), + path.join(root, "node_modules/foo/index.js") + ]); + }); + }); + + describe("ignorePatterns can unignore '.eslintrc.js'.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "!.eslintrc.js" + })}`, + "foo.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'false' for '.eslintrc.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored(".eslintrc.js"), false); + }); + + it("'lintFiles()' should verify '.eslintrc.js'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, ".eslintrc.js"), + path.join(root, "foo.js") + ]); + }); + }); + + describe(".eslintignore can re-ignore files that are unignored by ignorePatterns.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "!.*" + })}`, + ".eslintignore": ".foo*", + ".foo.js": "", + ".bar.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'true' for re-ignored '.foo.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored(".foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for unignored '.bar.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored(".bar.js"), false); + }); + + it("'lintFiles()' should not verify re-ignored '.foo.js'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, ".bar.js"), + path.join(root, ".eslintrc.js") + ]); + }); + }); + + describe(".eslintignore can unignore files that are ignored by ignorePatterns.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "*.js" + })}`, + ".eslintignore": "!foo.js", + "foo.js": "", + "bar.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'false' for unignored 'foo.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("foo.js"), false); + }); + + it("'isPathIgnored()' should return 'true' for ignored 'bar.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("bar.js"), true); + }); + + it("'lintFiles()' should verify unignored 'foo.js'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "foo.js") + ]); + }); + }); + + describe("ignorePatterns in the config file in a child directory affects to only in the directory.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: "foo.js" + }), + "subdir/.eslintrc.json": JSON.stringify({ + ignorePatterns: "bar.js" + }), + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "", + "subdir/subsubdir/foo.js": "", + "subdir/subsubdir/bar.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("foo.js"), true); + assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), true); + assert.strictEqual(await engine.isPathIgnored("subdir/subsubdir/foo.js"), true); + }); + + it("'isPathIgnored()' should return 'true' for 'bar.js' in 'subdir'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), true); + assert.strictEqual(await engine.isPathIgnored("subdir/subsubdir/bar.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'bar.js' in the outside of 'subdir'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("bar.js"), false); + }); + + it("'lintFiles()' should verify 'bar.js' in the outside of 'subdir'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar.js") + ]); + }); + }); + + describe("ignorePatterns in the config file in a child directory can unignore the ignored files in the parent directory's config.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: "foo.js" + }), + "subdir/.eslintrc.json": JSON.stringify({ + ignorePatterns: "!foo.js" + }), + "foo.js": "", + "subdir/foo.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'true' for 'foo.js' in the root directory.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'foo.js' in the child directory.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), false); + }); + + it("'lintFiles()' should verify 'foo.js' in the child directory.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "subdir/foo.js") + ]); + }); + }); + + describe(".eslintignore can unignore files that are ignored by ignorePatterns in the config file in the child directory.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({}), + "subdir/.eslintrc.json": JSON.stringify({ + ignorePatterns: "*.js" + }), + ".eslintignore": "!foo.js", + "foo.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'false' for unignored 'foo.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("foo.js"), false); + assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), false); + }); + + it("'isPathIgnored()' should return 'true' for ignored 'bar.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), true); + }); + + it("'lintFiles()' should verify unignored 'foo.js'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "foo.js"), + path.join(root, "subdir/foo.js") + ]); + }); + }); + + describe("if the config in a child directory has 'root:true', ignorePatterns in the config file in the parent directory should not be used.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: "foo.js" + }), + "subdir/.eslintrc.json": JSON.stringify({ + root: true, + ignorePatterns: "bar.js" + }), + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'true' for 'foo.js' in the root directory.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'bar.js' in the root directory.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("bar.js"), false); + }); + + it("'isPathIgnored()' should return 'false' for 'foo.js' in the child directory.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), false); + }); + + it("'isPathIgnored()' should return 'true' for 'bar.js' in the child directory.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), true); + }); + + it("'lintFiles()' should verify 'bar.js' in the root directory and 'foo.js' in the child directory.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar.js"), + path.join(root, "subdir/foo.js") + ]); + }); + }); + + describe("even if the config in a child directory has 'root:true', .eslintignore should be used.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({}), + "subdir/.eslintrc.json": JSON.stringify({ + root: true, + ignorePatterns: "bar.js" + }), + ".eslintignore": "foo.js", + "foo.js": "", + "bar.js": "", + "subdir/foo.js": "", + "subdir/bar.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("foo.js"), true); + assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'bar.js' in the root directory.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("bar.js"), false); + }); + + it("'isPathIgnored()' should return 'true' for 'bar.js' in the child directory.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("subdir/bar.js"), true); + }); + + it("'lintFiles()' should verify 'bar.js' in the root directory.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar.js") + ]); + }); + }); + + describe("ignorePatterns in the shareable config should be used.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "foo.js" + })}`, + ".eslintrc.json": JSON.stringify({ + extends: "one" + }), + "foo.js": "", + "bar.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'bar.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("bar.js"), false); + }); + + it("'lintFiles()' should verify 'bar.js'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar.js") + ]); + }); + }); + + describe("ignorePatterns in the shareable config should be relative to the entry config file.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "/foo.js" + })}`, + ".eslintrc.json": JSON.stringify({ + extends: "one" + }), + "foo.js": "", + "subdir/foo.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'subdir/foo.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("subdir/foo.js"), false); + }); + + it("'lintFiles()' should verify 'subdir/foo.js'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "subdir/foo.js") + ]); + }); + }); + + describe("ignorePatterns in a config file can unignore the files which are ignored by ignorePatterns in the shareable config.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + ignorePatterns: "*.js" + })}`, + ".eslintrc.json": JSON.stringify({ + extends: "one", + ignorePatterns: "!bar.js" + }), + "foo.js": "", + "bar.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'true' for 'foo.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("foo.js"), true); + }); + + it("'isPathIgnored()' should return 'false' for 'bar.js'.", async () => { + const engine = new InMemoryESLint(); + + assert.strictEqual(await engine.isPathIgnored("bar.js"), false); + }); + + it("'lintFiles()' should verify 'bar.js'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar.js") + ]); + }); + }); + + describe("ignorePatterns in a config file should not be used if --no-ignore option was given.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + ignorePatterns: "*.js" + }), + "foo.js": "" + } + }).ESLint; + }); + + it("'isPathIgnored()' should return 'false' for 'foo.js'.", async () => { + const engine = new InMemoryESLint({ ignore: false }); + + assert.strictEqual(await engine.isPathIgnored("foo.js"), false); + }); + + it("'lintFiles()' should verify 'foo.js'.", async () => { + const engine = new InMemoryESLint({ ignore: false }); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "foo.js") + ]); + }); + }); + + describe("ignorePatterns in overrides section is not allowed.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.js": `module.exports = ${JSON.stringify({ + overrides: [ + { + files: "*.js", + ignorePatterns: "foo.js" + } + ] + })}`, + "foo.js": "" + } + }).ESLint; + }); + + it("should throw a configuration error.", async () => { + await assert.rejects(async () => { + const engine = new InMemoryESLint(); + + await engine.lintFiles("*.js"); + }, /Unexpected top-level property "overrides\[0\]\.ignorePatterns"/u); + }); + }); + }); + + describe("'overrides[].files' adds lint targets", () => { + const root = getFixturePath("cli-engine/additional-lint-targets"); + let InMemoryESLint; + + describe("if { files: 'foo/*.txt', excludedFiles: '**/ignore.txt' } is present,", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + overrides: [ + { + files: "foo/*.txt", + excludedFiles: "**/ignore.txt" + } + ] + }), + "foo/nested/test.txt": "", + "foo/test.js": "", + "foo/test.txt": "", + "foo/ignore.txt": "", + "bar/test.js": "", + "bar/test.txt": "", + "bar/ignore.txt": "", + "test.js": "", + "test.txt": "", + "ignore.txt": "" + } + }).ESLint; + }); + + it("'lintFiles()' with a directory path should contain 'foo/test.txt'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles(".")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar/test.js"), + path.join(root, "foo/test.js"), + path.join(root, "foo/test.txt"), + path.join(root, "test.js") + ]); + }); + + it("'lintFiles()' with a glob pattern '*.js' should not contain 'foo/test.txt'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar/test.js"), + path.join(root, "foo/test.js"), + path.join(root, "test.js") + ]); + }); + }); + + describe("if { files: 'foo/**/*.txt' } is present,", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + overrides: [ + { + files: "foo/**/*.txt" + } + ] + }), + "foo/nested/test.txt": "", + "foo/test.js": "", + "foo/test.txt": "", + "bar/test.js": "", + "bar/test.txt": "", + "test.js": "", + "test.txt": "" + } + }).ESLint; + }); + + it("'lintFiles()' with a directory path should contain 'foo/test.txt' and 'foo/nested/test.txt'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles(".")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar/test.js"), + path.join(root, "foo/nested/test.txt"), + path.join(root, "foo/test.js"), + path.join(root, "foo/test.txt"), + path.join(root, "test.js") + ]); + }); + }); + + describe("if { files: 'foo/**/*' } is present,", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + ".eslintrc.json": JSON.stringify({ + overrides: [ + { + files: "foo/**/*" + } + ] + }), + "foo/nested/test.txt": "", + "foo/test.js": "", + "foo/test.txt": "", + "bar/test.js": "", + "bar/test.txt": "", + "test.js": "", + "test.txt": "" + } + }).ESLint; + }); + + it("'lintFiles()' with a directory path should NOT contain 'foo/test.txt' and 'foo/nested/test.txt'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles(".")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar/test.js"), + path.join(root, "foo/test.js"), + path.join(root, "test.js") + ]); + }); + }); + + describe("if { files: 'foo/**/*.txt' } is present in a shareable config,", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-config-foo/index.js": `module.exports = ${JSON.stringify({ + overrides: [ + { + files: "foo/**/*.txt" + } + ] + })}`, + ".eslintrc.json": JSON.stringify({ + extends: "foo" + }), + "foo/nested/test.txt": "", + "foo/test.js": "", + "foo/test.txt": "", + "bar/test.js": "", + "bar/test.txt": "", + "test.js": "", + "test.txt": "" + } + }).ESLint; + }); + + it("'lintFiles()' with a directory path should contain 'foo/test.txt' and 'foo/nested/test.txt'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles(".")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar/test.js"), + path.join(root, "foo/nested/test.txt"), + path.join(root, "foo/test.js"), + path.join(root, "foo/test.txt"), + path.join(root, "test.js") + ]); + }); + }); + + describe("if { files: 'foo/**/*.txt' } is present in a plugin config,", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-plugin-foo/index.js": `exports.configs = ${JSON.stringify({ + bar: { + overrides: [ + { + files: "foo/**/*.txt" + } + ] + } + })}`, + ".eslintrc.json": JSON.stringify({ + extends: "plugin:foo/bar" + }), + "foo/nested/test.txt": "", + "foo/test.js": "", + "foo/test.txt": "", + "bar/test.js": "", + "bar/test.txt": "", + "test.js": "", + "test.txt": "" + } + }).ESLint; + }); + + it("'lintFiles()' with a directory path should contain 'foo/test.txt' and 'foo/nested/test.txt'.", async () => { + const engine = new InMemoryESLint(); + const filePaths = (await engine.lintFiles(".")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(filePaths, [ + path.join(root, "bar/test.js"), + path.join(root, "foo/nested/test.txt"), + path.join(root, "foo/test.js"), + path.join(root, "foo/test.txt"), + path.join(root, "test.js") + ]); + }); + }); + }); + + describe("'ignorePatterns', 'overrides[].files', and 'overrides[].excludedFiles' of the configuration that the '--config' option provided should be resolved from CWD.", () => { + const root = getFixturePath("cli-engine/config-and-overrides-files"); + + /** @type {ESLint} */ + let InMemoryESLint; + + describe("if { files: 'foo/*.txt', ... } is present by '--config node_modules/myconf/.eslintrc.json',", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/myconf/.eslintrc.json": JSON.stringify({ + overrides: [ + { + files: "foo/*.js", + rules: { + eqeqeq: "error" + } + } + ] + }), + "node_modules/myconf/foo/test.js": "a == b", + "foo/test.js": "a == b" + } + }).ESLint; + }); + + it("'lintFiles()' with 'foo/test.js' should use the override entry.", async () => { + const engine = new InMemoryESLint({ + overrideConfigFile: "node_modules/myconf/.eslintrc.json", + cwd: root, + ignore: false, + useEslintrc: false + }); + const results = await engine.lintFiles("foo/test.js"); + + // Expected to be an 'eqeqeq' error because the file matches to `$CWD/foo/*.js`. + assert.deepStrictEqual(results, [ + { + errorCount: 1, + filePath: path.join(root, "foo/test.js"), + fixableErrorCount: 0, + fixableWarningCount: 0, + messages: [ + { + column: 3, + endColumn: 5, + endLine: 1, + line: 1, + message: "Expected '===' and instead saw '=='.", + messageId: "unexpected", + nodeType: "BinaryExpression", + ruleId: "eqeqeq", + severity: 2 + } + ], + source: "a == b", + usedDeprecatedRules: [], + warningCount: 0 + } + ]); + }); + + it("'lintFiles()' with 'node_modules/myconf/foo/test.js' should NOT use the override entry.", async () => { + const engine = new InMemoryESLint({ + overrideConfigFile: "node_modules/myconf/.eslintrc.json", + cwd: root, + ignore: false, + useEslintrc: false + }); + const results = await engine.lintFiles("node_modules/myconf/foo/test.js"); + + // Expected to be no errors because the file doesn't match to `$CWD/foo/*.js`. + assert.deepStrictEqual(results, [ + { + errorCount: 0, + filePath: path.join(root, "node_modules/myconf/foo/test.js"), + fixableErrorCount: 0, + fixableWarningCount: 0, + messages: [], + usedDeprecatedRules: [], + warningCount: 0 + } + ]); + }); + }); + + describe("if { files: '*', excludedFiles: 'foo/*.txt', ... } is present by '--config node_modules/myconf/.eslintrc.json',", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/myconf/.eslintrc.json": JSON.stringify({ + overrides: [ + { + files: "*", + excludedFiles: "foo/*.js", + rules: { + eqeqeq: "error" + } + } + ] + }), + "node_modules/myconf/foo/test.js": "a == b", + "foo/test.js": "a == b" + } + }).ESLint; + }); + + it("'lintFiles()' with 'foo/test.js' should NOT use the override entry.", async () => { + const engine = new InMemoryESLint({ + overrideConfigFile: "node_modules/myconf/.eslintrc.json", + cwd: root, + ignore: false, + useEslintrc: false + }); + const results = await engine.lintFiles("foo/test.js"); + + // Expected to be no errors because the file matches to `$CWD/foo/*.js`. + assert.deepStrictEqual(results, [ + { + errorCount: 0, + filePath: path.join(root, "foo/test.js"), + fixableErrorCount: 0, + fixableWarningCount: 0, + messages: [], + usedDeprecatedRules: [], + warningCount: 0 + } + ]); + }); + + it("'lintFiles()' with 'node_modules/myconf/foo/test.js' should use the override entry.", async () => { + const engine = new InMemoryESLint({ + overrideConfigFile: "node_modules/myconf/.eslintrc.json", + cwd: root, + ignore: false, + useEslintrc: false + }); + const results = await engine.lintFiles("node_modules/myconf/foo/test.js"); + + // Expected to be an 'eqeqeq' error because the file doesn't match to `$CWD/foo/*.js`. + assert.deepStrictEqual(results, [ + { + errorCount: 1, + filePath: path.join(root, "node_modules/myconf/foo/test.js"), + fixableErrorCount: 0, + fixableWarningCount: 0, + messages: [ + { + column: 3, + endColumn: 5, + endLine: 1, + line: 1, + message: "Expected '===' and instead saw '=='.", + messageId: "unexpected", + nodeType: "BinaryExpression", + ruleId: "eqeqeq", + severity: 2 + } + ], + source: "a == b", + usedDeprecatedRules: [], + warningCount: 0 + } + ]); + }); + }); + + describe("if { ignorePatterns: 'foo/*.txt', ... } is present by '--config node_modules/myconf/.eslintrc.json',", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/myconf/.eslintrc.json": JSON.stringify({ + ignorePatterns: ["!/node_modules/myconf", "foo/*.js"], + rules: { + eqeqeq: "error" + } + }), + "node_modules/myconf/foo/test.js": "a == b", + "foo/test.js": "a == b" + } + }).ESLint; + }); + + it("'lintFiles()' with '**/*.js' should iterate 'node_modules/myconf/foo/test.js' but not 'foo/test.js'.", async () => { + const engine = new InMemoryESLint({ + overrideConfigFile: "node_modules/myconf/.eslintrc.json", + cwd: root, + useEslintrc: false + }); + const files = (await engine.lintFiles("**/*.js")) + .map(r => r.filePath) + .sort(); + + assert.deepStrictEqual(files, [ + path.join(root, "node_modules/myconf/foo/test.js") + ]); + }); + }); + }); + + describe("plugin conflicts", () => { + let uid = 0; + let root = ""; + + beforeEach(() => { + root = getFixturePath(`eslint/plugin-conflicts-${++uid}`); + }); + + /** @type {typeof ESLint} */ + let InMemoryESLint; + + /** + * Verify thrown errors. + * @param {() => Promise} f The function to run and throw. + * @param {Record} props The properties to verify. + * @returns {Promise} void + */ + async function assertThrows(f, props) { + try { + await f(); + } catch (error) { + for (const [key, value] of Object.entries(props)) { + assert.deepStrictEqual(error[key], value, key); + } + return; + } + + assert.fail("Function should throw an error, but not."); + } + + describe("between a config file and linear extendees.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + "node_modules/eslint-config-one/node_modules/eslint-plugin-foo/index.js": "", + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + extends: ["two"], + plugins: ["foo"] + })}`, + "node_modules/eslint-config-two/node_modules/eslint-plugin-foo/index.js": "", + "node_modules/eslint-config-two/index.js": `module.exports = ${JSON.stringify({ + plugins: ["foo"] + })}`, + ".eslintrc.json": JSON.stringify({ + extends: ["one"], + plugins: ["foo"] + }), + "test.js": "" + } + }).ESLint; + }); + + it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file.)", async () => { + const engine = new InMemoryESLint({ cwd: root }); + + await engine.lintFiles("test.js"); + }); + }); + + describe("between a config file and same-depth extendees.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + "node_modules/eslint-config-one/node_modules/eslint-plugin-foo/index.js": "", + "node_modules/eslint-config-one/index.js": `module.exports = ${JSON.stringify({ + plugins: ["foo"] + })}`, + "node_modules/eslint-config-two/node_modules/eslint-plugin-foo/index.js": "", + "node_modules/eslint-config-two/index.js": `module.exports = ${JSON.stringify({ + plugins: ["foo"] + })}`, + ".eslintrc.json": JSON.stringify({ + extends: ["one", "two"], + plugins: ["foo"] + }), + "test.js": "" + } + }).ESLint; + }); + + it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file.)", async () => { + const engine = new InMemoryESLint({ cwd: root }); + + await engine.lintFiles("test.js"); + }); + }); + + describe("between two config files in different directories, with single node_modules.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + ".eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/test.js": "" + } + }).ESLint; + }); + + it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file, but there are two entry config files, but node_modules directory is unique.)", async () => { + const engine = new InMemoryESLint({ cwd: root }); + + await engine.lintFiles("subdir/test.js"); + }); + }); + + describe("between two config files in different directories, with multiple node_modules.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + ".eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/node_modules/eslint-plugin-foo/index.js": "", + "subdir/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/test.js": "" + } + }).ESLint; + }); + + it("'lintFiles()' should throw plugin-conflict error. (Load the plugin from the base directory of the entry config file, but there are two entry config files.)", async () => { + const engine = new InMemoryESLint({ cwd: root }); + + await assertThrows( + () => engine.lintFiles("subdir/test.js"), + { + message: `Plugin "foo" was conflicted between "subdir${path.sep}.eslintrc.json" and ".eslintrc.json".`, + messageTemplate: "plugin-conflict", + messageData: { + pluginId: "foo", + plugins: [ + { + filePath: path.join(root, "subdir/node_modules/eslint-plugin-foo/index.js"), + importerName: `subdir${path.sep}.eslintrc.json` + }, + { + filePath: path.join(root, "node_modules/eslint-plugin-foo/index.js"), + importerName: ".eslintrc.json" + } + ] + } + } + ); + }); + }); + + describe("between '--config' option and a regular config file, with single node_modules.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + "node_modules/mine/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + ".eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "test.js": "" + } + }).ESLint; + }); + + it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file, but there are two entry config files, but node_modules directory is unique.)", async () => { + const engine = new InMemoryESLint({ + cwd: root, + overrideConfigFile: "node_modules/mine/.eslintrc.json" + }); + + await engine.lintFiles("test.js"); + }); + }); + + describe("between '--config' option and a regular config file, with multiple node_modules.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + "node_modules/mine/node_modules/eslint-plugin-foo/index.js": "", + "node_modules/mine/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + ".eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "test.js": "" + } + }).ESLint; + }); + + it("'lintFiles()' should throw plugin-conflict error. (Load the plugin from the base directory of the entry config file, but there are two entry config files.)", async () => { + const engine = new InMemoryESLint({ + cwd: root, + overrideConfigFile: "node_modules/mine/.eslintrc.json" + }); + + await assertThrows( + () => engine.lintFiles("test.js"), + { + message: "Plugin \"foo\" was conflicted between \"--config\" and \".eslintrc.json\".", + messageTemplate: "plugin-conflict", + messageData: { + pluginId: "foo", + plugins: [ + { + filePath: path.join(root, "node_modules/mine/node_modules/eslint-plugin-foo/index.js"), + importerName: "--config" + }, + { + filePath: path.join(root, "node_modules/eslint-plugin-foo/index.js"), + importerName: ".eslintrc.json" + } + ] + } + } + ); + }); + }); + + describe("between '--plugin' option and a regular config file, with single node_modules.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + "subdir/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/test.js": "" + } + }).ESLint; + }); + + it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from both CWD and the base directory of the entry config file, but node_modules directory is unique.)", async () => { + const engine = new InMemoryESLint({ + cwd: root, + overrideConfig: { plugins: ["foo"] } + }); + + await engine.lintFiles("subdir/test.js"); + }); + }); + + describe("between '--plugin' option and a regular config file, with multiple node_modules.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + "subdir/node_modules/eslint-plugin-foo/index.js": "", + "subdir/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/test.js": "" + } + }).ESLint; + }); + + it("'lintFiles()' should throw plugin-conflict error. (Load the plugin from both CWD and the base directory of the entry config file.)", async () => { + const engine = new InMemoryESLint({ + cwd: root, + overrideConfig: { plugins: ["foo"] } + }); + + await assertThrows( + () => engine.lintFiles("subdir/test.js"), + { + message: `Plugin "foo" was conflicted between "CLIOptions" and "subdir${path.sep}.eslintrc.json".`, + messageTemplate: "plugin-conflict", + messageData: { + pluginId: "foo", + plugins: [ + { + filePath: path.join(root, "node_modules/eslint-plugin-foo/index.js"), + importerName: "CLIOptions" + }, + { + filePath: path.join(root, "subdir/node_modules/eslint-plugin-foo/index.js"), + importerName: `subdir${path.sep}.eslintrc.json` + } + ] + } + } + ); + }); + }); + + describe("'--resolve-plugins-relative-to' option overrides the location that ESLint load plugins from.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "node_modules/eslint-plugin-foo/index.js": "", + ".eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/node_modules/eslint-plugin-foo/index.js": "", + "subdir/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "subdir/test.js": "" + } + }).ESLint; + }); + + it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from '--resolve-plugins-relative-to'.)", async () => { + const engine = new InMemoryESLint({ + cwd: root, + resolvePluginsRelativeTo: root + }); + + await engine.lintFiles("subdir/test.js"); + }); + }); + + describe("between two config files with different target files.", () => { + beforeEach(() => { + InMemoryESLint = defineESLintWithInMemoryFileSystem({ + cwd: () => root, + files: { + "one/node_modules/eslint-plugin-foo/index.js": "", + "one/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "one/test.js": "", + "two/node_modules/eslint-plugin-foo/index.js": "", + "two/.eslintrc.json": JSON.stringify({ + plugins: ["foo"] + }), + "two/test.js": "" + } + }).ESLint; + }); + + it("'lintFiles()' should NOT throw plugin-conflict error. (Load the plugin from the base directory of the entry config file for each target file. Not related to each other.)", async () => { + const engine = new InMemoryESLint({ cwd: root }); + const results = await engine.lintFiles("*/test.js"); + + assert.strictEqual(results.length, 2); + }); + }); + }); +}); diff --git a/eslint/tests/lib/init/autoconfig.js b/eslint/tests/lib/init/autoconfig.js index 3ce766a..1fa2b04 100644 --- a/eslint/tests/lib/init/autoconfig.js +++ b/eslint/tests/lib/init/autoconfig.js @@ -12,7 +12,8 @@ const assert = require("chai").assert, autoconfig = require("../../../lib/init/autoconfig"), sourceCodeUtils = require("../../../lib/init/source-code-utils"), - baseDefaultOptions = require("../../../conf/default-cli-options"); + baseDefaultOptions = require("../../../conf/default-cli-options"), + recommendedConfig = require("../../conf/eslint-recommended"); const defaultOptions = Object.assign({}, baseDefaultOptions, { cwd: process.cwd() }); @@ -328,4 +329,43 @@ describe("autoconfig", () => { }); }); }); + + describe("extendFromRecommended()", () => { + it("should return a configuration which has `extends` key with Array type value", () => { + const oldConfig = { extends: [], rules: {} }; + const newConfig = autoconfig.extendFromRecommended(oldConfig); + + assert.exists(newConfig.extends); + assert.isArray(newConfig.extends); + }); + + it("should return a configuration which has array property `extends`", () => { + const oldConfig = { extends: [], rules: {} }; + const newConfig = autoconfig.extendFromRecommended(oldConfig); + + assert.include(newConfig.extends, "eslint:recommended"); + }); + + it("should return a configuration which preserves the previous extending configurations", () => { + const oldConfig = { extends: ["previous:configuration1", "previous:configuration2"], rules: {} }; + const newConfig = autoconfig.extendFromRecommended(oldConfig); + + assert.includeMembers(newConfig.extends, oldConfig.extends); + }); + + it("should return a configuration which has `eslint:recommended` at the first of `extends`", () => { + const oldConfig = { extends: ["previous:configuration1", "previous:configuration2"], rules: {} }; + const newConfig = autoconfig.extendFromRecommended(oldConfig); + const [firstExtendInNewConfig] = newConfig.extends; + + assert.strictEqual(firstExtendInNewConfig, "eslint:recommended"); + }); + + it("should return a configuration which not includes rules configured in `eslint:recommended`", () => { + const oldConfig = { extends: [], rules: { ...recommendedConfig.rules } }; + const newConfig = autoconfig.extendFromRecommended(oldConfig); + + assert.notInclude(newConfig.rules, oldConfig.rules); + }); + }); }); diff --git a/eslint/tests/lib/init/config-initializer.js b/eslint/tests/lib/init/config-initializer.js index 3393ae2..5f4690a 100644 --- a/eslint/tests/lib/init/config-initializer.js +++ b/eslint/tests/lib/init/config-initializer.js @@ -15,6 +15,7 @@ const assert = require("chai").assert, os = require("os"), sinon = require("sinon"), sh = require("shelljs"), + espree = require("espree"), autoconfig = require("../../../lib/init/autoconfig"), npmUtils = require("../../../lib/init/npm-utils"); @@ -138,7 +139,7 @@ describe("configInitializer", () => { assert.strictEqual(config.env.es6, true); assert.strictEqual(config.globals.Atomics, "readonly"); assert.strictEqual(config.globals.SharedArrayBuffer, "readonly"); - assert.strictEqual(config.parserOptions.ecmaVersion, 2018); + assert.strictEqual(config.parserOptions.ecmaVersion, espree.latestEcmaVersion); assert.strictEqual(config.parserOptions.sourceType, "module"); assert.strictEqual(config.env.browser, true); assert.strictEqual(config.extends, "eslint:recommended"); @@ -156,7 +157,7 @@ describe("configInitializer", () => { const config = init.processAnswers(answers); assert.strictEqual(config.parserOptions.ecmaFeatures.jsx, true); - assert.strictEqual(config.parserOptions.ecmaVersion, 2018); + assert.strictEqual(config.parserOptions.ecmaVersion, espree.latestEcmaVersion); assert.deepStrictEqual(config.plugins, ["react"]); }); @@ -164,7 +165,7 @@ describe("configInitializer", () => { answers.framework = "vue"; const config = init.processAnswers(answers); - assert.strictEqual(config.parserOptions.ecmaVersion, 2018); + assert.strictEqual(config.parserOptions.ecmaVersion, espree.latestEcmaVersion); assert.deepStrictEqual(config.plugins, ["vue"]); assert.deepStrictEqual(config.extends, ["eslint:recommended", "plugin:vue/essential"]); }); diff --git a/eslint/tests/lib/init/npm-utils.js b/eslint/tests/lib/init/npm-utils.js index d0326bd..8465796 100644 --- a/eslint/tests/lib/init/npm-utils.js +++ b/eslint/tests/lib/init/npm-utils.js @@ -14,7 +14,7 @@ const sinon = require("sinon"), npmUtils = require("../../../lib/init/npm-utils"), log = require("../../../lib/shared/logging"), - { defineInMemoryFs } = require("../_utils"); + { defineInMemoryFs } = require("../../_utils"); const proxyquire = require("proxyquire").noCallThru().noPreserveCache(); diff --git a/eslint/tests/lib/linter/code-path-analysis/code-path.js b/eslint/tests/lib/linter/code-path-analysis/code-path.js index 9c0ae9f..0c4fdd1 100644 --- a/eslint/tests/lib/linter/code-path-analysis/code-path.js +++ b/eslint/tests/lib/linter/code-path-analysis/code-path.js @@ -50,7 +50,7 @@ function getOrderOfTraversing(codePath, options, callback) { codePath.traverseSegments(options, (segment, controller) => { retv.push(segment.id); if (callback) { - callback(segment, controller); // eslint-disable-line callback-return + callback(segment, controller); // eslint-disable-line node/callback-return } }); diff --git a/eslint/tests/lib/linter/linter.js b/eslint/tests/lib/linter/linter.js index 74d231e..56d028e 100644 --- a/eslint/tests/lib/linter/linter.js +++ b/eslint/tests/lib/linter/linter.js @@ -2733,18 +2733,48 @@ describe("Linter", () => { }); describe("when evaluating code with comments which have colon in its value", () => { - const code = "/* eslint max-len: [2, 100, 2, {ignoreUrls: true, ignorePattern: \"data:image\\/|\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-\"}] */\nalert('test');"; + const code = String.raw` +/* eslint max-len: [2, 100, 2, {ignoreUrls: true, ignorePattern: "data:image\\/|\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-"}] */ +alert('test'); +`; it("should not parse errors, should report a violation", () => { const messages = linter.verify(code, {}, filename); assert.strictEqual(messages.length, 1); assert.strictEqual(messages[0].ruleId, "max-len"); - assert.strictEqual(messages[0].message, "This line has a length of 122. Maximum allowed is 100."); + assert.strictEqual(messages[0].message, "This line has a length of 129. Maximum allowed is 100."); assert.include(messages[0].nodeType, "Program"); }); }); + describe("when evaluating code with comments that contain escape sequences", () => { + const code = String.raw` +/* eslint max-len: ["error", 1, { ignoreComments: true, ignorePattern: "console\\.log\\(" }] */ +console.log("test"); +consolexlog("test2"); +var a = "test2"; +`; + + it("should validate correctly", () => { + const config = { rules: {} }; + const messages = linter.verify(code, config, filename); + const [message1, message2] = messages; + + assert.strictEqual(messages.length, 2); + assert.strictEqual(message1.ruleId, "max-len"); + assert.strictEqual(message1.message, "This line has a length of 21. Maximum allowed is 1."); + assert.strictEqual(message1.line, 4); + assert.strictEqual(message1.column, 1); + assert.include(message1.nodeType, "Program"); + assert.strictEqual(message2.ruleId, "max-len"); + assert.strictEqual(message2.message, "This line has a length of 16. Maximum allowed is 1."); + assert.strictEqual(message2.line, 5); + assert.strictEqual(message2.column, 1); + assert.include(message2.nodeType, "Program"); + }); + }); + describe("when evaluating a file with a shebang", () => { const code = "#!bin/program\n\nvar foo;;"; diff --git a/eslint/tests/lib/options.js b/eslint/tests/lib/options.js index 940804a..c84e46d 100644 --- a/eslint/tests/lib/options.js +++ b/eslint/tests/lib/options.js @@ -79,11 +79,10 @@ describe("options", () => { assert.strictEqual(currentOptions.ext[1], ".js"); }); - it("should return an array one item when not passed", () => { + it("should not exist when not passed", () => { const currentOptions = options.parse(""); - assert.isArray(currentOptions.ext); - assert.strictEqual(currentOptions.ext[0], ".js"); + assert.notProperty(currentOptions, "ext"); }); }); diff --git a/eslint/tests/lib/rule-tester/rule-tester.js b/eslint/tests/lib/rule-tester/rule-tester.js index 65a06da..fedc237 100644 --- a/eslint/tests/lib/rule-tester/rule-tester.js +++ b/eslint/tests/lib/rule-tester/rule-tester.js @@ -1829,7 +1829,7 @@ describe("RuleTester", () => { invalid: [] } ); - }, /A fatal parsing error occurred in autofix/u); + }, /A fatal parsing error occurred in autofix.\nError: .+\nAutofix output:\n.+/u); }); describe("sanitize test cases", () => { diff --git a/eslint/tests/lib/rules/array-bracket-newline.js b/eslint/tests/lib/rules/array-bracket-newline.js index d20c365..90ee821 100644 --- a/eslint/tests/lib/rules/array-bracket-newline.js +++ b/eslint/tests/lib/rules/array-bracket-newline.js @@ -39,6 +39,7 @@ ruleTester.run("array-bracket-newline", rule, { "var foo = [\nfunction foo() {\nreturn dosomething();\n}\n];", "var foo = [/* \nany comment\n */];", "var foo = [/* single line multiline comment for no real reason */];", + "var foo = [[1,2]]", // "always" { code: "var foo = [\n];", options: ["always"] }, @@ -47,9 +48,37 @@ ruleTester.run("array-bracket-newline", rule, { { code: "var foo = [\n/* any */\n1\n];", options: ["always"] }, { code: "var foo = [\n1, 2\n];", options: ["always"] }, { code: "var foo = [\n1, 2 // any comment\n];", options: ["always"] }, - { code: "var foo = [\n1, 2 /* any comment */\n];", options: ["always"] }, + { + code: "var foo = [\n1, 2 /* any comment */\n];", + options: ["always"] + }, { code: "var foo = [\n1,\n2\n];", options: ["always"] }, - { code: "var foo = [\nfunction foo() {\ndosomething();\n}\n];", options: ["always"] }, + { + code: "var foo = [\nfunction foo() {\ndosomething();\n}\n];", + options: ["always"] + }, + { + code: ` + var foo = [ + [ + 1,2 + ] + ] + `, + options: ["always"] + }, + { + code: ` + var foo = [ + 0, + [ + 1,2 + ], + 3 + ] + `, + options: ["always"] + }, // "never" { code: "var foo = [];", options: ["never"] }, @@ -58,7 +87,14 @@ ruleTester.run("array-bracket-newline", rule, { { code: "var foo = [1, 2];", options: ["never"] }, { code: "var foo = [1,\n2];", options: ["never"] }, { code: "var foo = [1,\n/* any comment */\n2];", options: ["never"] }, - { code: "var foo = [function foo() {\ndosomething();\n}];", options: ["never"] }, + { + code: "var foo = [function foo() {\ndosomething();\n}];", + options: ["never"] + }, + { + code: "var foo = [[1,2],3];", + options: ["never"] + }, // "consistent" { code: "var a = []", options: ["consistent"] }, @@ -69,67 +105,177 @@ ruleTester.run("array-bracket-newline", rule, { { code: "var a = [/**/\n1\n]", options: ["consistent"] }, { code: "var a = [/*\n*/1\n]", options: ["consistent"] }, { code: "var a = [//\n]", options: ["consistent"] }, + { + code: `var a = [ + [1,2] + ]`, + options: ["consistent"] + }, + { + code: `var a = [ + [[1,2]] + ]`, + options: ["consistent"] + }, // { multiline: true } { code: "var foo = [];", options: [{ multiline: true }] }, { code: "var foo = [1];", options: [{ multiline: true }] }, - { code: "var foo = /* any comment */[1];", options: [{ multiline: true }] }, - { code: "var foo = /* any comment */\n[1];", options: [{ multiline: true }] }, + { + code: "var foo = /* any comment */[1];", + options: [{ multiline: true }] + }, + { + code: "var foo = /* any comment */\n[1];", + options: [{ multiline: true }] + }, { code: "var foo = [1, 2];", options: [{ multiline: true }] }, - { code: "var foo = [ // any comment\n1, 2\n];", options: [{ multiline: true }] }, - { code: "var foo = [\n// any comment\n1, 2\n];", options: [{ multiline: true }] }, - { code: "var foo = [\n1, 2\n// any comment\n];", options: [{ multiline: true }] }, + { + code: "var foo = [ // any comment\n1, 2\n];", + options: [{ multiline: true }] + }, + { + code: "var foo = [\n// any comment\n1, 2\n];", + options: [{ multiline: true }] + }, + { + code: "var foo = [\n1, 2\n// any comment\n];", + options: [{ multiline: true }] + }, { code: "var foo = [\n1,\n2\n];", options: [{ multiline: true }] }, - { code: "var foo = [\nfunction foo() {\nreturn dosomething();\n}\n];", options: [{ multiline: true }] }, - { code: "var foo = [/* \nany comment\n */];", options: [{ multiline: true }] }, + { + code: "var foo = [\nfunction foo() {\nreturn dosomething();\n}\n];", + options: [{ multiline: true }] + }, + { + code: "var foo = [/* \nany comment\n */];", + options: [{ multiline: true }] + }, + { + code: "var foo = [\n1,\n2,\n[3,4],\n];", + options: [{ multiline: true }] + }, + { + code: "var foo = [\n1,\n2,\n[\n3,\n4\n],\n];", + options: [{ multiline: true }] + }, // { multiline: false } { code: "var foo = [];", options: [{ multiline: false }] }, { code: "var foo = [1];", options: [{ multiline: false }] }, - { code: "var foo = [1]/* any comment*/;", options: [{ multiline: false }] }, - { code: "var foo = [1]\n/* any comment*/\n;", options: [{ multiline: false }] }, + { + code: "var foo = [1]/* any comment*/;", + options: [{ multiline: false }] + }, + { + code: "var foo = [1]\n/* any comment*/\n;", + options: [{ multiline: false }] + }, { code: "var foo = [1, 2];", options: [{ multiline: false }] }, { code: "var foo = [1,\n2];", options: [{ multiline: false }] }, - { code: "var foo = [function foo() {\nreturn dosomething();\n}];", options: [{ multiline: false }] }, + { + code: "var foo = [function foo() {\nreturn dosomething();\n}];", + options: [{ multiline: false }] + }, + { code: "var foo = [1,\n2,[3,\n4]];", options: [{ multiline: false }] }, // { minItems: 2 } { code: "var foo = [];", options: [{ minItems: 2 }] }, { code: "var foo = [1];", options: [{ minItems: 2 }] }, { code: "var foo = [\n1, 2\n];", options: [{ minItems: 2 }] }, { code: "var foo = [\n1,\n2\n];", options: [{ minItems: 2 }] }, - { code: "var foo = [function foo() {\ndosomething();\n}];", options: [{ minItems: 2 }] }, + { + code: "var foo = [function foo() {\ndosomething();\n}];", + options: [{ minItems: 2 }] + }, + { + code: `var foo = [ + 1,[ + 2,3 + ] + ];`, + options: [{ minItems: 2 }] + }, + { + code: `var foo = [[ + 1,2 + ]]`, + options: [{ minItems: 2 }] + }, // { minItems: 0 } { code: "var foo = [\n];", options: [{ minItems: 0 }] }, { code: "var foo = [\n1\n];", options: [{ minItems: 0 }] }, { code: "var foo = [\n1, 2\n];", options: [{ minItems: 0 }] }, { code: "var foo = [\n1,\n2\n];", options: [{ minItems: 0 }] }, - { code: "var foo = [\nfunction foo() {\ndosomething();\n}\n];", options: [{ minItems: 0 }] }, + { + code: "var foo = [\nfunction foo() {\ndosomething();\n}\n];", + options: [{ minItems: 0 }] + }, // { minItems: null } { code: "var foo = [];", options: [{ minItems: null }] }, { code: "var foo = [1];", options: [{ minItems: null }] }, { code: "var foo = [1, 2];", options: [{ minItems: null }] }, { code: "var foo = [1,\n2];", options: [{ minItems: null }] }, - { code: "var foo = [function foo() {\ndosomething();\n}];", options: [{ minItems: null }] }, + { + code: "var foo = [function foo() {\ndosomething();\n}];", + options: [{ minItems: null }] + }, // { multiline: true, minItems: null } - { code: "var foo = [];", options: [{ multiline: true, minItems: null }] }, - { code: "var foo = [1];", options: [{ multiline: true, minItems: null }] }, - { code: "var foo = [1, 2];", options: [{ multiline: true, minItems: null }] }, - { code: "var foo = [\n1,\n2\n];", options: [{ multiline: true, minItems: null }] }, - { code: "var foo = [\nfunction foo() {\ndosomething();\n}\n];", options: [{ multiline: true, minItems: null }] }, + { + code: "var foo = [];", + options: [{ multiline: true, minItems: null }] + }, + { + code: "var foo = [1];", + options: [{ multiline: true, minItems: null }] + }, + { + code: "var foo = [1, 2];", + options: [{ multiline: true, minItems: null }] + }, + { + code: "var foo = [\n1,\n2\n];", + options: [{ multiline: true, minItems: null }] + }, + { + code: "var foo = [\nfunction foo() {\ndosomething();\n}\n];", + options: [{ multiline: true, minItems: null }] + }, // { multiline: true, minItems: 2 } { code: "var a = [];", options: [{ multiline: true, minItems: 2 }] }, { code: "var b = [1];", options: [{ multiline: true, minItems: 2 }] }, - { code: "var b = [ // any comment\n1\n];", options: [{ multiline: true, minItems: 2 }] }, - { code: "var b = [ /* any comment */ 1];", options: [{ multiline: true, minItems: 2 }] }, - { code: "var c = [\n1, 2\n];", options: [{ multiline: true, minItems: 2 }] }, - { code: "var c = [\n/* any comment */1, 2\n];", options: [{ multiline: true, minItems: 2 }] }, - { code: "var c = [\n1, /* any comment */ 2\n];", options: [{ multiline: true, minItems: 2 }] }, - { code: "var d = [\n1,\n2\n];", options: [{ multiline: true, minItems: 2 }] }, - { code: "var e = [\nfunction foo() {\ndosomething();\n}\n];", options: [{ multiline: true, minItems: 2 }] }, + { + code: "var b = [ // any comment\n1\n];", + options: [{ multiline: true, minItems: 2 }] + }, + { + code: "var b = [ /* any comment */ 1];", + options: [{ multiline: true, minItems: 2 }] + }, + { + code: "var c = [\n1, 2\n];", + options: [{ multiline: true, minItems: 2 }] + }, + { + code: "var c = [\n/* any comment */1, 2\n];", + options: [{ multiline: true, minItems: 2 }] + }, + { + code: "var c = [\n1, /* any comment */ 2\n];", + options: [{ multiline: true, minItems: 2 }] + }, + { + code: "var d = [\n1,\n2\n];", + options: [{ multiline: true, minItems: 2 }] + }, + { + code: "var e = [\nfunction foo() {\ndosomething();\n}\n];", + options: [{ multiline: true, minItems: 2 }] + }, /* * ArrayPattern @@ -137,53 +283,262 @@ ruleTester.run("array-bracket-newline", rule, { */ { code: "var [] = foo", parserOptions: { ecmaVersion: 6 } }, { code: "var [a] = foo;", parserOptions: { ecmaVersion: 6 } }, - { code: "var /* any comment */[a] = foo;", parserOptions: { ecmaVersion: 6 } }, - { code: "var /* any comment */\n[a] = foo;", parserOptions: { ecmaVersion: 6 } }, + { + code: "var /* any comment */[a] = foo;", + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var /* any comment */\n[a] = foo;", + parserOptions: { ecmaVersion: 6 } + }, { code: "var [a, b] = foo;", parserOptions: { ecmaVersion: 6 } }, - { code: "var [ // any comment\na, b\n] = foo;", parserOptions: { ecmaVersion: 6 } }, - { code: "var [\n// any comment\na, b\n] = foo;", parserOptions: { ecmaVersion: 6 } }, - { code: "var [\na, b\n// any comment\n] = foo;", parserOptions: { ecmaVersion: 6 } }, + { + code: "var [ // any comment\na, b\n] = foo;", + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [\n// any comment\na, b\n] = foo;", + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [\na, b\n// any comment\n] = foo;", + parserOptions: { ecmaVersion: 6 } + }, { code: "var [\na,\nb\n] = foo;", parserOptions: { ecmaVersion: 6 } }, // "always" - { code: "var [\n] = foo;", options: ["always"], parserOptions: { ecmaVersion: 6 } }, - { code: "var [\na\n] = foo;", options: ["always"], parserOptions: { ecmaVersion: 6 } }, - { code: "var [\n// any\na\n] = foo;", options: ["always"], parserOptions: { ecmaVersion: 6 } }, - { code: "var [\n/* any */\na\n] = foo;", options: ["always"], parserOptions: { ecmaVersion: 6 } }, - { code: "var [\na, b\n] = foo;", options: ["always"], parserOptions: { ecmaVersion: 6 } }, - { code: "var [\na, b // any comment\n] = foo;", options: ["always"], parserOptions: { ecmaVersion: 6 } }, - { code: "var [\na, b /* any comment */\n] = foo;", options: ["always"], parserOptions: { ecmaVersion: 6 } }, - { code: "var [\na,\nb\n] = foo;", options: ["always"], parserOptions: { ecmaVersion: 6 } }, + { + code: "var [\n] = foo;", + options: ["always"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [\na\n] = foo;", + options: ["always"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [\n// any\na\n] = foo;", + options: ["always"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [\n/* any */\na\n] = foo;", + options: ["always"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [\na, b\n] = foo;", + options: ["always"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [\na, b // any comment\n] = foo;", + options: ["always"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [\na, b /* any comment */\n] = foo;", + options: ["always"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [\na,\nb\n] = foo;", + options: ["always"], + parserOptions: { ecmaVersion: 6 } + }, // "consistent" - { code: "var [] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } }, - { code: "var [\n] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } }, - { code: "var [a] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } }, - { code: "var [\na\n] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } }, - { code: "var [//\na\n] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } }, - { code: "var [/**/\na\n] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } }, - { code: "var [/*\n*/a\n] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } }, - { code: "var [//\n] = foo", options: ["consistent"], parserOptions: { ecmaVersion: 6 } }, + { + code: "var [] = foo", + options: ["consistent"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [\n] = foo", + options: ["consistent"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [a] = foo", + options: ["consistent"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [\na\n] = foo", + options: ["consistent"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [//\na\n] = foo", + options: ["consistent"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [/**/\na\n] = foo", + options: ["consistent"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [/*\n*/a\n] = foo", + options: ["consistent"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [//\n] = foo", + options: ["consistent"], + parserOptions: { ecmaVersion: 6 } + }, // { multiline: true } - { code: "var [] = foo;", options: [{ multiline: true }], parserOptions: { ecmaVersion: 6 } }, - { code: "var [a] = foo;", options: [{ multiline: true }], parserOptions: { ecmaVersion: 6 } }, - { code: "var /* any comment */[a] = foo;", options: [{ multiline: true }], parserOptions: { ecmaVersion: 6 } }, - { code: "var /* any comment */\n[a] = foo;", options: [{ multiline: true }], parserOptions: { ecmaVersion: 6 } }, - { code: "var [a, b] = foo;", options: [{ multiline: true }], parserOptions: { ecmaVersion: 6 } }, - { code: "var [ // any comment\na, b\n] = foo;", options: [{ multiline: true }], parserOptions: { ecmaVersion: 6 } }, - { code: "var [\n// any comment\na, b\n] = foo;", options: [{ multiline: true }], parserOptions: { ecmaVersion: 6 } }, - { code: "var [\na, b\n// any comment\n] = foo;", options: [{ multiline: true }], parserOptions: { ecmaVersion: 6 } }, - { code: "var [\na,\nb\n] = foo;", options: [{ multiline: true }], parserOptions: { ecmaVersion: 6 } } - + { + code: "var [] = foo;", + options: [{ multiline: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [a] = foo;", + options: [{ multiline: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var /* any comment */[a] = foo;", + options: [{ multiline: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var /* any comment */\n[a] = foo;", + options: [{ multiline: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [a, b] = foo;", + options: [{ multiline: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [ // any comment\na, b\n] = foo;", + options: [{ multiline: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [\n// any comment\na, b\n] = foo;", + options: [{ multiline: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [\na, b\n// any comment\n] = foo;", + options: [{ multiline: true }], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "var [\na,\nb\n] = foo;", + options: [{ multiline: true }], + parserOptions: { ecmaVersion: 6 } + } ], invalid: [ + // default : { mutliline : true} + { + code: `var foo = [ + [1,2] + ]`, + output: "var foo = [[1,2]]", + errors: [ + { + messageId: "unexpectedOpeningLinebreak", + type: "ArrayExpression", + line: 1, + column: 11, + endLine: 1, + endColumn: 12 + }, + { + messageId: "unexpectedClosingLinebreak", + type: "ArrayExpression", + line: 3, + column: 13, + endLine: 3, + endColumn: 14 + } + ] + }, + { + code: "var foo = [[2,\n3]]", + output: "var foo = [\n[\n2,\n3\n]\n]", + errors: [ + { + line: 1, + column: 11, + messageId: "missingOpeningLinebreak", + endLine: 1, + endColumn: 12 + }, + { + line: 1, + column: 12, + messageId: "missingOpeningLinebreak", + endLine: 1, + endColumn: 13 + }, + { + line: 2, + column: 2, + messageId: "missingClosingLinebreak", + endLine: 2, + endColumn: 3 + }, + { + line: 2, + column: 3, + messageId: "missingClosingLinebreak", + endLine: 2, + endColumn: 4 + } + ] + }, + /* * ArrayExpression * "always" */ + { + code: "var foo = [[1,2]]", + output: "var foo = [\n[\n1,2\n]\n]", + options: ["always"], + errors: [ + { + line: 1, + column: 11, + messageId: "missingOpeningLinebreak", + endLine: 1, + endColumn: 12 + }, + { + line: 1, + column: 12, + messageId: "missingOpeningLinebreak", + endLine: 1, + endColumn: 13 + }, + { + line: 1, + column: 16, + messageId: "missingClosingLinebreak", + endLine: 1, + endColumn: 17 + }, + { + line: 1, + column: 17, + messageId: "missingClosingLinebreak", + endLine: 1, + endColumn: 18 + } + ] + }, { code: "var foo = [];", output: "var foo = [\n];", @@ -349,6 +704,21 @@ ruleTester.run("array-bracket-newline", rule, { }, // "never" + { + code: `var foo = [[ + 1,2],3];`, + output: "var foo = [[1,2],3];", + options: ["never"], + errors: [ + { + line: 1, + column: 12, + messageId: "unexpectedOpeningLinebreak", + endLine: 1, + endColumn: 13 + } + ] + }, { code: "var foo = [\n];", output: "var foo = [];", @@ -507,6 +877,35 @@ ruleTester.run("array-bracket-newline", rule, { }, // "consistent" + { + code: `var a = [[1,2] + ]`, + output: "var a = [[1,2]]", + options: ["consistent"], + errors: [ + { + line: 2, + column: 13, + messageId: "unexpectedClosingLinebreak", + endLine: 2, + endColumn: 14 + } + ] + }, + { + code: "var a = [\n[\n[1,2]]\n]", + output: "var a = [\n[\n[1,2]\n]\n]", + options: ["consistent"], + errors: [ + { + line: 3, + column: 6, + messageId: "missingClosingLinebreak", + endLine: 3, + endColumn: 7 + } + ] + }, { code: "var foo = [\n1]", output: "var foo = [\n1\n]", @@ -670,6 +1069,34 @@ ruleTester.run("array-bracket-newline", rule, { }, // { minItems: 2 } + { + code: "var foo = [1,[\n2,3\n]\n];", + output: "var foo = [\n1,[\n2,3\n]\n];", + options: [{ minItems: 2 }], + errors: [ + { + line: 1, + column: 11, + messageId: "missingOpeningLinebreak", + endLine: 1, + endColumn: 12 + } + ] + }, + { + code: "var foo = [[1,2\n]]", + output: "var foo = [[\n1,2\n]]", + options: [{ minItems: 2 }], + errors: [ + { + line: 1, + column: 12, + messageId: "missingOpeningLinebreak", + endLine: 1, + endColumn: 13 + } + ] + }, { code: "var foo = [\n];", output: "var foo = [];", @@ -841,7 +1268,6 @@ ruleTester.run("array-bracket-newline", rule, { line: 2, column: 2 } - ] }, { @@ -1580,6 +2006,5 @@ ruleTester.run("array-bracket-newline", rule, { } ] } - ] }); diff --git a/eslint/tests/lib/rules/array-callback-return.js b/eslint/tests/lib/rules/array-callback-return.js index 71731de..24b40cb 100644 --- a/eslint/tests/lib/rules/array-callback-return.js +++ b/eslint/tests/lib/rules/array-callback-return.js @@ -147,7 +147,7 @@ ruleTester.run("array-callback-return", rule, { { code: "foo[`every`](function foo() {})", parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: "expectedInside", data: { name: "function 'foo'" } }] }, { code: "foo.every(() => {})", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Expected to return a value in arrow function.", column: 14 }] }, { code: "foo.every(function() { if (a) return true; })", errors: [{ message: "Expected to return a value at the end of function.", column: 11 }] }, - { code: "foo.every(function cb() { if (a) return true; })", errors: [{ message: "Expected to return a value at the end of function 'cb'.", column: 20 }] }, + { code: "foo.every(function cb() { if (a) return true; })", errors: [{ message: "Expected to return a value at the end of function 'cb'.", column: 11 }] }, { code: "foo.every(function() { switch (a) { case 0: break; default: return true; } })", errors: [{ messageId: "expectedAtEnd", data: { name: "function" } }] }, { code: "foo.every(function foo() { switch (a) { case 0: break; default: return true; } })", errors: [{ messageId: "expectedAtEnd", data: { name: "function 'foo'" } }] }, { code: "foo.every(function() { try { bar(); } catch (err) { return true; } })", errors: [{ messageId: "expectedAtEnd", data: { name: "function" } }] }, @@ -163,7 +163,7 @@ ruleTester.run("array-callback-return", rule, { { code: "foo.every(a ? function() {} : function() {})", errors: ["Expected to return a value in function.", "Expected to return a value in function."] }, { code: "foo.every(a ? function foo() {} : function bar() {})", errors: ["Expected to return a value in function 'foo'.", "Expected to return a value in function 'bar'."] }, { code: "foo.every(function(){ return function() {}; }())", errors: [{ message: "Expected to return a value in function.", column: 30 }] }, - { code: "foo.every(function(){ return function foo() {}; }())", errors: [{ message: "Expected to return a value in function 'foo'.", column: 39 }] }, + { code: "foo.every(function(){ return function foo() {}; }())", errors: [{ message: "Expected to return a value in function 'foo'.", column: 30 }] }, { code: "foo.every(() => {})", options: [{ allowImplicit: false }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Expected to return a value in arrow function." }] }, { code: "foo.every(() => {})", options: [{ allowImplicit: true }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Expected to return a value in arrow function." }] }, @@ -198,7 +198,250 @@ ruleTester.run("array-callback-return", rule, { { code: "foo.every(function() {})", options: checkForEachOptions, errors: [{ messageId: "expectedInside", data: { name: "function" } }] }, { code: "foo.filter(function foo() {})", options: checkForEachOptions, errors: [{ messageId: "expectedInside", data: { name: "function 'foo'" } }] }, { code: "foo.filter(function foo() { return; })", options: checkForEachOptions, errors: [{ messageId: "expectedReturnValue", data: { name: "Function 'foo'" } }] }, - { code: "foo.every(cb || function() {})", options: checkForEachOptions, errors: ["Expected to return a value in function."] } + { code: "foo.every(cb || function() {})", options: checkForEachOptions, errors: ["Expected to return a value in function."] }, + // full location tests + { + code: "foo.filter(bar => { baz(); } )", + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "expectedInside", + data: { name: "arrow function" }, + type: "ArrowFunctionExpression", + line: 1, + column: 16, + endLine: 1, + endColumn: 18 + }] + }, + { + code: "foo.filter(\n() => {} )", + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "expectedInside", + data: { name: "arrow function" }, + type: "ArrowFunctionExpression", + line: 2, + column: 4, + endLine: 2, + endColumn: 6 + }] + }, + { + code: "foo.filter(bar || ((baz) => {}) )", + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "expectedInside", + data: { name: "arrow function" }, + type: "ArrowFunctionExpression", + line: 1, + column: 26, + endLine: 1, + endColumn: 28 + }] + }, + { + code: "foo.filter(bar => { return; })", + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "expectedReturnValue", + data: { name: "Arrow function" }, + type: "ReturnStatement", + line: 1, + column: 21, + endLine: 1, + endColumn: 28 + }] + }, + { + code: "Array.from(foo, bar => { bar })", + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "expectedInside", + data: { name: "arrow function" }, + type: "ArrowFunctionExpression", + line: 1, + column: 21, + endLine: 1, + endColumn: 23 + }] + }, + { + code: "foo.forEach(bar => bar)", + options: checkForEachOptions, + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "expectedNoReturnValue", + data: { name: "Arrow function" }, + type: "ArrowFunctionExpression", + line: 1, + column: 17, + endLine: 1, + endColumn: 19 + }] + }, + { + code: "foo.forEach((function () { return (bar) => bar; })())", + options: checkForEachOptions, + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "expectedNoReturnValue", + data: { name: "Arrow function" }, + type: "ArrowFunctionExpression", + line: 1, + column: 41, + endLine: 1, + endColumn: 43 + }] + }, + { + code: "foo.forEach((() => {\n return bar => bar; })())", + options: checkForEachOptions, + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "expectedNoReturnValue", + data: { name: "Arrow function" }, + type: "ArrowFunctionExpression", + line: 2, + column: 13, + endLine: 2, + endColumn: 15 + }] + }, + { + code: "foo.forEach((bar) => { if (bar) { return; } else { return bar ; } })", + options: checkForEachOptions, + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "expectedNoReturnValue", + data: { name: "Arrow function" }, + type: "ReturnStatement", + line: 1, + column: 52, + endLine: 1, + endColumn: 64 + }] + }, + { + code: "foo.filter(function(){})", + errors: [{ + messageId: "expectedInside", + data: { name: "function" }, + type: "FunctionExpression", + line: 1, + column: 12, + endLine: 1, + endColumn: 20 + }] + }, + { + code: "foo.filter(function (){})", + errors: [{ + messageId: "expectedInside", + data: { name: "function" }, + type: "FunctionExpression", + line: 1, + column: 12, + endLine: 1, + endColumn: 21 + }] + }, + { + code: "foo.filter(function\n(){})", + errors: [{ + messageId: "expectedInside", + data: { name: "function" }, + type: "FunctionExpression", + line: 1, + column: 12, + endLine: 2, + endColumn: 1 + }] + }, + { + code: "foo.filter(function bar(){})", + errors: [{ + messageId: "expectedInside", + data: { name: "function 'bar'" }, + type: "FunctionExpression", + line: 1, + column: 12, + endLine: 1, + endColumn: 24 + }] + }, + { + code: "foo.filter(function bar (){})", + errors: [{ + messageId: "expectedInside", + data: { name: "function 'bar'" }, + type: "FunctionExpression", + line: 1, + column: 12, + endLine: 1, + endColumn: 26 + }] + }, + { + code: "foo.filter(function\n bar() {})", + errors: [{ + messageId: "expectedInside", + data: { name: "function 'bar'" }, + type: "FunctionExpression", + line: 1, + column: 12, + endLine: 2, + endColumn: 5 + }] + }, + { + code: "Array.from(foo, function bar(){})", + errors: [{ + messageId: "expectedInside", + data: { name: "function 'bar'" }, + type: "FunctionExpression", + line: 1, + column: 17, + endLine: 1, + endColumn: 29 + }] + }, + { + code: "Array.from(foo, bar ? function (){} : baz)", + errors: [{ + messageId: "expectedInside", + data: { name: "function" }, + type: "FunctionExpression", + line: 1, + column: 23, + endLine: 1, + endColumn: 32 + }] + }, + { + code: "foo.filter(function bar() { return \n })", + errors: [{ + messageId: "expectedReturnValue", + data: { name: "Function 'bar'" }, + type: "ReturnStatement", + line: 1, + column: 29, + endLine: 1, + endColumn: 35 + }] + }, + { + code: "foo.forEach(function () { \nif (baz) return bar\nelse return\n })", + options: checkForEachOptions, + errors: [{ + messageId: "expectedNoReturnValue", + data: { name: "Function" }, + type: "ReturnStatement", + line: 2, + column: 10, + endLine: 2, + endColumn: 20 + }] + } ] }); diff --git a/eslint/tests/lib/rules/array-element-newline.js b/eslint/tests/lib/rules/array-element-newline.js index b17af83..195b5be 100644 --- a/eslint/tests/lib/rules/array-element-newline.js +++ b/eslint/tests/lib/rules/array-element-newline.js @@ -43,6 +43,8 @@ ruleTester.run("array-element-newline", rule, { "var foo = [1\n, 2\n, 3];", "var foo = [1,\n2,\n,\n3];", "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\nosomething();\n}\n];", + "var foo = [1,\n[2,\n3],\n4]", + "var foo = [[],\n[\n[]]]", { code: "var foo = [];", options: ["always"] }, { code: "var foo = [1];", options: ["always"] }, @@ -54,6 +56,7 @@ ruleTester.run("array-element-newline", rule, { { code: "var foo = [1,\n2 // any comment\n];", options: ["always"] }, { code: "var foo = [1,\n2,\n3];", options: ["always"] }, { code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];", options: ["always"] }, + { code: "var foo = [\n[1,\n2],\n3,\n[\n4]]", options: ["always"] }, // "never" { code: "var foo = [];", options: ["never"] }, @@ -65,6 +68,8 @@ ruleTester.run("array-element-newline", rule, { { code: "var foo = [1, 2, 3];", options: ["never"] }, { code: "var foo = [1, (\n2\n), 3];", options: ["never"] }, { code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];", options: ["never"] }, + { code: "var foo = [\n[1,2],3,[4]\n]", options: ["never"] }, + { code: "var foo = [[1,2\n],3,[4\n]\n]", options: ["never"] }, // "consistent" { code: "var foo = [];", options: ["consistent"] }, @@ -82,6 +87,9 @@ ruleTester.run("array-element-newline", rule, { { code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];", options: ["consistent"] }, { code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}];", options: ["consistent"] }, { code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}, function bar() {\ndosomething();\n}];", options: ["consistent"] }, + { code: "var foo = [1,\n[\n2,3,\n]\n];", options: ["consistent"] }, + { code: "var foo = [\n1,\n[2\n,3\n,]\n];", options: ["consistent"] }, + { code: "var foo = [\n1,[2,\n3]];", options: ["consistent"] }, // { multiline: true } { code: "var foo = [];", options: [{ multiline: true }] }, @@ -89,6 +97,7 @@ ruleTester.run("array-element-newline", rule, { { code: "var foo = [1, 2];", options: [{ multiline: true }] }, { code: "var foo = [1, 2, 3];", options: [{ multiline: true }] }, { code: "var f = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];", options: [{ multiline: true }] }, + { code: "var foo = [\n1,\n2,\n3,\n[\n]\n];", options: [{ multiline: true }] }, // { minItems: null } { code: "var foo = [];", options: [{ minItems: null }] }, @@ -96,6 +105,7 @@ ruleTester.run("array-element-newline", rule, { { code: "var foo = [1, 2];", options: [{ minItems: null }] }, { code: "var foo = [1, 2, 3];", options: [{ minItems: null }] }, { code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];", options: [{ minItems: null }] }, + { code: "var foo = [1, 2, 3, [[],1,[[]]]];", options: [{ minItems: null }] }, // { minItems: 0 } { code: "var foo = [];", options: [{ minItems: 0 }] }, @@ -103,6 +113,7 @@ ruleTester.run("array-element-newline", rule, { { code: "var foo = [1,\n2];", options: [{ minItems: 0 }] }, { code: "var foo = [1,\n2,\n3];", options: [{ minItems: 0 }] }, { code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];", options: [{ minItems: 0 }] }, + { code: "var foo = [\n1, \n2, \n3,\n[\n[],\n[]],\n[]];", options: [{ minItems: 0 }] }, // { minItems: 3 } { code: "var foo = [];", options: [{ minItems: 3 }] }, @@ -110,6 +121,7 @@ ruleTester.run("array-element-newline", rule, { { code: "var foo = [1, 2];", options: [{ minItems: 3 }] }, { code: "var foo = [1,\n2,\n3];", options: [{ minItems: 3 }] }, { code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];", options: [{ minItems: 3 }] }, + { code: "var foo = [[1,2],[[\n1,\n2,\n3]]];", options: [{ minItems: 3 }] }, // { multiline: true, minItems: 3 } { code: "var foo = [];", options: [{ multiline: true, minItems: 3 }] }, @@ -130,6 +142,10 @@ ruleTester.run("array-element-newline", rule, { { code: "var [// any comment \na,\nb] = foo;", parserOptions: { ecmaVersion: 6 } }, { code: "var [a,\nb // any comment\n] = foo;", parserOptions: { ecmaVersion: 6 } }, { code: "var [a,\nb,\nb] = foo;", parserOptions: { ecmaVersion: 6 } }, + { code: "var [\na,\n[\nb,\nc]] = foo;", parserOptions: { ecmaVersion: 6 } }, + + // "never" + { code: "var [a,[b,c]] = foo;", options: ["never"], parserOptions: { ecmaVersion: 6 } }, // { minItems: 3 } { code: "var [] = foo;", options: [{ minItems: 3 }], parserOptions: { ecmaVersion: 6 } }, @@ -144,6 +160,19 @@ ruleTester.run("array-element-newline", rule, { { code: "var [a, b] = [1,\n2]", options: [{ ArrayExpression: "always", ArrayPattern: "never" }], parserOptions: { ecmaVersion: 6 } }], invalid: [ + { + code: "var foo = [\n1,[2,\n3]]", + output: "var foo = [\n1,\n[2,\n3]]", + errors: [ + { + line: 2, + column: 3, + messageId: "missingLineBreak", + endLine: 2, + endColumn: 3 + } + ] + }, /* * ArrayExpression @@ -363,6 +392,18 @@ ruleTester.run("array-element-newline", rule, { } ] }, + { + code: "var foo = [\n[1,\n2],\n3,[\n4]]", + output: "var foo = [\n[1,\n2],\n3,\n[\n4]]", + options: ["always"], + errors: [ + { + line: 4, + column: 3, + messageId: "missingLineBreak" + } + ] + }, // "never" { @@ -471,6 +512,19 @@ ruleTester.run("array-element-newline", rule, { ] }, + { + code: "var foo = [[1,\n2\n],3,[4\n]\n]", + output: "var foo = [[1, 2\n],3,[4\n]\n]", + options: ["never"], + errors: [ + { + line: 1, + column: 15, + messageId: "unexpectedLineBreak" + } + ] + }, + // "consistent" { code: "var foo = [1,\n2, 3];", @@ -552,6 +606,23 @@ ruleTester.run("array-element-newline", rule, { } ] }, + { + code: "var foo = [\n1,[2,3,\n[]],\n[]\n];", + output: "var foo = [\n1,\n[2,\n3,\n[]],\n[]\n];", + options: ["consistent"], + errors: [ + { + line: 2, + column: 3, + messageId: "missingLineBreak" + }, + { + line: 2, + column: 6, + messageId: "missingLineBreak" + } + ] + }, // { multiline: true } { @@ -590,6 +661,23 @@ ruleTester.run("array-element-newline", rule, { } ] }, + { + code: "var foo = [\n1,2,3,\n[\n]\n];", + output: "var foo = [\n1,\n2,\n3,\n[\n]\n];", + options: [{ multiline: true }], + errors: [ + { + line: 2, + column: 3, + messageId: "missingLineBreak" + }, + { + line: 2, + column: 5, + messageId: "missingLineBreak" + } + ] + }, // { minItems: null } { diff --git a/eslint/tests/lib/rules/comma-style.js b/eslint/tests/lib/rules/comma-style.js index 5aa401f..1eb3082 100644 --- a/eslint/tests/lib/rules/comma-style.js +++ b/eslint/tests/lib/rules/comma-style.js @@ -292,7 +292,9 @@ ruleTester.run("comma-style", rule, { output: "var foo = 1,\nbar = 2;", errors: [{ messageId: "expectedCommaLast", - type: "VariableDeclarator" + type: "VariableDeclarator", + column: 1, + endColumn: 2 }] }, { @@ -473,7 +475,9 @@ ruleTester.run("comma-style", rule, { options: ["first"], errors: [{ messageId: "expectedCommaFirst", - type: "VariableDeclarator" + type: "VariableDeclarator", + column: 12, + endColumn: 13 }] }, { @@ -590,7 +594,9 @@ ruleTester.run("comma-style", rule, { output: "var foo = [\n(bar\n),\nbaz\n];", errors: [{ messageId: "unexpectedLineBeforeAndAfterComma", - type: "Identifier" + type: "Identifier", + column: 1, + endColumn: 2 }] }, { diff --git a/eslint/tests/lib/rules/func-call-spacing.js b/eslint/tests/lib/rules/func-call-spacing.js index d276ae9..0774ec4 100644 --- a/eslint/tests/lib/rules/func-call-spacing.js +++ b/eslint/tests/lib/rules/func-call-spacing.js @@ -226,99 +226,99 @@ ruleTester.run("func-call-spacing", rule, { { code: "f ();", output: "f();", - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "f (a, b);", output: "f(a, b);", - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "f.b ();", output: "f.b();", - errors: [{ messageId: "unexpected", type: "CallExpression", column: 3 }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression", column: 3 }] }, { code: "f.b().c ();", output: "f.b().c();", - errors: [{ messageId: "unexpected", type: "CallExpression", column: 7 }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression", column: 7 }] }, { code: "f() ()", output: "f()()", - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "(function() {} ())", output: "(function() {}())", - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "var f = new Foo ()", output: "var f = new Foo()", - errors: [{ messageId: "unexpected", type: "NewExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "NewExpression" }] }, { code: "f ( (0) )", output: "f( (0) )", - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "f(0) (1)", output: "f(0)(1)", - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "(f) (0)", output: "(f)(0)", - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "f ();\n t ();", output: "f();\n t();", errors: [ - { messageId: "unexpected", type: "CallExpression" }, - { messageId: "unexpected", type: "CallExpression" } + { messageId: "unexpectedWhitespace", type: "CallExpression" }, + { messageId: "unexpectedWhitespace", type: "CallExpression" } ] }, { code: "import (source);", output: "import(source);", parserOptions: { ecmaVersion: 2020 }, - errors: [{ messageId: "unexpected", type: "ImportExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "ImportExpression" }] }, // https://github.com/eslint/eslint/issues/7787 { code: "f\n();", output: null, // no change - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "f\r();", output: null, // no change - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "f\u2028();", output: null, // no change - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "f\u2029();", output: null, // no change - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "f\r\n();", output: null, // no change - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "import\n(source);", output: null, parserOptions: { ecmaVersion: 2020 }, - errors: [{ messageId: "unexpected", type: "ImportExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "ImportExpression" }] }, // "never" @@ -326,69 +326,69 @@ ruleTester.run("func-call-spacing", rule, { code: "f ();", output: "f();", options: ["never"], - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "f (a, b);", output: "f(a, b);", options: ["never"], - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "f.b ();", output: "f.b();", options: ["never"], - errors: [{ messageId: "unexpected", type: "CallExpression", column: 3 }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression", column: 3 }] }, { code: "f.b().c ();", output: "f.b().c();", options: ["never"], - errors: [{ messageId: "unexpected", type: "CallExpression", column: 7 }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression", column: 7 }] }, { code: "f() ()", output: "f()()", options: ["never"], - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "(function() {} ())", output: "(function() {}())", options: ["never"], - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "var f = new Foo ()", output: "var f = new Foo()", options: ["never"], - errors: [{ messageId: "unexpected", type: "NewExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "NewExpression" }] }, { code: "f ( (0) )", output: "f( (0) )", options: ["never"], - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "f(0) (1)", output: "f(0)(1)", options: ["never"], - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "(f) (0)", output: "(f)(0)", options: ["never"], - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "CallExpression" }] }, { code: "f ();\n t ();", output: "f();\n t();", options: ["never"], errors: [ - { messageId: "unexpected", type: "CallExpression" }, - { messageId: "unexpected", type: "CallExpression" } + { messageId: "unexpectedWhitespace", type: "CallExpression" }, + { messageId: "unexpectedWhitespace", type: "CallExpression" } ] }, { @@ -396,7 +396,7 @@ ruleTester.run("func-call-spacing", rule, { output: "import(source);", options: ["never"], parserOptions: { ecmaVersion: 2020 }, - errors: [{ messageId: "unexpected", type: "ImportExpression" }] + errors: [{ messageId: "unexpectedWhitespace", type: "ImportExpression" }] }, // https://github.com/eslint/eslint/issues/7787 @@ -406,7 +406,7 @@ ruleTester.run("func-call-spacing", rule, { options: ["never"], errors: [ { - messageId: "unexpected", + messageId: "unexpectedWhitespace", type: "CallExpression" } ] @@ -421,7 +421,7 @@ ruleTester.run("func-call-spacing", rule, { options: ["never"], errors: [ { - messageId: "unexpected", + messageId: "unexpectedWhitespace", type: "CallExpression", line: 2, column: 23 @@ -437,7 +437,7 @@ ruleTester.run("func-call-spacing", rule, { options: ["never"], errors: [ { - messageId: "unexpected", + messageId: "unexpectedWhitespace", type: "CallExpression", line: 1, column: 9 @@ -453,7 +453,7 @@ ruleTester.run("func-call-spacing", rule, { options: ["never"], errors: [ { - messageId: "unexpected", + messageId: "unexpectedWhitespace", type: "CallExpression", line: 1, column: 9 @@ -466,7 +466,7 @@ ruleTester.run("func-call-spacing", rule, { options: ["never"], errors: [ { - messageId: "unexpected", + messageId: "unexpectedWhitespace", type: "CallExpression" } ] @@ -477,7 +477,7 @@ ruleTester.run("func-call-spacing", rule, { options: ["never"], errors: [ { - messageId: "unexpected", + messageId: "unexpectedWhitespace", type: "CallExpression" } ] @@ -488,7 +488,7 @@ ruleTester.run("func-call-spacing", rule, { options: ["never"], errors: [ { - messageId: "unexpected", + messageId: "unexpectedWhitespace", type: "CallExpression" } ] @@ -499,7 +499,7 @@ ruleTester.run("func-call-spacing", rule, { options: ["never"], errors: [ { - messageId: "unexpected", + messageId: "unexpectedWhitespace", type: "CallExpression" } ] @@ -516,7 +516,7 @@ ruleTester.run("func-call-spacing", rule, { code: "f\n();", output: "f ();", options: ["always"], - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedNewline", type: "CallExpression" }] }, { code: "f(a, b);", @@ -528,7 +528,7 @@ ruleTester.run("func-call-spacing", rule, { code: "f\n(a, b);", output: "f (a, b);", options: ["always"], - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedNewline", type: "CallExpression" }] }, { code: "f.b();", @@ -540,7 +540,7 @@ ruleTester.run("func-call-spacing", rule, { code: "f.b\n();", output: "f.b ();", options: ["always"], - errors: [{ messageId: "unexpected", type: "CallExpression", column: 3 }] + errors: [{ messageId: "unexpectedNewline", type: "CallExpression", column: 3 }] }, { code: "f.b().c ();", @@ -552,7 +552,7 @@ ruleTester.run("func-call-spacing", rule, { code: "f.b\n().c ();", output: "f.b ().c ();", options: ["always"], - errors: [{ messageId: "unexpected", type: "CallExpression", column: 3 }] + errors: [{ messageId: "unexpectedNewline", type: "CallExpression", column: 3 }] }, { code: "f() ()", @@ -564,14 +564,14 @@ ruleTester.run("func-call-spacing", rule, { code: "f\n() ()", output: "f () ()", options: ["always"], - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedNewline", type: "CallExpression" }] }, { code: "f\n()()", output: "f () ()", options: ["always"], errors: [ - { messageId: "unexpected", type: "CallExpression" }, + { messageId: "unexpectedNewline", type: "CallExpression" }, { messageId: "missing", type: "CallExpression" } ] }, @@ -625,25 +625,25 @@ ruleTester.run("func-call-spacing", rule, { code: "f\r();", output: "f ();", options: ["always"], - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedNewline", type: "CallExpression" }] }, { code: "f\u2028();", output: "f ();", options: ["always"], - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedNewline", type: "CallExpression" }] }, { code: "f\u2029();", output: "f ();", options: ["always"], - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedNewline", type: "CallExpression" }] }, { code: "f\r\n();", output: "f ();", options: ["always"], - errors: [{ messageId: "unexpected", type: "CallExpression" }] + errors: [{ messageId: "unexpectedNewline", type: "CallExpression" }] }, // "always", "allowNewlines": true diff --git a/eslint/tests/lib/rules/getter-return.js b/eslint/tests/lib/rules/getter-return.js index 082e584..ea18165 100644 --- a/eslint/tests/lib/rules/getter-return.js +++ b/eslint/tests/lib/rules/getter-return.js @@ -81,9 +81,56 @@ ruleTester.run("getter-return", rule, { * test obj: get * option: {allowImplicit: false} */ - { code: "var foo = { get bar() {} };", errors: [expectedError] }, - { code: "var foo = { get bar(){if(baz) {return true;}} };", errors: [expectedAlwaysError] }, - { code: "var foo = { get bar() { ~function () {return true;}} };", errors: [expectedError] }, + { + code: "var foo = { get bar() {} };", + errors: [{ + ...expectedError, + line: 1, + column: 13, + endLine: 1, + endColumn: 20 + }] + }, + { + code: "var foo = { get\n bar () {} };", + errors: [{ + ...expectedError, + line: 1, + column: 13, + endLine: 2, + endColumn: 6 + }] + }, + { + code: "var foo = { get bar(){if(baz) {return true;}} };", + errors: [{ + ...expectedAlwaysError, + line: 1, + column: 13, + endLine: 1, + endColumn: 20 + }] + }, + { + code: "var foo = { get bar() { ~function () {return true;}} };", + errors: [{ + ...expectedError, + line: 1, + column: 13, + endLine: 1, + endColumn: 20 + }] + }, + { + code: "var foo = { get bar() { return; } };", + errors: [{ + ...expectedError, + line: 1, + column: 25, + endLine: 1, + endColumn: 32 + }] + }, // option: {allowImplicit: true} { code: "var foo = { get bar() {} };", options, errors: [expectedError] }, @@ -93,7 +140,27 @@ ruleTester.run("getter-return", rule, { * test class: get * option: {allowImplicit: false} */ - { code: "class foo { get bar(){} }", errors: [expectedError] }, + { + code: "class foo { get bar(){} }", + errors: [{ + ...expectedError, + line: 1, + column: 13, + endLine: 1, + endColumn: 20 + }] + }, + { + code: "var foo = class {\n static get\nbar(){} }", + errors: [{ + messageId: "expected", + data: { name: "static getter 'bar'" }, + line: 2, + column: 3, + endLine: 3, + endColumn: 4 + }] + }, { code: "class foo { get bar(){ if (baz) { return true; }}}", errors: [expectedAlwaysError] }, { code: "class foo { get bar(){ ~function () { return true; }()}}", errors: [expectedError] }, @@ -105,8 +172,50 @@ ruleTester.run("getter-return", rule, { * test object.defineProperty(s) * option: {allowImplicit: false} */ - { code: "Object.defineProperty(foo, \"bar\", { get: function (){}});", errors: [{ messageId: "expected", data: { name: "method 'get'" } }] }, - { code: "Object.defineProperty(foo, \"bar\", { get: () => {}});", errors: [{ messageId: "expected", data: { name: "arrow function 'get'" } }] }, + { + code: "Object.defineProperty(foo, 'bar', { get: function (){}});", + errors: [{ + messageId: "expected", + data: { name: "method 'get'" }, + line: 1, + column: 37, + endLine: 1, + endColumn: 51 + }] + }, + { + code: "Object.defineProperty(foo, 'bar', { get: function getfoo (){}});", + errors: [{ + messageId: "expected", + data: { name: "method 'getfoo'" }, + line: 1, + column: 37, + endLine: 1, + endColumn: 58 + }] + }, + { + code: "Object.defineProperty(foo, 'bar', { get(){} });", + errors: [{ + messageId: "expected", + data: { name: "method 'get'" }, + line: 1, + column: 37, + endLine: 1, + endColumn: 40 + }] + }, + { + code: "Object.defineProperty(foo, 'bar', { get: () => {}});", + errors: [{ + messageId: "expected", + data: { name: "arrow function 'get'" }, + line: 1, + column: 45, + endLine: 1, + endColumn: 47 + }] + }, { code: "Object.defineProperty(foo, \"bar\", { get: function (){if(bar) {return true;}}});", errors: [{ messageId: "expectedAlways" }] }, { code: "Object.defineProperty(foo, \"bar\", { get: function (){ ~function () { return true; }()}});", errors: [{ messageId: "expected" }] }, { code: "Object.defineProperties(foo, { bar: { get: function () {}} });", options, errors: [{ messageId: "expected" }] }, diff --git a/eslint/tests/lib/rules/id-blacklist.js b/eslint/tests/lib/rules/id-blacklist.js index 3d40bee..a900924 100644 --- a/eslint/tests/lib/rules/id-blacklist.js +++ b/eslint/tests/lib/rules/id-blacklist.js @@ -367,7 +367,7 @@ ruleTester.run("id-blacklist", rule, { column: 5 }, - // reports each occurence of local identifier, although it's renamed in this export specifier + // reports each occurrence of local identifier, although it's renamed in this export specifier { messageId: "blacklisted", data: { name: "foo" }, diff --git a/eslint/tests/lib/rules/implicit-arrow-linebreak.js b/eslint/tests/lib/rules/implicit-arrow-linebreak.js index 089424e..c65d475 100644 --- a/eslint/tests/lib/rules/implicit-arrow-linebreak.js +++ b/eslint/tests/lib/rules/implicit-arrow-linebreak.js @@ -10,7 +10,7 @@ const rule = require("../../../lib/rules/implicit-arrow-linebreak"); const { RuleTester } = require("../../../lib/rule-tester"); -const { unIndent } = require("../_utils"); +const { unIndent } = require("../../_utils"); const EXPECTED_LINEBREAK = { messageId: "expected" }; const UNEXPECTED_LINEBREAK = { messageId: "unexpected" }; diff --git a/eslint/tests/lib/rules/indent.js b/eslint/tests/lib/rules/indent.js index 7496da2..56589be 100644 --- a/eslint/tests/lib/rules/indent.js +++ b/eslint/tests/lib/rules/indent.js @@ -21,7 +21,7 @@ const path = require("path"); const fixture = fs.readFileSync(path.join(__dirname, "../../fixtures/rules/indent/indent-invalid-fixture-1.js"), "utf8"); const fixedFixture = fs.readFileSync(path.join(__dirname, "../../fixtures/rules/indent/indent-valid-fixture-1.js"), "utf8"); const parser = require("../../fixtures/fixture-parser"); -const { unIndent } = require("../_utils"); +const { unIndent } = require("../../_utils"); /** @@ -5590,9 +5590,9 @@ ruleTester.run("indent", rule, { \${a} \${b} template literal - \`(() => { + \`(() => { foo(); - + tagTwo\`multiline template literal @@ -5609,12 +5609,12 @@ ruleTester.run("indent", rule, { tagOne\`multiline template literal - \${a} \${b}\`({ + \${a} \${b}\`({ foo: 1, bar: tagTwo\`multiline template literal\`(() => { - + baz(); }) }); @@ -5651,7 +5651,7 @@ ruleTester.run("indent", rule, { code: unIndent` foo .bar - .baz\` template + .baz\` template literal \`(() => { baz(); }) @@ -11207,9 +11207,9 @@ ruleTester.run("indent", rule, { tagOne\`multiline \${a} \${b} template literal - \`(() => { + \`(() => { foo(); - + tagTwo\`multiline template literal @@ -11223,9 +11223,9 @@ ruleTester.run("indent", rule, { tagOne\`multiline \${a} \${b} template literal - \`(() => { + \`(() => { foo(); - + tagTwo\`multiline template literal @@ -11250,7 +11250,7 @@ ruleTester.run("indent", rule, { bar: tagTwo\`multiline template literal\`(() => { - + baz(); }) }); @@ -11263,7 +11263,7 @@ ruleTester.run("indent", rule, { bar: tagTwo\`multiline template literal\`(() => { - + baz(); }) }); diff --git a/eslint/tests/lib/rules/keyword-spacing.js b/eslint/tests/lib/rules/keyword-spacing.js index 9971fac..f7f5cca 100644 --- a/eslint/tests/lib/rules/keyword-spacing.js +++ b/eslint/tests/lib/rules/keyword-spacing.js @@ -34,7 +34,7 @@ const NEITHER = { before: false, after: false }; * after: false, * overrides: {as: {before: true, after: true}} * } - * @param {string} keyword A keyword to be overriden. + * @param {string} keyword A keyword to be overridden. * @param {Object} value A value to override. * @returns {Object} An option object to test an "overrides" option. */ diff --git a/eslint/tests/lib/rules/new-cap.js b/eslint/tests/lib/rules/new-cap.js index 706bca1..ec85c4b 100644 --- a/eslint/tests/lib/rules/new-cap.js +++ b/eslint/tests/lib/rules/new-cap.js @@ -74,15 +74,105 @@ ruleTester.run("new-cap", rule, { { code: "var x = foo.Bar(42);", options: [{ capIsNew: false, properties: false }] } ], invalid: [ - { code: "var x = new c();", errors: [{ messageId: "lower", type: "NewExpression" }] }, - { code: "var x = new φ;", errors: [{ messageId: "lower", type: "NewExpression" }] }, - { code: "var x = new a.b.c;", errors: [{ messageId: "lower", type: "NewExpression" }] }, - { code: "var x = new a.b['c'];", errors: [{ messageId: "lower", type: "NewExpression" }] }, - { code: "var b = Foo();", errors: [{ messageId: "upper", type: "CallExpression" }] }, - { code: "var b = a.Foo();", errors: [{ messageId: "upper", type: "CallExpression" }] }, - { code: "var b = a['Foo']();", errors: [{ messageId: "upper", type: "CallExpression" }] }, - { code: "var b = a.Date.UTC();", errors: [{ messageId: "upper", type: "CallExpression" }] }, - { code: "var b = UTC();", errors: [{ messageId: "upper", type: "CallExpression" }] }, + { + code: "var x = new c();", + errors: [{ + messageId: "lower", + type: "NewExpression", + line: 1, + column: 13, + endLine: 1, + endColumn: 14 + }] + }, + { + code: "var x = new φ;", + errors: [{ + messageId: "lower", + type: "NewExpression", + line: 1, + column: 13, + endLine: 1, + endColumn: 14 + }] + }, + { + code: "var x = new a.b.c;", + errors: [{ + messageId: "lower", + type: "NewExpression", + line: 1, + column: 17, + endLine: 1, + endColumn: 18 + }] + }, + { + code: "var x = new a.b['c'];", + errors: [{ + messageId: "lower", + type: "NewExpression", + line: 1, + column: 17, + endLine: 1, + endColumn: 20 + }] + }, + { + code: "var b = Foo();", + errors: [{ + messageId: "upper", + type: "CallExpression", + line: 1, + column: 9, + endLine: 1, + endColumn: 12 + }] + }, + { + code: "var b = a.Foo();", + errors: [{ + messageId: "upper", + type: "CallExpression", + line: 1, + column: 11, + endLine: 1, + endColumn: 14 + }] + }, + { + code: "var b = a['Foo']();", + errors: [{ + messageId: "upper", + type: "CallExpression", + line: 1, + column: 11, + endLine: 1, + endColumn: 16 + }] + }, + { + code: "var b = a.Date.UTC();", + errors: [{ + messageId: "upper", + type: "CallExpression", + line: 1, + column: 16, + endLine: 1, + endColumn: 19 + }] + }, + { + code: "var b = UTC();", + errors: [{ + messageId: "upper", + type: "CallExpression", + line: 1, + column: 9, + endLine: 1, + endColumn: 12 + }] + }, { code: "var a = B.C();", errors: [ @@ -90,7 +180,9 @@ ruleTester.run("new-cap", rule, { messageId: "upper", type: "CallExpression", line: 1, - column: 11 + column: 11, + endLine: 1, + endColumn: 12 } ] }, @@ -101,7 +193,9 @@ ruleTester.run("new-cap", rule, { messageId: "upper", type: "CallExpression", line: 2, - column: 2 + column: 2, + endLine: 2, + endColumn: 3 } ] }, @@ -112,7 +206,9 @@ ruleTester.run("new-cap", rule, { messageId: "lower", type: "NewExpression", line: 1, - column: 15 + column: 15, + endLine: 1, + endColumn: 16 } ] }, @@ -123,7 +219,9 @@ ruleTester.run("new-cap", rule, { messageId: "lower", type: "NewExpression", line: 2, - column: 1 + column: 1, + endLine: 2, + endColumn: 2 } ] }, @@ -134,7 +232,51 @@ ruleTester.run("new-cap", rule, { messageId: "lower", type: "NewExpression", line: 1, - column: 13 + column: 13, + endLine: 1, + endColumn: 14 + } + ] + }, + { + code: "var a = new b[ ( 'foo' ) ]();", + parserOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "lower", + type: "NewExpression", + line: 1, + column: 18, + endLine: 1, + endColumn: 23 + } + ] + }, + { + code: "var a = new b[`foo`];", + parserOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "lower", + type: "NewExpression", + line: 1, + column: 15, + endLine: 1, + endColumn: 20 + } + ] + }, + { + code: "var a = b[`\\\nFoo`]();", + parserOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "upper", + type: "CallExpression", + line: 1, + column: 11, + endLine: 2, + endColumn: 5 } ] }, diff --git a/eslint/tests/lib/rules/newline-per-chained-call.js b/eslint/tests/lib/rules/newline-per-chained-call.js index a0eefd2..28d45c6 100644 --- a/eslint/tests/lib/rules/newline-per-chained-call.js +++ b/eslint/tests/lib/rules/newline-per-chained-call.js @@ -30,47 +30,92 @@ ruleTester.run("newline-per-chained-call", rule, { code: "_\n.chain({}).map(foo).filter(bar).value();", output: "_\n.chain({}).map(foo)\n.filter(bar)\n.value();", errors: [{ - messageId: "expected", data: { callee: ".filter" } + messageId: "expected", + data: { callee: ".filter" }, + line: 2, + column: 20, + endLine: 2, + endColumn: 27 }, { - messageId: "expected", data: { callee: ".value" } + messageId: "expected", + data: { callee: ".value" }, + line: 2, + column: 32, + endLine: 2, + endColumn: 38 }] }, { code: "_\n.chain({})\n.map(foo)\n.filter(bar).value();", output: "_\n.chain({})\n.map(foo)\n.filter(bar)\n.value();", errors: [{ - messageId: "expected", data: { callee: ".value" } + messageId: "expected", + data: { callee: ".value" }, + line: 4, + column: 13, + endLine: 4, + endColumn: 19 }] }, { code: "a().b().c().e.d()", output: "a().b()\n.c().e.d()", errors: [{ - messageId: "expected", data: { callee: ".c" } + messageId: "expected", + data: { callee: ".c" }, + line: 1, + column: 8, + endLine: 1, + endColumn: 10 }] }, { code: "a.b.c().e().d()", output: "a.b.c().e()\n.d()", errors: [{ - messageId: "expected", data: { callee: ".d" } + messageId: "expected", + data: { callee: ".d" }, + line: 1, + column: 12, + endLine: 1, + endColumn: 14 }] }, { code: "_.chain({}).map(a).value(); ", output: "_.chain({}).map(a)\n.value(); ", errors: [{ - messageId: "expected", data: { callee: ".value" } + messageId: "expected", + data: { callee: ".value" }, + line: 1, + column: 19, + endLine: 1, + endColumn: 25 }] }, { code: "var a = m1.m2();\n var b = m1.m2().m3().m4().m5();", output: "var a = m1.m2();\n var b = m1.m2().m3()\n.m4()\n.m5();", errors: [{ - messageId: "expected", data: { callee: ".m4" } + messageId: "expected", + data: { callee: ".m4" }, + line: 2, + column: 22, + endLine: 2, + endColumn: 25 }, { - messageId: "expected", data: { callee: ".m5" } + messageId: "expected", + data: { callee: ".m5" }, + line: 2, + column: 27, + endLine: 2, + endColumn: 30 }] }, { code: "var a = m1.m2();\n var b = m1.m2().m3()\n.m4().m5();", output: "var a = m1.m2();\n var b = m1.m2().m3()\n.m4()\n.m5();", errors: [{ - messageId: "expected", data: { callee: ".m5" } + messageId: "expected", + data: { callee: ".m5" }, + line: 3, + column: 6, + endLine: 3, + endColumn: 9 }] }, { code: "var a = m1().m2\n.m3().m4().m5().m6().m7();", @@ -79,9 +124,19 @@ ruleTester.run("newline-per-chained-call", rule, { ignoreChainWithDepth: 3 }], errors: [{ - messageId: "expected", data: { callee: ".m6" } + messageId: "expected", + data: { callee: ".m6" }, + line: 2, + column: 16, + endLine: 2, + endColumn: 19 }, { - messageId: "expected", data: { callee: ".m7" } + messageId: "expected", + data: { callee: ".m7" }, + line: 2, + column: 21, + endLine: 2, + endColumn: 24 }] }, { code: [ @@ -145,9 +200,19 @@ ruleTester.run("newline-per-chained-call", rule, { ".end();" ].join("\n"), errors: [{ - messageId: "expected", data: { callee: ".on" } + messageId: "expected", + data: { callee: ".on" }, + line: 16, + column: 3, + endLine: 16, + endColumn: 6 }, { - messageId: "expected", data: { callee: ".end" } + messageId: "expected", + data: { callee: ".end" }, + line: 27, + column: 3, + endLine: 27, + endColumn: 7 }] }, { code: [ @@ -163,34 +228,117 @@ ruleTester.run("newline-per-chained-call", rule, { " 'method4']()" ].join("\n"), errors: [{ - messageId: "expected", data: { callee: "['method' + n]" } + messageId: "expected", + data: { callee: "['method' + n]" }, + line: 1, + column: 29, + endLine: 1, + endColumn: 43 }, { - messageId: "expected", data: { callee: "[aCondition ?" } + messageId: "expected", + data: { callee: "[aCondition ?" }, + line: 1, + column: 45, + endLine: 3, + endColumn: 15 }] }, { code: "foo.bar()['foo' + \u2029 + 'bar']()", output: "foo.bar()\n['foo' + \u2029 + 'bar']()", options: [{ ignoreChainWithDepth: 1 }], - errors: [{ messageId: "expected", data: { callee: "['foo' + " } }] + errors: [{ + messageId: "expected", + data: { callee: "['foo' + " }, + line: 1, + column: 10, + endLine: 2, + endColumn: 10 + }] }, { code: "foo.bar()[(biz)]()", output: "foo.bar()\n[(biz)]()", options: [{ ignoreChainWithDepth: 1 }], - errors: [{ messageId: "expected", data: { callee: "[biz]" } }] + errors: [{ + messageId: "expected", + data: { callee: "[biz]" }, + line: 1, + column: 10, + endLine: 1, + endColumn: 17 + }] }, { code: "(foo).bar().biz()", output: "(foo).bar()\n.biz()", options: [{ ignoreChainWithDepth: 1 }], - errors: [{ messageId: "expected", data: { callee: ".biz" } }] + errors: [{ + messageId: "expected", + data: { callee: ".biz" }, + line: 1, + column: 12, + endLine: 1, + endColumn: 16 + }] }, { code: "foo.bar(). /* comment */ biz()", output: "foo.bar()\n. /* comment */ biz()", options: [{ ignoreChainWithDepth: 1 }], - errors: [{ messageId: "expected", data: { callee: ".biz" } }] + errors: [{ + messageId: "expected", + data: { callee: ".biz" }, + line: 1, + column: 10, + endLine: 1, + endColumn: 29 + }] }, { code: "foo.bar() /* comment */ .biz()", output: "foo.bar() /* comment */ \n.biz()", options: [{ ignoreChainWithDepth: 1 }], - errors: [{ messageId: "expected", data: { callee: ".biz" } }] + errors: [{ + messageId: "expected", + data: { callee: ".biz" }, + line: 1, + column: 25, + endLine: 1, + endColumn: 29 + }] + }, { + code: "((foo.bar()) . baz()).quux();", + output: "((foo.bar()) \n. baz())\n.quux();", + options: [{ ignoreChainWithDepth: 1 }], + errors: [{ + messageId: "expected", + data: { callee: ".baz" }, + line: 1, + column: 14, + endLine: 1, + endColumn: 19 + }, { + messageId: "expected", + data: { callee: ".quux" }, + line: 1, + column: 22, + endLine: 1, + endColumn: 27 + }] + }, { + code: "((foo.bar()) [a + b] ()) [(c + d)]()", + output: "((foo.bar()) \n[a + b] ()) \n[(c + d)]()", + options: [{ ignoreChainWithDepth: 1 }], + errors: [{ + messageId: "expected", + data: { callee: "[a + b]" }, + line: 1, + column: 14, + endLine: 1, + endColumn: 21 + }, { + messageId: "expected", + data: { callee: "[c + d]" }, + line: 1, + column: 26, + endLine: 1, + endColumn: 35 + }] }] }); diff --git a/eslint/tests/lib/rules/no-empty-function.js b/eslint/tests/lib/rules/no-empty-function.js index c43ec4a..f42c232 100644 --- a/eslint/tests/lib/rules/no-empty-function.js +++ b/eslint/tests/lib/rules/no-empty-function.js @@ -312,5 +312,66 @@ ruleTester.run("no-empty-function", rule, [ } ], - invalid: [] + invalid: [ + + // location tests + { + code: "function foo() {}", + errors: [{ + messageId: "unexpected", + data: { name: "function 'foo'" }, + line: 1, + column: 16, + endLine: 1, + endColumn: 18 + }] + }, + { + code: "var foo = function () {\n}", + errors: [{ + messageId: "unexpected", + data: { name: "function" }, + line: 1, + column: 23, + endLine: 2, + endColumn: 2 + }] + }, + { + code: "var foo = () => { \n\n }", + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "unexpected", + data: { name: "arrow function" }, + line: 1, + column: 17, + endLine: 3, + endColumn: 4 + }] + }, + { + code: "var obj = {\n\tfoo() {\n\t}\n}", + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "unexpected", + data: { name: "method 'foo'" }, + line: 2, + column: 8, + endLine: 3, + endColumn: 3 + }] + }, + { + code: "class A { foo() { } }", + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "unexpected", + data: { name: "method 'foo'" }, + line: 1, + column: 17, + endLine: 1, + endColumn: 20 + }] + } + ] })); diff --git a/eslint/tests/lib/rules/no-extra-parens.js b/eslint/tests/lib/rules/no-extra-parens.js index 13df7dc..5062857 100644 --- a/eslint/tests/lib/rules/no-extra-parens.js +++ b/eslint/tests/lib/rules/no-extra-parens.js @@ -530,13 +530,17 @@ ruleTester.run("no-extra-parens", rule, { "() => ({ foo: 1 }).foo", "() => ({ foo: 1 }.foo().bar).baz.qux()", "() => ({ foo: 1 }.foo().bar + baz)", + { + code: "export default (a, b)", + parserOptions: { sourceType: "module" } + }, { code: "export default (function(){}).foo", - parserOptions: { ecmaVersion: 6, sourceType: "module" } + parserOptions: { sourceType: "module" } }, { code: "export default (class{}).foo", - parserOptions: { ecmaVersion: 6, sourceType: "module" } + parserOptions: { sourceType: "module" } }, "({}).hasOwnProperty.call(foo, bar)", "({}) ? foo() : bar()", @@ -1358,6 +1362,55 @@ ruleTester.run("no-extra-parens", rule, { "UpdateExpression", 1 ), + invalid( + "export default ((a, b))", + "export default (a, b)", + "SequenceExpression", + 1, + { parserOptions: { sourceType: "module" } } + ), + invalid( + "export default (() => {})", + "export default () => {}", + "ArrowFunctionExpression", + 1, + { parserOptions: { sourceType: "module" } } + ), + invalid( + "export default ((a, b) => a + b)", + "export default (a, b) => a + b", + "ArrowFunctionExpression", + 1, + { parserOptions: { sourceType: "module" } } + ), + invalid( + "export default (a => a)", + "export default a => a", + "ArrowFunctionExpression", + 1, + { parserOptions: { sourceType: "module" } } + ), + invalid( + "export default (a = b)", + "export default a = b", + "AssignmentExpression", + 1, + { parserOptions: { sourceType: "module" } } + ), + invalid( + "export default (a ? b : c)", + "export default a ? b : c", + "ConditionalExpression", + 1, + { parserOptions: { sourceType: "module" } } + ), + invalid( + "export default (a)", + "export default a", + "Identifier", + 1, + { parserOptions: { sourceType: "module" } } + ), invalid( "for (foo of(bar));", "for (foo of bar);", diff --git a/eslint/tests/lib/rules/no-implied-eval.js b/eslint/tests/lib/rules/no-implied-eval.js index 0cd1d18..1c712f4 100644 --- a/eslint/tests/lib/rules/no-implied-eval.js +++ b/eslint/tests/lib/rules/no-implied-eval.js @@ -150,7 +150,7 @@ ruleTester.run("no-implied-eval", rule, { { code: "window.window.setTimeout(`foo${bar}`)", parserOptions: { ecmaVersion: 6 }, env: { browser: true }, errors: [expectedError] }, { code: "global.global.setTimeout(`foo${bar}`)", parserOptions: { ecmaVersion: 6 }, env: { node: true }, errors: [expectedError] }, - // string concatination + // string concatenation { code: "setTimeout('foo' + bar)", errors: [expectedError] }, { code: "setTimeout(foo + 'bar')", errors: [expectedError] }, { code: "setTimeout(`foo` + bar)", parserOptions: { ecmaVersion: 6 }, errors: [expectedError] }, diff --git a/eslint/tests/lib/rules/no-inner-declarations.js b/eslint/tests/lib/rules/no-inner-declarations.js index d255824..a9f70fd 100644 --- a/eslint/tests/lib/rules/no-inner-declarations.js +++ b/eslint/tests/lib/rules/no-inner-declarations.js @@ -45,74 +45,233 @@ ruleTester.run("no-inner-declarations", rule, { code: "var x = {doSomething() {var foo;}}", options: ["both"], parserOptions: { ecmaVersion: 6 } + }, + { + code: "export var foo;", + options: ["both"], + parserOptions: { sourceType: "module", ecmaVersion: 6 } + }, + { + code: "export function bar() {}", + options: ["both"], + parserOptions: { sourceType: "module", ecmaVersion: 6 } + }, + { + code: "export default function baz() {}", + options: ["both"], + parserOptions: { sourceType: "module", ecmaVersion: 6 } + }, + { + code: "exports.foo = () => {}", + options: ["both"], + parserOptions: { ecmaVersion: 6 } + }, + { + code: "exports.foo = function(){}", + options: ["both"] + }, + { + code: "module.exports = function foo(){}", + options: ["both"] } ], // Examples of code that should trigger the rule - invalid: [{ - code: "if (test) { function doSomething() { } }", - options: ["both"], - errors: [{ - messageId: "moveDeclToRoot", - data: { - type: "function", - body: "program" - }, - type: "FunctionDeclaration" - }] - }, { - code: "function doSomething() { do { function somethingElse() { } } while (test); }", - errors: [{ - messageId: "moveDeclToRoot", - data: { - type: "function", - body: "function body" - }, - type: "FunctionDeclaration" - }] - }, { - code: "(function() { if (test) { function doSomething() { } } }());", - errors: [{ - messageId: "moveDeclToRoot", - data: { - type: "function", - body: "function body" - }, - type: "FunctionDeclaration" - }] - }, { - code: "while (test) { var foo; }", - options: ["both"], - errors: [{ - messageId: "moveDeclToRoot", - data: { - type: "variable", - body: "program" - }, - type: "VariableDeclaration" - }] - }, { - code: "function doSomething() { if (test) { var foo = 42; } }", - options: ["both"], - errors: [{ - messageId: "moveDeclToRoot", - data: { - type: "variable", - body: "function body" - }, - type: "VariableDeclaration" - }] - }, { - code: "(function() { if (test) { var foo; } }());", - options: ["both"], - errors: [{ - messageId: "moveDeclToRoot", - data: { - type: "variable", - body: "function body" - }, - type: "VariableDeclaration" - }] - }] + invalid: [ + { + code: "if (test) { function doSomething() { } }", + options: ["both"], + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "function", + body: "program" + }, + type: "FunctionDeclaration" + }] + }, { + code: "if (foo) var a; ", + options: ["both"], + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "variable", + body: "program" + }, + type: "VariableDeclaration" + }] + }, { + code: "if (foo) /* some comments */ var a; ", + options: ["both"], + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "variable", + body: "program" + }, + type: "VariableDeclaration" + }] + }, { + code: "if (foo){ function f(){ if(bar){ var a; } } }", + options: ["both"], + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "function", + body: "program" + }, + type: "FunctionDeclaration" + }, { + messageId: "moveDeclToRoot", + data: { + type: "variable", + body: "function body" + }, + type: "VariableDeclaration" + }] + }, { + code: "if (foo) function f(){ if(bar) var a; } ", + options: ["both"], + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "function", + body: "program" + }, + type: "FunctionDeclaration" + }, { + messageId: "moveDeclToRoot", + data: { + type: "variable", + body: "function body" + }, + type: "VariableDeclaration" + }] + }, { + code: "if (foo) { var fn = function(){} } ", + options: ["both"], + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "variable", + body: "program" + }, + type: "VariableDeclaration" + }] + }, + { + code: "if (foo) function f(){} ", + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "function", + body: "program" + }, + type: "FunctionDeclaration" + }] + }, + { + code: "function bar() { if (foo) function f(){}; }", + options: ["both"], + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "function", + body: "function body" + }, + type: "FunctionDeclaration" + }] + }, + { + code: "function bar() { if (foo) var a; }", + options: ["both"], + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "variable", + body: "function body" + }, + type: "VariableDeclaration" + }] + }, + { + code: "if (foo){ var a; }", + options: ["both"], + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "variable", + body: "program" + }, + type: "VariableDeclaration" + }] + }, { + code: "function doSomething() { do { function somethingElse() { } } while (test); }", + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "function", + body: "function body" + }, + type: "FunctionDeclaration" + }] + }, { + code: "(function() { if (test) { function doSomething() { } } }());", + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "function", + body: "function body" + }, + type: "FunctionDeclaration" + }] + }, { + code: "while (test) { var foo; }", + options: ["both"], + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "variable", + body: "program" + }, + type: "VariableDeclaration" + }] + }, { + code: "function doSomething() { if (test) { var foo = 42; } }", + options: ["both"], + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "variable", + body: "function body" + }, + type: "VariableDeclaration" + }] + }, { + code: "(function() { if (test) { var foo; } }());", + options: ["both"], + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "variable", + body: "function body" + }, + type: "VariableDeclaration" + }] + }, { + code: "const doSomething = () => { if (test) { var foo = 42; } }", + options: ["both"], + parserOptions: { ecmaVersion: 6 }, + errors: [{ + messageId: "moveDeclToRoot", + data: { + type: "variable", + body: "function body" + }, + type: "VariableDeclaration" + }] + } + + ] }); diff --git a/eslint/tests/lib/rules/no-new-object.js b/eslint/tests/lib/rules/no-new-object.js index 667da7c..0e8f5eb 100644 --- a/eslint/tests/lib/rules/no-new-object.js +++ b/eslint/tests/lib/rules/no-new-object.js @@ -20,15 +20,50 @@ const ruleTester = new RuleTester(); ruleTester.run("no-new-object", rule, { valid: [ - "var foo = new foo.Object()" + "var myObject = {};", + "var myObject = new CustomObject();", + "var foo = new foo.Object()", + `var Object = function Object() {}; + new Object();`, + `var x = something ? MyClass : Object; + var y = new x();`, + { + code: ` + class Object { + constructor(){ + + } + } + new Object(); + `, + parserOptions: { ecmaVersion: 6 } + }, + { + code: ` + import { Object } from './' + new Object(); + `, + parserOptions: { ecmaVersion: 6, sourceType: "module" } + } ], invalid: [ { code: "var foo = new Object()", - errors: [{ - messageId: "preferLiteral", - type: "NewExpression" - }] + errors: [ + { + messageId: "preferLiteral", + type: "NewExpression" + } + ] + }, + { + code: "new Object();", + errors: [{ messageId: "preferLiteral", type: "NewExpression" }] + }, + { + code: "const a = new Object()", + parserOptions: { ecmaVersion: 6 }, + errors: [{ messageId: "preferLiteral", type: "NewExpression" }] } ] }); diff --git a/eslint/tests/lib/rules/no-return-assign.js b/eslint/tests/lib/rules/no-return-assign.js index e9d0c58..600503c 100644 --- a/eslint/tests/lib/rules/no-return-assign.js +++ b/eslint/tests/lib/rules/no-return-assign.js @@ -52,6 +52,19 @@ ruleTester.run("no-return-assign", rule, { { code: "() => (result = a * b)", options: ["except-parens"] + }, + "const foo = (a,b,c) => ((a = b), c)", + `function foo(){ + return (a = b) + }`, + `function bar(){ + return function foo(){ + return (a = b) && c + } + }`, + { + code: "const foo = (a) => (b) => (a = b)", + parserOptions: { ecmaVersion: 6 } } ], invalid: [ @@ -79,7 +92,12 @@ ruleTester.run("no-return-assign", rule, { }, { code: "() => result = a * b", - errors: [{ messageId: "arrowAssignment", type: "ArrowFunctionExpression" }] + errors: [ + { + messageId: "arrowAssignment", + type: "ArrowFunctionExpression" + } + ] }, { code: "function x() { return result = a * b; };", @@ -95,6 +113,68 @@ ruleTester.run("no-return-assign", rule, { code: "function x() { return result || (result = a * b); };", options: ["always"], errors: [{ messageId: "returnAssignment", type: "ReturnStatement" }] + }, + { + code: `function foo(){ + return a = b + }`, + errors: [{ messageId: "returnAssignment", type: "ReturnStatement" }] + }, + { + code: `function doSomething() { + return foo = bar && foo > 0; + }`, + errors: [{ messageId: "returnAssignment", type: "ReturnStatement" }] + }, + { + code: `function doSomething() { + return foo = function(){ + return (bar = bar1) + } + }`, + errors: [{ messageId: "returnAssignment", type: "ReturnStatement" }] + }, + { + code: `function doSomething() { + return foo = () => a + }`, + parserOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "returnAssignment", + type: "ReturnStatement" + } + ] + }, + { + code: `function doSomething() { + return () => a = () => b + }`, + parserOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "arrowAssignment", + type: "ArrowFunctionExpression" + } + ] + }, + { + code: `function foo(a){ + return function bar(b){ + return a = b + } + }`, + errors: [{ messageId: "returnAssignment", type: "ReturnStatement" }] + }, + { + code: "const foo = (a) => (b) => a = b", + parserOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "arrowAssignment", + type: "ArrowFunctionExpression" + } + ] } ] }); diff --git a/eslint/tests/lib/rules/no-unexpected-multiline.js b/eslint/tests/lib/rules/no-unexpected-multiline.js index 6d0eba3..29d05a9 100644 --- a/eslint/tests/lib/rules/no-unexpected-multiline.js +++ b/eslint/tests/lib/rules/no-unexpected-multiline.js @@ -43,6 +43,18 @@ ruleTester.run("no-unexpected-multiline", rule, { code: "x\n.y\nz `Valid Test Case`", parserOptions: { ecmaVersion: 6 } }, + { + code: "f(x\n)`Valid Test Case`", + parserOptions: { ecmaVersion: 6 } + }, + { + code: "x.\ny `Valid Test Case`", + parserOptions: { ecmaVersion: 6 } + }, + { + code: "(x\n)`Valid Test Case`", + parserOptions: { ecmaVersion: 6 } + }, ` foo / bar /2 @@ -118,8 +130,9 @@ ruleTester.run("no-unexpected-multiline", rule, { errors: [{ messageId: "function", line: 2, - column: 1 - + column: 1, + endLine: 2, + endColumn: 2 }] }, { @@ -127,6 +140,8 @@ ruleTester.run("no-unexpected-multiline", rule, { errors: [{ line: 2, column: 1, + endLine: 2, + endColumn: 2, messageId: "function" }] }, @@ -135,6 +150,8 @@ ruleTester.run("no-unexpected-multiline", rule, { errors: [{ line: 2, column: 1, + endLine: 2, + endColumn: 2, messageId: "function" }] }, @@ -143,6 +160,8 @@ ruleTester.run("no-unexpected-multiline", rule, { errors: [{ line: 2, column: 1, + endLine: 2, + endColumn: 2, messageId: "property" }] }, @@ -151,6 +170,8 @@ ruleTester.run("no-unexpected-multiline", rule, { errors: [{ line: 2, column: 5, + endLine: 2, + endColumn: 6, messageId: "function" }] }, @@ -159,6 +180,8 @@ ruleTester.run("no-unexpected-multiline", rule, { errors: [{ line: 2, column: 3, + endLine: 2, + endColumn: 4, messageId: "property" }] }, @@ -166,8 +189,10 @@ ruleTester.run("no-unexpected-multiline", rule, { code: "let x = function() {}\n `hello`", parserOptions: { ecmaVersion: 6 }, errors: [{ - line: 1, - column: 9, + line: 2, + column: 2, + endLine: 2, + endColumn: 3, messageId: "taggedTemplate" }] }, @@ -175,8 +200,10 @@ ruleTester.run("no-unexpected-multiline", rule, { code: "let x = function() {}\nx\n`hello`", parserOptions: { ecmaVersion: 6 }, errors: [{ - line: 2, + line: 3, column: 1, + endLine: 3, + endColumn: 2, messageId: "taggedTemplate" }] }, @@ -184,8 +211,10 @@ ruleTester.run("no-unexpected-multiline", rule, { code: "x\n.y\nz\n`Invalid Test Case`", parserOptions: { ecmaVersion: 6 }, errors: [{ - line: 3, + line: 4, column: 1, + endLine: 4, + endColumn: 2, messageId: "taggedTemplate" }] }, @@ -197,6 +226,8 @@ ruleTester.run("no-unexpected-multiline", rule, { errors: [{ line: 3, column: 17, + endLine: 3, + endColumn: 18, messageId: "division" }] }, @@ -208,6 +239,8 @@ ruleTester.run("no-unexpected-multiline", rule, { errors: [{ line: 3, column: 17, + endLine: 3, + endColumn: 18, messageId: "division" }] }, @@ -219,6 +252,8 @@ ruleTester.run("no-unexpected-multiline", rule, { errors: [{ line: 3, column: 17, + endLine: 3, + endColumn: 18, messageId: "division" }] }, @@ -230,6 +265,8 @@ ruleTester.run("no-unexpected-multiline", rule, { errors: [{ line: 3, column: 17, + endLine: 3, + endColumn: 18, messageId: "division" }] }, @@ -241,24 +278,28 @@ ruleTester.run("no-unexpected-multiline", rule, { errors: [{ line: 3, column: 17, + endLine: 3, + endColumn: 18, messageId: "division" }] }, // https://github.com/eslint/eslint/issues/11650 { - code: ` - const x = aaaa< - test - >/* - test - */\`foo\` - `, + code: [ + "const x = aaaa<", + " test", + ">/*", + "test", + "*/`foo`" + ].join("\n"), parser: require.resolve("../../fixtures/parsers/typescript-parsers/tagged-template-with-generic/tagged-template-with-generic-and-comment"), errors: [ { - line: 1, - column: 11, + line: 5, + column: 3, + endLine: 5, + endColumn: 4, messageId: "taggedTemplate" } ] diff --git a/eslint/tests/lib/rules/no-useless-concat.js b/eslint/tests/lib/rules/no-useless-concat.js index 9376266..c15b88f 100644 --- a/eslint/tests/lib/rules/no-useless-concat.js +++ b/eslint/tests/lib/rules/no-useless-concat.js @@ -42,7 +42,25 @@ ruleTester.run("no-useless-concat", rule, { { code: "'a' + 'b'", errors: [ - { messageId: "unexpectedConcat" } + { + messageId: "unexpectedConcat", + line: 1, + column: 5, + endLine: 1, + endColumn: 6 + } + ] + }, + { + code: "'a' +\n'b' + 'c'", + errors: [ + { + messageId: "unexpectedConcat", + line: 2, + column: 5, + endLine: 2, + endColumn: 6 + } ] }, { @@ -57,12 +75,16 @@ ruleTester.run("no-useless-concat", rule, { { messageId: "unexpectedConcat", line: 1, - column: 5 + column: 5, + endLine: 1, + endColumn: 6 }, { messageId: "unexpectedConcat", line: 1, - column: 11 + column: 11, + endLine: 1, + endColumn: 12 } ] }, diff --git a/eslint/tests/lib/rules/object-shorthand.js b/eslint/tests/lib/rules/object-shorthand.js index b9b8132..01a72f4 100644 --- a/eslint/tests/lib/rules/object-shorthand.js +++ b/eslint/tests/lib/rules/object-shorthand.js @@ -11,7 +11,7 @@ const rule = require("../../../lib/rules/object-shorthand"), { RuleTester } = require("../../../lib/rule-tester"); -const { unIndent } = require("../_utils"); +const { unIndent } = require("../../_utils"); //------------------------------------------------------------------------------ // Tests diff --git a/eslint/tests/lib/rules/space-before-function-paren.js b/eslint/tests/lib/rules/space-before-function-paren.js index bc19239..09187fa 100644 --- a/eslint/tests/lib/rules/space-before-function-paren.js +++ b/eslint/tests/lib/rules/space-before-function-paren.js @@ -136,7 +136,8 @@ ruleTester.run("space-before-function-paren", rule, { type: "FunctionDeclaration", messageId: "missingSpace", line: 1, - column: 13 + column: 13, + endColumn: 14 } ] }, @@ -148,7 +149,8 @@ ruleTester.run("space-before-function-paren", rule, { type: "FunctionDeclaration", messageId: "missingSpace", line: 1, - column: 13 + column: 18, + endColumn: 19 } ] }, @@ -230,7 +232,8 @@ ruleTester.run("space-before-function-paren", rule, { type: "FunctionDeclaration", messageId: "unexpectedSpace", line: 1, - column: 13 + column: 13, + endColumn: 14 } ] }, @@ -273,6 +276,20 @@ ruleTester.run("space-before-function-paren", rule, { } ] }, + { + code: "function foo () {}", + output: "function foo() {}", + options: ["never"], + errors: [ + { + type: "FunctionDeclaration", + messageId: "unexpectedSpace", + line: 1, + column: 13, + endColumn: 15 + } + ] + }, { code: "function foo//\n() {}", output: null, @@ -282,7 +299,9 @@ ruleTester.run("space-before-function-paren", rule, { type: "FunctionDeclaration", messageId: "unexpectedSpace", line: 1, - column: 13 + column: 13, + endLine: 2, + endColumn: 1 } ] }, @@ -321,7 +340,8 @@ ruleTester.run("space-before-function-paren", rule, { type: "FunctionExpression", messageId: "unexpectedSpace", line: 1, - column: 19 + column: 19, + endColumn: 20 } ] }, diff --git a/eslint/tests/lib/rules/yoda.js b/eslint/tests/lib/rules/yoda.js index c0d76ca..4344940 100644 --- a/eslint/tests/lib/rules/yoda.js +++ b/eslint/tests/lib/rules/yoda.js @@ -21,148 +21,314 @@ ruleTester.run("yoda", rule, { valid: [ // "never" mode - { code: "if (value === \"red\") {}", options: ["never"] }, + { code: 'if (value === "red") {}', options: ["never"] }, { code: "if (value === value) {}", options: ["never"] }, { code: "if (value != 5) {}", options: ["never"] }, { code: "if (5 & foo) {}", options: ["never"] }, { code: "if (5 === 4) {}", options: ["never"] }, - { code: "if (value === `red`) {}", options: ["never"], parserOptions: { ecmaVersion: 2015 } }, - { code: "if (`red` === `red`) {}", options: ["never"], parserOptions: { ecmaVersion: 2015 } }, - { code: "if (`${foo}` === `red`) {}", options: ["never"], parserOptions: { ecmaVersion: 2015 } }, - { code: "if (`${\"\"}` === `red`) {}", options: ["never"], parserOptions: { ecmaVersion: 2015 } }, - { code: "if (`${\"red\"}` === foo) {}", options: ["never"], parserOptions: { ecmaVersion: 2015 } }, - { code: "if (b > `a` && b > `a`) {}", options: ["never"], parserOptions: { ecmaVersion: 2015 } }, - { code: "if (`b` > `a` && \"b\" > \"a\") {}", options: ["never"], parserOptions: { ecmaVersion: 2015 } }, + { + code: "if (value === `red`) {}", + options: ["never"], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: "if (`red` === `red`) {}", + options: ["never"], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: "if (`${foo}` === `red`) {}", + options: ["never"], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: 'if (`${""}` === `red`) {}', + options: ["never"], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: 'if (`${"red"}` === foo) {}', + options: ["never"], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: "if (b > `a` && b > `a`) {}", + options: ["never"], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: 'if (`b` > `a` && "b" > "a") {}', + options: ["never"], + parserOptions: { ecmaVersion: 2015 } + }, // "always" mode - { code: "if (\"blue\" === value) {}", options: ["always"] }, + { code: 'if ("blue" === value) {}', options: ["always"] }, { code: "if (value === value) {}", options: ["always"] }, { code: "if (4 != value) {}", options: ["always"] }, { code: "if (foo & 4) {}", options: ["always"] }, { code: "if (5 === 4) {}", options: ["always"] }, - { code: "if (`red` === value) {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } }, - { code: "if (`red` === `red`) {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } }, - { code: "if (`red` === `${foo}`) {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } }, - { code: "if (`red` === `${\"\"}`) {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } }, - { code: "if (foo === `${\"red\"}`) {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } }, - { code: "if (`a` > b && `a` > b) {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } }, - { code: "if (`b` > `a` && \"b\" > \"a\") {}", options: ["always"], parserOptions: { ecmaVersion: 2015 } }, + { + code: "if (`red` === value) {}", + options: ["always"], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: "if (`red` === `red`) {}", + options: ["always"], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: "if (`red` === `${foo}`) {}", + options: ["always"], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: 'if (`red` === `${""}`) {}', + options: ["always"], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: 'if (foo === `${"red"}`) {}', + options: ["always"], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: "if (`a` > b && `a` > b) {}", + options: ["always"], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: 'if (`b` > `a` && "b" > "a") {}', + options: ["always"], + parserOptions: { ecmaVersion: 2015 } + }, // Range exception { - code: "if (0 < x && x <= 1) {}", + code: 'if ("a" < x && x < MAX ) {}', options: ["never", { exceptRange: true }] - }, { - code: "if (x < 0 || 1 <= x) {}", + }, + { + code: "if (1 < x && x < MAX ) {}", + options: ["never", { exceptRange: true }] + }, + { + code: "if ('a' < x && x < MAX ) {}", + options: ["never", { exceptRange: true }] + }, + { + code: "if (x < `x` || `x` <= x) {}", + options: ["never", { exceptRange: true }], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: "if (0 < x && x <= 1) {}", options: ["never", { exceptRange: true }] - }, { + }, + { code: "if (0 <= x && x < 1) {}", options: ["always", { exceptRange: true }] - }, { - code: "if (x <= 'bar' || 'foo' < x) {}", - options: ["always", { exceptRange: true }] - }, { + }, + { code: "if ('blue' < x.y && x.y < 'green') {}", options: ["never", { exceptRange: true }] - }, { + }, + { code: "if (0 < x[``] && x[``] < 100) {}", options: ["never", { exceptRange: true }], parserOptions: { ecmaVersion: 2015 } - }, { + }, + { code: "if (0 < x[''] && x[``] < 100) {}", options: ["never", { exceptRange: true }], parserOptions: { ecmaVersion: 2015 } - }, { + }, + { + code: + "if (a < 4 || (b[c[0]].d['e'] < 0 || 1 <= b[c[0]].d['e'])) {}", + options: ["never", { exceptRange: true }] + }, + { code: "if (0 <= x['y'] && x['y'] <= 100) {}", options: ["never", { exceptRange: true }] - }, { + }, + { code: "if (a < 0 && (0 < b && b < 1)) {}", options: ["never", { exceptRange: true }] - }, { + }, + { code: "if ((0 < a && a < 1) && b < 0) {}", options: ["never", { exceptRange: true }] - }, { - code: "if (a < 4 || (b[c[0]].d['e'] < 0 || 1 <= b[c[0]].d['e'])) {}", - options: ["never", { exceptRange: true }] - }, { + }, + { code: "if (-1 < x && x < 0) {}", options: ["never", { exceptRange: true }] - }, { + }, + { code: "if (0 <= this.prop && this.prop <= 1) {}", options: ["never", { exceptRange: true }] - }, { + }, + { code: "if (0 <= index && index < list.length) {}", options: ["never", { exceptRange: true }] - }, { + }, + { code: "if (ZERO <= index && index < 100) {}", options: ["never", { exceptRange: true }] - }, { + }, + { code: "if (value <= MIN || 10 < value) {}", options: ["never", { exceptRange: true }] - }, { + }, + { code: "if (value <= 0 || MAX < value) {}", options: ["never", { exceptRange: true }] - }, { - code: "if (0 <= a.b && a[\"b\"] <= 100) {}", + }, + { + code: 'if (0 <= a.b && a["b"] <= 100) {}', options: ["never", { exceptRange: true }] - }, { + }, + { code: "if (0 <= a.b && a[`b`] <= 100) {}", options: ["never", { exceptRange: true }], parserOptions: { ecmaVersion: 2015 } - }, { + }, + { code: "if (-1n < x && x <= 1n) {}", options: ["never", { exceptRange: true }], parserOptions: { ecmaVersion: 2020 } - }, { - code: "if (x < -1n || 1n <= x) {}", - options: ["never", { exceptRange: true }], - parserOptions: { ecmaVersion: 2020 } - }, { + }, + { code: "if (-1n <= x && x < 1n) {}", options: ["always", { exceptRange: true }], parserOptions: { ecmaVersion: 2020 } - }, { - code: "if (x < -1n || 1n <= x) {}", - options: ["always", { exceptRange: true }], - parserOptions: { ecmaVersion: 2020 } - }, { + }, + { code: "if (x < `1` || `1` < x) {}", options: ["always", { exceptRange: true }], parserOptions: { ecmaVersion: 2020 } - }, { + }, + { code: "if (1 <= a['/(?0)/'] && a[/(?0)/] <= 100) {}", options: ["never", { exceptRange: true }], parserOptions: { ecmaVersion: 2018 } - }, { + }, + { code: "if (x <= `bar` || `foo` < x) {}", options: ["always", { exceptRange: true }], parserOptions: { ecmaVersion: 2015 } - }, { + }, + { + code: "if ('a' < x && x < MAX ) {}", + options: ["always", { exceptRange: true }], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: "if ('a' < x && x < MAX ) {}", + options: ["always"], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: "if (MIN < x && x < 'a' ) {}", + options: ["never", { exceptRange: true }], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: "if (MIN < x && x < 'a' ) {}", + options: ["never"], + parserOptions: { ecmaVersion: 2015 } + }, + { code: "if (`blue` < x.y && x.y < `green`) {}", options: ["never", { exceptRange: true }], parserOptions: { ecmaVersion: 2015 } - }, { + }, + { code: "if (0 <= x[`y`] && x[`y`] <= 100) {}", options: ["never", { exceptRange: true }], parserOptions: { ecmaVersion: 2015 } - }, { - code: "if (0 <= x[`y`] && x[\"y\"] <= 100) {}", + }, + { + code: 'if (0 <= x[`y`] && x["y"] <= 100) {}', options: ["never", { exceptRange: true }], parserOptions: { ecmaVersion: 2015 } }, + { + code: "if ('a' <= x && x < 'b') {}", + options: ["never", { exceptRange: true }] + }, + { + code: "if (x < -1n || 1n <= x) {}", + options: ["never", { exceptRange: true }], + parserOptions: { ecmaVersion: 2020 } + }, + { + code: "if (x < -1n || 1n <= x) {}", + options: ["always", { exceptRange: true }], + parserOptions: { ecmaVersion: 2020 } + }, + { + code: "if (1 < a && a <= 2) {}", + options: ["never", { exceptRange: true }] + }, + { + code: "if (x < -1 || 1 < x) {}", + options: ["never", { exceptRange: true }] + }, + { + code: "if (x <= 'bar' || 'foo' < x) {}", + options: ["always", { exceptRange: true }] + }, + { + code: "if (x < 0 || 1 <= x) {}", + options: ["never", { exceptRange: true }] + }, + { + code: "if('a' <= x && x < MAX) {}", + options: ["never", { exceptRange: true }] + }, // onlyEquality - { code: "if (0 < x && x <= 1) {}", options: ["never", { onlyEquality: true }] }, - { code: "if (x !== 'foo' && 'foo' !== x) {}", options: ["never", { onlyEquality: true }] }, - { code: "if (x < 2 && x !== -3) {}", options: ["always", { onlyEquality: true }] }, - { code: "if (x !== `foo` && `foo` !== x) {}", options: ["never", { onlyEquality: true }], parserOptions: { ecmaVersion: 2015 } }, - { code: "if (x < `2` && x !== `-3`) {}", options: ["always", { onlyEquality: true }], parserOptions: { ecmaVersion: 2015 } } + { + code: "if (0 < x && x <= 1) {}", + options: ["never", { onlyEquality: true }] + }, + { + code: "if (x !== 'foo' && 'foo' !== x) {}", + options: ["never", { onlyEquality: true }] + }, + { + code: "if (x < 2 && x !== -3) {}", + options: ["always", { onlyEquality: true }] + }, + { + code: "if (x !== `foo` && `foo` !== x) {}", + options: ["never", { onlyEquality: true }], + parserOptions: { ecmaVersion: 2015 } + }, + { + code: "if (x < `2` && x !== `-3`) {}", + options: ["always", { onlyEquality: true }], + parserOptions: { ecmaVersion: 2015 } + } ], invalid: [ - { - code: "if (\"red\" == value) {}", - output: "if (value == \"red\") {}", + code: "if (x <= 'foo' || 'bar' < x) {}", + output: "if ('foo' >= x || 'bar' < x) {}", + options: ["always", { exceptRange: true }], + errors: [ + { + messageId: "expected", + data: { expectedSide: "left", operator: "<=" }, + type: "BinaryExpression" + } + ] + }, + { + code: 'if ("red" == value) {}', + output: 'if (value == "red") {}', options: ["never"], errors: [ { @@ -222,8 +388,8 @@ ruleTester.run("yoda", rule, { ] }, { - code: "if (\"red\" <= value) {}", - output: "if (value >= \"red\") {}", + code: 'if ("red" <= value) {}', + output: 'if (value >= "red") {}', options: ["never"], errors: [ { @@ -260,8 +426,8 @@ ruleTester.run("yoda", rule, { ] }, { - code: "if (`red` <= `${\"red\"}`) {}", - output: "if (`${\"red\"}` >= `red`) {}", + code: 'if (`red` <= `${"red"}`) {}', + output: 'if (`${"red"}` >= `red`) {}', options: ["never"], parserOptions: { ecmaVersion: 2015 }, errors: [ @@ -321,8 +487,8 @@ ruleTester.run("yoda", rule, { ] }, { - code: "if (value == \"red\") {}", - output: "if (\"red\" == value) {}", + code: 'if (value == "red") {}', + output: 'if ("red" == value) {}', options: ["always"], errors: [ { @@ -371,8 +537,8 @@ ruleTester.run("yoda", rule, { ] }, { - code: "if (`${\"red\"}` <= `red`) {}", - output: "if (`red` >= `${\"red\"}`) {}", + code: 'if (`${"red"}` <= `red`) {}', + output: 'if (`red` >= `${"red"}`) {}', options: ["always"], parserOptions: { ecmaVersion: 2015 }, errors: [ @@ -480,6 +646,19 @@ ruleTester.run("yoda", rule, { } ] }, + { + code: "if (`green` < x.y && x.y < `blue`) {}", + output: "if (x.y > `green` && x.y < `blue`) {}", + options: ["never", { exceptRange: true }], + parserOptions: { ecmaVersion: 2015 }, + errors: [ + { + messageId: "expected", + data: { expectedSide: "right", operator: "<" }, + type: "BinaryExpression" + } + ] + }, { code: "if (0 <= a[b] && a['b'] < 1) {}", output: "if (a[b] >= 0 && a['b'] < 1) {}", @@ -641,19 +820,6 @@ ruleTester.run("yoda", rule, { } ] }, - { - code: "if (`green` < x.y && x.y < `blue`) {}", - output: "if (x.y > `green` && x.y < `blue`) {}", - options: ["never", { exceptRange: true }], - parserOptions: { ecmaVersion: 2015 }, - errors: [ - { - messageId: "expected", - data: { expectedSide: "right", operator: "<" }, - type: "BinaryExpression" - } - ] - }, { code: "if (3 == a) {}", output: "if (a == 3) {}", @@ -992,6 +1158,67 @@ ruleTester.run("yoda", rule, { type: "BinaryExpression" } ] + }, + { + code: "if (`green` < x.y && x.y < `blue`) {}", + output: "if (`green` < x.y && `blue` > x.y) {}", + options: ["always", { exceptRange: true }], + parserOptions: { ecmaVersion: 2015 }, + errors: [ + { + messageId: "expected", + data: { expectedSide: "left", operator: "<" }, + type: "BinaryExpression" + } + ] + }, + { + code: "if('a' <= x && x < 'b') {}", + output: "if('a' <= x && 'b' > x) {}", + options: ["always"], + errors: [ + { + messageId: "expected", + data: { expectedSide: "left", operator: "<" }, + type: "BinaryExpression" + } + ] + }, + { + code: "if ('b' <= x && x < 'a') {}", + output: "if (x >= 'b' && x < 'a') {}", + options: ["never", { exceptRange: true }], + errors: [ + { + messageId: "expected", + data: { expectedSide: "right", operator: "<=" }, + type: "BinaryExpression" + } + ] + }, + { + code: "if('a' <= x && x < 1) {}", + output: "if(x >= 'a' && x < 1) {}", + options: ["never", { exceptRange: true }], + errors: [ + { + messageId: "expected", + data: { expectedSide: "right", operator: "<=" }, + type: "BinaryExpression" + } + ] + }, + { + code: "if (0 < a && b < max) {}", + output: "if (a > 0 && b < max) {}", + options: ["never", { exceptRange: true }], + errors: [ + { + messageId: "expected", + data: { expectedSide: "right", operator: "<" }, + type: "BinaryExpression" + } + ] } ] }); diff --git a/eslint/tests/lib/shared/runtime-info.js b/eslint/tests/lib/shared/runtime-info.js index 79c50a4..76ca7c7 100644 --- a/eslint/tests/lib/shared/runtime-info.js +++ b/eslint/tests/lib/shared/runtime-info.js @@ -12,7 +12,7 @@ const assert = require("chai").assert; const sinon = require("sinon"); const spawn = require("cross-spawn"); -const { unIndent } = require("../_utils"); +const { unIndent } = require("../../_utils"); const RuntimeInfo = require("../../../lib/shared/runtime-info"); const log = require("../../../lib/shared/logging"); const packageJson = require("../../../package.json"); diff --git a/eslint/tests/performance/jshint.js b/eslint/tests/performance/jshint.js index 6ebb5fb..f79d6ed 100644 --- a/eslint/tests/performance/jshint.js +++ b/eslint/tests/performance/jshint.js @@ -1111,7 +1111,7 @@ var state = require("./state.js").state; var style = require("./style.js"); // We need this module here because environments such as IE and Rhino -// don't necessarilly expose the 'console' API and browserify uses +// don't necessarily expose the 'console' API and browserify uses // it to log things. It's a sad state of affair, really. var console = require("console-browserify"); @@ -1169,7 +1169,7 @@ var JSHINT = (function () { immed : true, // if immediate invocations must be wrapped in parens iterator : true, // if the `__iterator__` property should be allowed jquery : true, // if jQuery globals should be predefined - lastsemic : true, // if semicolons may be ommitted for the trailing + lastsemic : true, // if semicolons may be omitted for the trailing // statements inside of a one-line blocks. laxbreak : true, // if line breaks should not be checked laxcomma : true, // if line breaks should not be checked around commas @@ -7466,7 +7466,7 @@ Lexer.prototype = { }, /* - * Scan for any occurence of mixed tabs and spaces. If smarttabs option + * Scan for any occurrence of mixed tabs and spaces. If smarttabs option * is on, ignore tabs followed by spaces. * * Tabs followed by one space followed by a block comment are allowed. @@ -7594,7 +7594,7 @@ Lexer.prototype = { /* * Produce the next token. This function is called by advance() to get - * the next token. It retuns a token in a JSLint-compatible format. + * the next token. It returns a token in a JSLint-compatible format. */ token: function () { /*jshint loopfunc:true */ diff --git a/src/eslint.js b/src/eslint.js index cb7b803..554756b 100755 --- a/src/eslint.js +++ b/src/eslint.js @@ -1496,7 +1496,7 @@ var store = __webpack_require__(26); (module.exports = function (key, value) { return store[key] || (store[key] = value !== undefined ? value : {}); })('versions', []).push({ - version: '3.6.4', + version: '3.6.5', mode: IS_PURE ? 'pure' : 'global', copyright: '© 2020 Denis Pushkarev (zloirock.ru)' }); @@ -9509,7 +9509,7 @@ if (!set || !clear) { channel.port1.onmessage = listener; defer = bind(port.postMessage, port, 1); // Browsers with postMessage, skip WebWorkers // IE8 has postMessage, but it's sync & typeof its postMessage is 'object' - } else if (global.addEventListener && typeof postMessage == 'function' && !global.importScripts && !fails(post)) { + } else if (global.addEventListener && typeof postMessage == 'function' && !global.importScripts && !fails(post) && location.protocol !== 'file:') { defer = post; global.addEventListener('message', listener, false); // IE8- } else if (ONREADYSTATECHANGE in createElement('script')) { @@ -13041,7 +13041,7 @@ var INVALID_SCHEME = 'Invalid scheme'; var INVALID_HOST = 'Invalid host'; var INVALID_PORT = 'Invalid port'; var ALPHA = /[A-Za-z]/; -var ALPHANUMERIC = /[\d+\-.A-Za-z]/; +var ALPHANUMERIC = /[\d+-.A-Za-z]/; var DIGIT = /\d/; var HEX_START = /^(0x|0X)/; var OCT = /^[0-7]+$/; @@ -16778,7 +16778,7 @@ class Linter { supportsAutofix } = processor; const disableFixes = options.disableFixes || !supportsAutofix; - return this._verifyWithProcessor(textOrSourceCode, config, _objectSpread({}, options, { + return this._verifyWithProcessor(textOrSourceCode, config, _objectSpread(_objectSpread({}, options), {}, { disableFixes, postprocess, preprocess @@ -16827,13 +16827,13 @@ class Linter { if (configForRecursive && path.extname(blockName) !== originalExtname) { debug("Resolving configuration again because the file extension was changed."); - return this._verifyWithConfigArray(blockText, configForRecursive, _objectSpread({}, options, { + return this._verifyWithConfigArray(blockText, configForRecursive, _objectSpread(_objectSpread({}, options), {}, { filename: blockName })); } // Does lint. - return this._verifyWithoutProcessors(blockText, config, _objectSpread({}, options, { + return this._verifyWithoutProcessors(blockText, config, _objectSpread(_objectSpread({}, options), {}, { filename: blockName })); }); @@ -18967,7 +18967,7 @@ module.exports = { /* 383 */ /***/ (function(module) { -module.exports = JSON.parse("{\"_from\":\"estraverse@^4.1.1\",\"_id\":\"estraverse@4.3.0\",\"_inBundle\":false,\"_integrity\":\"sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==\",\"_location\":\"/estraverse\",\"_phantomChildren\":{},\"_requested\":{\"type\":\"range\",\"registry\":true,\"raw\":\"estraverse@^4.1.1\",\"name\":\"estraverse\",\"escapedName\":\"estraverse\",\"rawSpec\":\"^4.1.1\",\"saveSpec\":null,\"fetchSpec\":\"^4.1.1\"},\"_requiredBy\":[\"/babel-eslint/eslint-scope\",\"/eslint-scope\",\"/esrecurse\",\"/webpack/eslint-scope\"],\"_resolved\":\"https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz\",\"_shasum\":\"398ad3f3c5a24948be7725e83d11a7de28cdbd1d\",\"_spec\":\"estraverse@^4.1.1\",\"_where\":\"/home/dcsapak/git/pve-eslint/eslint-v7.0.0-alpha.3/node_modules/eslint-scope\",\"bugs\":{\"url\":\"https://github.com/estools/estraverse/issues\"},\"bundleDependencies\":false,\"deprecated\":false,\"description\":\"ECMAScript JS AST traversal functions\",\"devDependencies\":{\"babel-preset-env\":\"^1.6.1\",\"babel-register\":\"^6.3.13\",\"chai\":\"^2.1.1\",\"espree\":\"^1.11.0\",\"gulp\":\"^3.8.10\",\"gulp-bump\":\"^0.2.2\",\"gulp-filter\":\"^2.0.0\",\"gulp-git\":\"^1.0.1\",\"gulp-tag-version\":\"^1.3.0\",\"jshint\":\"^2.5.6\",\"mocha\":\"^2.1.0\"},\"engines\":{\"node\":\">=4.0\"},\"homepage\":\"https://github.com/estools/estraverse\",\"license\":\"BSD-2-Clause\",\"main\":\"estraverse.js\",\"maintainers\":[{\"name\":\"Yusuke Suzuki\",\"email\":\"utatane.tea@gmail.com\",\"url\":\"http://github.com/Constellation\"}],\"name\":\"estraverse\",\"repository\":{\"type\":\"git\",\"url\":\"git+ssh://git@github.com/estools/estraverse.git\"},\"scripts\":{\"lint\":\"jshint estraverse.js\",\"test\":\"npm run-script lint && npm run-script unit-test\",\"unit-test\":\"mocha --compilers js:babel-register\"},\"version\":\"4.3.0\"}"); +module.exports = JSON.parse("{\"_from\":\"estraverse@^4.1.1\",\"_id\":\"estraverse@4.3.0\",\"_inBundle\":false,\"_integrity\":\"sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==\",\"_location\":\"/estraverse\",\"_phantomChildren\":{},\"_requested\":{\"type\":\"range\",\"registry\":true,\"raw\":\"estraverse@^4.1.1\",\"name\":\"estraverse\",\"escapedName\":\"estraverse\",\"rawSpec\":\"^4.1.1\",\"saveSpec\":null,\"fetchSpec\":\"^4.1.1\"},\"_requiredBy\":[\"/babel-eslint/eslint-scope\",\"/eslint-scope\",\"/esrecurse\",\"/webpack/eslint-scope\"],\"_resolved\":\"https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz\",\"_shasum\":\"398ad3f3c5a24948be7725e83d11a7de28cdbd1d\",\"_spec\":\"estraverse@^4.1.1\",\"_where\":\"/home/dcsapak/git/pve-eslint/eslint-v7.0.0/node_modules/eslint-scope\",\"bugs\":{\"url\":\"https://github.com/estools/estraverse/issues\"},\"bundleDependencies\":false,\"deprecated\":false,\"description\":\"ECMAScript JS AST traversal functions\",\"devDependencies\":{\"babel-preset-env\":\"^1.6.1\",\"babel-register\":\"^6.3.13\",\"chai\":\"^2.1.1\",\"espree\":\"^1.11.0\",\"gulp\":\"^3.8.10\",\"gulp-bump\":\"^0.2.2\",\"gulp-filter\":\"^2.0.0\",\"gulp-git\":\"^1.0.1\",\"gulp-tag-version\":\"^1.3.0\",\"jshint\":\"^2.5.6\",\"mocha\":\"^2.1.0\"},\"engines\":{\"node\":\">=4.0\"},\"homepage\":\"https://github.com/estools/estraverse\",\"license\":\"BSD-2-Clause\",\"main\":\"estraverse.js\",\"maintainers\":[{\"name\":\"Yusuke Suzuki\",\"email\":\"utatane.tea@gmail.com\",\"url\":\"http://github.com/Constellation\"}],\"name\":\"estraverse\",\"repository\":{\"type\":\"git\",\"url\":\"git+ssh://git@github.com/estools/estraverse.git\"},\"scripts\":{\"lint\":\"jshint estraverse.js\",\"test\":\"npm run-script lint && npm run-script unit-test\",\"unit-test\":\"mocha --compilers js:babel-register\"},\"version\":\"4.3.0\"}"); /***/ }), /* 384 */ @@ -20026,7 +20026,7 @@ module.exports = Referencer; /* 389 */ /***/ (function(module) { -module.exports = JSON.parse("{\"_from\":\"esrecurse@^4.1.0\",\"_id\":\"esrecurse@4.2.1\",\"_inBundle\":false,\"_integrity\":\"sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==\",\"_location\":\"/esrecurse\",\"_phantomChildren\":{},\"_requested\":{\"type\":\"range\",\"registry\":true,\"raw\":\"esrecurse@^4.1.0\",\"name\":\"esrecurse\",\"escapedName\":\"esrecurse\",\"rawSpec\":\"^4.1.0\",\"saveSpec\":null,\"fetchSpec\":\"^4.1.0\"},\"_requiredBy\":[\"/babel-eslint/eslint-scope\",\"/eslint-scope\",\"/webpack/eslint-scope\"],\"_resolved\":\"https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz\",\"_shasum\":\"007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf\",\"_spec\":\"esrecurse@^4.1.0\",\"_where\":\"/home/dcsapak/git/pve-eslint/eslint-v7.0.0-alpha.3/node_modules/eslint-scope\",\"babel\":{\"presets\":[\"es2015\"]},\"bugs\":{\"url\":\"https://github.com/estools/esrecurse/issues\"},\"bundleDependencies\":false,\"dependencies\":{\"estraverse\":\"^4.1.0\"},\"deprecated\":false,\"description\":\"ECMAScript AST recursive visitor\",\"devDependencies\":{\"babel-cli\":\"^6.24.1\",\"babel-eslint\":\"^7.2.3\",\"babel-preset-es2015\":\"^6.24.1\",\"babel-register\":\"^6.24.1\",\"chai\":\"^4.0.2\",\"esprima\":\"^4.0.0\",\"gulp\":\"^3.9.0\",\"gulp-bump\":\"^2.7.0\",\"gulp-eslint\":\"^4.0.0\",\"gulp-filter\":\"^5.0.0\",\"gulp-git\":\"^2.4.1\",\"gulp-mocha\":\"^4.3.1\",\"gulp-tag-version\":\"^1.2.1\",\"jsdoc\":\"^3.3.0-alpha10\",\"minimist\":\"^1.1.0\"},\"engines\":{\"node\":\">=4.0\"},\"homepage\":\"https://github.com/estools/esrecurse\",\"license\":\"BSD-2-Clause\",\"main\":\"esrecurse.js\",\"maintainers\":[{\"name\":\"Yusuke Suzuki\",\"email\":\"utatane.tea@gmail.com\",\"url\":\"https://github.com/Constellation\"}],\"name\":\"esrecurse\",\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/estools/esrecurse.git\"},\"scripts\":{\"lint\":\"gulp lint\",\"test\":\"gulp travis\",\"unit-test\":\"gulp test\"},\"version\":\"4.2.1\"}"); +module.exports = JSON.parse("{\"_from\":\"esrecurse@^4.1.0\",\"_id\":\"esrecurse@4.2.1\",\"_inBundle\":false,\"_integrity\":\"sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==\",\"_location\":\"/esrecurse\",\"_phantomChildren\":{},\"_requested\":{\"type\":\"range\",\"registry\":true,\"raw\":\"esrecurse@^4.1.0\",\"name\":\"esrecurse\",\"escapedName\":\"esrecurse\",\"rawSpec\":\"^4.1.0\",\"saveSpec\":null,\"fetchSpec\":\"^4.1.0\"},\"_requiredBy\":[\"/babel-eslint/eslint-scope\",\"/eslint-scope\",\"/webpack/eslint-scope\"],\"_resolved\":\"https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz\",\"_shasum\":\"007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf\",\"_spec\":\"esrecurse@^4.1.0\",\"_where\":\"/home/dcsapak/git/pve-eslint/eslint-v7.0.0/node_modules/eslint-scope\",\"babel\":{\"presets\":[\"es2015\"]},\"bugs\":{\"url\":\"https://github.com/estools/esrecurse/issues\"},\"bundleDependencies\":false,\"dependencies\":{\"estraverse\":\"^4.1.0\"},\"deprecated\":false,\"description\":\"ECMAScript AST recursive visitor\",\"devDependencies\":{\"babel-cli\":\"^6.24.1\",\"babel-eslint\":\"^7.2.3\",\"babel-preset-es2015\":\"^6.24.1\",\"babel-register\":\"^6.24.1\",\"chai\":\"^4.0.2\",\"esprima\":\"^4.0.0\",\"gulp\":\"^3.9.0\",\"gulp-bump\":\"^2.7.0\",\"gulp-eslint\":\"^4.0.0\",\"gulp-filter\":\"^5.0.0\",\"gulp-git\":\"^2.4.1\",\"gulp-mocha\":\"^4.3.1\",\"gulp-tag-version\":\"^1.2.1\",\"jsdoc\":\"^3.3.0-alpha10\",\"minimist\":\"^1.1.0\"},\"engines\":{\"node\":\">=4.0\"},\"homepage\":\"https://github.com/estools/esrecurse\",\"license\":\"BSD-2-Clause\",\"main\":\"esrecurse.js\",\"maintainers\":[{\"name\":\"Yusuke Suzuki\",\"email\":\"utatane.tea@gmail.com\",\"url\":\"https://github.com/Constellation\"}],\"name\":\"esrecurse\",\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/estools/esrecurse.git\"},\"scripts\":{\"lint\":\"gulp lint\",\"test\":\"gulp travis\",\"unit-test\":\"gulp test\"},\"version\":\"4.2.1\"}"); /***/ }), /* 390 */ @@ -20177,7 +20177,7 @@ module.exports = PatternVisitor; /* 391 */ /***/ (function(module) { -module.exports = JSON.parse("{\"_from\":\"eslint-scope@^5.0.0\",\"_id\":\"eslint-scope@5.0.0\",\"_inBundle\":false,\"_integrity\":\"sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==\",\"_location\":\"/eslint-scope\",\"_phantomChildren\":{},\"_requested\":{\"type\":\"range\",\"registry\":true,\"raw\":\"eslint-scope@^5.0.0\",\"name\":\"eslint-scope\",\"escapedName\":\"eslint-scope\",\"rawSpec\":\"^5.0.0\",\"saveSpec\":null,\"fetchSpec\":\"^5.0.0\"},\"_requiredBy\":[\"/\",\"/eslint\"],\"_resolved\":\"https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz\",\"_shasum\":\"e87c8887c73e8d1ec84f1ca591645c358bfc8fb9\",\"_spec\":\"eslint-scope@^5.0.0\",\"_where\":\"/home/dcsapak/git/pve-eslint/eslint-v7.0.0-alpha.3\",\"bugs\":{\"url\":\"https://github.com/eslint/eslint-scope/issues\"},\"bundleDependencies\":false,\"dependencies\":{\"esrecurse\":\"^4.1.0\",\"estraverse\":\"^4.1.1\"},\"deprecated\":false,\"description\":\"ECMAScript scope analyzer for ESLint\",\"devDependencies\":{\"@typescript-eslint/parser\":\"^1.11.0\",\"chai\":\"^4.2.0\",\"eslint\":\"^6.0.1\",\"eslint-config-eslint\":\"^5.0.1\",\"eslint-plugin-node\":\"^9.1.0\",\"eslint-release\":\"^1.0.0\",\"espree\":\"^6.0.0\",\"istanbul\":\"^0.4.5\",\"mocha\":\"^6.1.4\",\"npm-license\":\"^0.3.3\",\"shelljs\":\"^0.8.3\",\"typescript\":\"^3.5.2\"},\"engines\":{\"node\":\">=8.0.0\"},\"files\":[\"LICENSE\",\"README.md\",\"lib\"],\"homepage\":\"http://github.com/eslint/eslint-scope\",\"license\":\"BSD-2-Clause\",\"main\":\"lib/index.js\",\"name\":\"eslint-scope\",\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/eslint/eslint-scope.git\"},\"scripts\":{\"generate-alpharelease\":\"eslint-generate-prerelease alpha\",\"generate-betarelease\":\"eslint-generate-prerelease beta\",\"generate-rcrelease\":\"eslint-generate-prerelease rc\",\"generate-release\":\"eslint-generate-release\",\"lint\":\"node Makefile.js lint\",\"publish-release\":\"eslint-publish-release\",\"test\":\"node Makefile.js test\"},\"version\":\"5.0.0\"}"); +module.exports = JSON.parse("{\"_from\":\"eslint-scope@^5.0.0\",\"_id\":\"eslint-scope@5.0.0\",\"_inBundle\":false,\"_integrity\":\"sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==\",\"_location\":\"/eslint-scope\",\"_phantomChildren\":{},\"_requested\":{\"type\":\"range\",\"registry\":true,\"raw\":\"eslint-scope@^5.0.0\",\"name\":\"eslint-scope\",\"escapedName\":\"eslint-scope\",\"rawSpec\":\"^5.0.0\",\"saveSpec\":null,\"fetchSpec\":\"^5.0.0\"},\"_requiredBy\":[\"/\",\"/eslint\"],\"_resolved\":\"https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz\",\"_shasum\":\"e87c8887c73e8d1ec84f1ca591645c358bfc8fb9\",\"_spec\":\"eslint-scope@^5.0.0\",\"_where\":\"/home/dcsapak/git/pve-eslint/eslint-v7.0.0\",\"bugs\":{\"url\":\"https://github.com/eslint/eslint-scope/issues\"},\"bundleDependencies\":false,\"dependencies\":{\"esrecurse\":\"^4.1.0\",\"estraverse\":\"^4.1.1\"},\"deprecated\":false,\"description\":\"ECMAScript scope analyzer for ESLint\",\"devDependencies\":{\"@typescript-eslint/parser\":\"^1.11.0\",\"chai\":\"^4.2.0\",\"eslint\":\"^6.0.1\",\"eslint-config-eslint\":\"^5.0.1\",\"eslint-plugin-node\":\"^9.1.0\",\"eslint-release\":\"^1.0.0\",\"espree\":\"^6.0.0\",\"istanbul\":\"^0.4.5\",\"mocha\":\"^6.1.4\",\"npm-license\":\"^0.3.3\",\"shelljs\":\"^0.8.3\",\"typescript\":\"^3.5.2\"},\"engines\":{\"node\":\">=8.0.0\"},\"files\":[\"LICENSE\",\"README.md\",\"lib\"],\"homepage\":\"http://github.com/eslint/eslint-scope\",\"license\":\"BSD-2-Clause\",\"main\":\"lib/index.js\",\"name\":\"eslint-scope\",\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/eslint/eslint-scope.git\"},\"scripts\":{\"generate-alpharelease\":\"eslint-generate-prerelease alpha\",\"generate-betarelease\":\"eslint-generate-prerelease beta\",\"generate-rcrelease\":\"eslint-generate-prerelease rc\",\"generate-release\":\"eslint-generate-release\",\"lint\":\"node Makefile.js lint\",\"publish-release\":\"eslint-publish-release\",\"test\":\"node Makefile.js test\"},\"version\":\"5.0.0\"}"); /***/ }), /* 392 */ @@ -20481,8 +20481,8 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); // code point above 128. // Generated by `bin/generate-identifier-regex.js`. - var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u08a0-\u08b4\u08b6-\u08bd\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fef\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7c6\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab67\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; - var nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08d3-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c04\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d00-\u0d03\u0d3b\u0d3c\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4\u1cf7-\u1cf9\u1dc0-\u1df9\u1dfb-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; + var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u08a0-\u08b4\u08b6-\u08c7\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d04-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31bf\u31f0-\u31ff\u3400-\u4dbf\u4e00-\u9ffc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7ca\ua7f5-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab69\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; + var nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08d3-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b55-\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c04\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d00-\u0d03\u0d3b\u0d3c\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d81-\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1abf\u1ac0\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4\u1cf7-\u1cf9\u1dc0-\u1df9\u1dfb-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua82c\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); nonASCIIidentifierStartChars = nonASCIIidentifierChars = null; // These are a run-length and offset encoded representation of the @@ -20492,9 +20492,9 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); // generated by bin/generate-identifier-regex.js // eslint-disable-next-line comma-spacing - var astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 14, 29, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 19, 35, 5, 35, 5, 39, 9, 51, 157, 310, 10, 21, 11, 7, 153, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 66, 18, 2, 1, 11, 21, 11, 25, 71, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 28, 43, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 56, 50, 14, 50, 14, 35, 477, 28, 11, 0, 9, 21, 155, 22, 13, 52, 76, 44, 33, 24, 27, 35, 30, 0, 12, 34, 4, 0, 13, 47, 15, 3, 22, 0, 2, 0, 36, 17, 2, 24, 85, 6, 2, 0, 2, 3, 2, 14, 2, 9, 8, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 19, 0, 13, 4, 159, 52, 19, 3, 21, 0, 33, 47, 21, 1, 2, 0, 185, 46, 42, 3, 37, 47, 21, 0, 60, 42, 14, 0, 72, 26, 230, 43, 117, 63, 32, 0, 161, 7, 3, 38, 17, 0, 2, 0, 29, 0, 11, 39, 8, 0, 22, 0, 12, 45, 20, 0, 35, 56, 264, 8, 2, 36, 18, 0, 50, 29, 113, 6, 2, 1, 2, 37, 22, 0, 26, 5, 2, 1, 2, 31, 15, 0, 328, 18, 270, 921, 103, 110, 18, 195, 2749, 1070, 4050, 582, 8634, 568, 8, 30, 114, 29, 19, 47, 17, 3, 32, 20, 6, 18, 689, 63, 129, 74, 6, 0, 67, 12, 65, 1, 2, 0, 29, 6135, 9, 754, 9486, 286, 50, 2, 18, 3, 9, 395, 2309, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 2357, 44, 11, 6, 17, 0, 370, 43, 1301, 196, 60, 67, 8, 0, 1205, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42710, 42, 4148, 12, 221, 3, 5761, 15, 7472, 3104, 541]; // eslint-disable-next-line comma-spacing + var astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 14, 29, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 19, 35, 5, 35, 5, 39, 9, 51, 157, 310, 10, 21, 11, 7, 153, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 66, 18, 2, 1, 11, 21, 11, 25, 71, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 28, 43, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 56, 50, 14, 50, 14, 35, 349, 41, 7, 1, 79, 28, 11, 0, 9, 21, 107, 20, 28, 22, 13, 52, 76, 44, 33, 24, 27, 35, 30, 0, 3, 0, 9, 34, 4, 0, 13, 47, 15, 3, 22, 0, 2, 0, 36, 17, 2, 24, 85, 6, 2, 0, 2, 3, 2, 14, 2, 9, 8, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 19, 0, 13, 4, 159, 52, 19, 3, 21, 2, 31, 47, 21, 1, 2, 0, 185, 46, 42, 3, 37, 47, 21, 0, 60, 42, 14, 0, 72, 26, 230, 43, 117, 63, 32, 7, 3, 0, 3, 7, 2, 1, 2, 23, 16, 0, 2, 0, 95, 7, 3, 38, 17, 0, 2, 0, 29, 0, 11, 39, 8, 0, 22, 0, 12, 45, 20, 0, 35, 56, 264, 8, 2, 36, 18, 0, 50, 29, 113, 6, 2, 1, 2, 37, 22, 0, 26, 5, 2, 1, 2, 31, 15, 0, 328, 18, 190, 0, 80, 921, 103, 110, 18, 195, 2749, 1070, 4050, 582, 8634, 568, 8, 30, 114, 29, 19, 47, 17, 3, 32, 20, 6, 18, 689, 63, 129, 74, 6, 0, 67, 12, 65, 1, 2, 0, 29, 6135, 9, 1237, 43, 8, 8952, 286, 50, 2, 18, 3, 9, 395, 2309, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 2357, 44, 11, 6, 17, 0, 370, 43, 1301, 196, 60, 67, 8, 0, 1205, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42717, 35, 4148, 12, 221, 3, 5761, 15, 7472, 3104, 541, 1507, 4938]; // eslint-disable-next-line comma-spacing - var astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 574, 3, 9, 9, 525, 10, 176, 2, 54, 14, 32, 9, 16, 3, 46, 10, 54, 9, 7, 2, 37, 13, 2, 9, 6, 1, 45, 0, 13, 2, 49, 13, 9, 3, 4, 9, 83, 11, 7, 0, 161, 11, 6, 9, 7, 3, 56, 1, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 193, 17, 10, 9, 5, 0, 82, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 84, 14, 5, 9, 243, 14, 166, 9, 232, 6, 3, 6, 4, 0, 29, 9, 41, 6, 2, 3, 9, 0, 10, 10, 47, 15, 406, 7, 2, 7, 17, 9, 57, 21, 2, 13, 123, 5, 4, 0, 2, 1, 2, 6, 2, 0, 9, 9, 49, 4, 2, 1, 2, 4, 9, 9, 330, 3, 19306, 9, 135, 4, 60, 6, 26, 9, 1014, 0, 2, 54, 8, 3, 19723, 1, 5319, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 513, 54, 5, 49, 9, 0, 15, 0, 23, 4, 2, 14, 1361, 6, 2, 16, 3, 6, 2, 1, 2, 4, 262, 6, 10, 9, 419, 13, 1495, 6, 110, 6, 6, 9, 792487, 239]; // This has a complexity linear to the value of the code. The + var astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 574, 3, 9, 9, 370, 1, 154, 10, 176, 2, 54, 14, 32, 9, 16, 3, 46, 10, 54, 9, 7, 2, 37, 13, 2, 9, 6, 1, 45, 0, 13, 2, 49, 13, 9, 3, 2, 11, 83, 11, 7, 0, 161, 11, 6, 9, 7, 3, 56, 1, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 193, 17, 10, 9, 5, 0, 82, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 84, 14, 5, 9, 243, 14, 166, 9, 71, 5, 2, 1, 3, 3, 2, 0, 2, 1, 13, 9, 120, 6, 3, 6, 4, 0, 29, 9, 41, 6, 2, 3, 9, 0, 10, 10, 47, 15, 406, 7, 2, 7, 17, 9, 57, 21, 2, 13, 123, 5, 4, 0, 2, 1, 2, 6, 2, 0, 9, 9, 49, 4, 2, 1, 2, 4, 9, 9, 330, 3, 19306, 9, 135, 4, 60, 6, 26, 9, 1014, 0, 2, 54, 8, 3, 82, 0, 12, 1, 19628, 1, 5319, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 513, 54, 5, 49, 9, 0, 15, 0, 23, 4, 2, 14, 1361, 6, 2, 16, 3, 6, 2, 1, 2, 4, 262, 6, 10, 9, 419, 13, 1495, 6, 110, 6, 6, 9, 4759, 9, 787719, 239]; // This has a complexity linear to the value of the code. The // assumption is that looking up astral identifier characters is // rare. @@ -20724,6 +20724,7 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); starstar: new TokenType("**", { beforeExpr: true }), + coalesce: binop("??", 1), // Keyword token types. _break: kw("break"), _case: kw("case", beforeExpr), @@ -21202,7 +21203,11 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); } if ((match[1] || match[2]) === "use strict") { - return true; + skipWhiteSpace.lastIndex = start + match[0].length; + var spaceAfter = skipWhiteSpace.exec(this.input), + end = spaceAfter.index + spaceAfter[0].length; + var next = this.input.charAt(end); + return next === ";" || next === "}" || lineBreak.test(spaceAfter[0]) && !(/[(`.[+\-/*%<>=,?^&]/.test(next) || next === "!" && this.input.charAt(end + 1) === "="); } start += match[0].length; // Skip semicolon, if any. @@ -21547,7 +21552,7 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); var next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next); - if (nextCh === 40) // '(' + if (nextCh === 40 || nextCh === 46) // '(' or '.' { return this.parseExpressionStatement(node, this.parseExpression()); } @@ -21951,7 +21956,7 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); // function bodies). - pp$1.parseBlock = function (createNewLexicalScope, node) { + pp$1.parseBlock = function (createNewLexicalScope, node, exitStrict) { if (createNewLexicalScope === void 0) createNewLexicalScope = true; if (node === void 0) node = this.startNode(); node.body = []; @@ -21961,11 +21966,17 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); this.enterScope(0); } - while (!this.eat(types.braceR)) { + while (this.type !== types.braceR) { var stmt = this.parseStatement(null); node.body.push(stmt); } + if (exitStrict) { + this.strict = false; + } + + this.next(); + if (createNewLexicalScope) { this.exitScope(); } @@ -22118,7 +22129,7 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); classBody.body = []; this.expect(types.braceL); - while (!this.eat(types.braceR)) { + while (this.type !== types.braceR) { var element = this.parseClassElement(node.superClass !== null); if (element) { @@ -22134,8 +22145,9 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); } } - node.body = this.finishNode(classBody, "ClassBody"); this.strict = oldStrict; + this.next(); + node.body = this.finishNode(classBody, "ClassBody"); return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression"); }; @@ -22261,6 +22273,15 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); this.next(); // export * from '...' if (this.eat(types.star)) { + if (this.options.ecmaVersion >= 11) { + if (this.eatContextual("as")) { + node.exported = this.parseIdent(true); + this.checkExport(exports, node.exported.name, this.lastTokStart); + } else { + node.exported = null; + } + } + this.expectContextual("from"); if (this.type !== types.string) { @@ -23034,12 +23055,25 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); if (prec != null && (!noIn || this.type !== types._in)) { if (prec > minPrec) { var logical = this.type === types.logicalOR || this.type === types.logicalAND; + var coalesce = this.type === types.coalesce; + + if (coalesce) { + // Handle the precedence of `tt.coalesce` as equal to the range of logical expressions. + // In other words, `node.right` shouldn't contain logical expressions in order to check the mixed error. + prec = types.logicalAND.binop; + } + var op = this.value; this.next(); var startPos = this.start, startLoc = this.startLoc; var right = this.parseExprOp(this.parseMaybeUnary(null, false), startPos, startLoc, prec, noIn); - var node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical); + var node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical || coalesce); + + if (logical && this.type === types.coalesce || coalesce && (this.type === types.logicalOR || this.type === types.logicalAND)) { + this.raiseRecoverable(this.start, "Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses"); + } + return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn); } } @@ -23133,7 +23167,7 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); }; pp$3.parseSubscripts = function (base, startPos, startLoc, noCalls) { - var maybeAsyncArrow = this.options.ecmaVersion >= 8 && base.type === "Identifier" && base.name === "async" && this.lastTokEnd === base.end && !this.canInsertSemicolon() && this.input.slice(base.start, base.end) === "async"; + var maybeAsyncArrow = this.options.ecmaVersion >= 8 && base.type === "Identifier" && base.name === "async" && this.lastTokEnd === base.end && !this.canInsertSemicolon() && base.end - base.start === 5 && this.potentialArrowAt === base.start; while (true) { var element = this.parseSubscript(base, startPos, startLoc, noCalls, maybeAsyncArrow); @@ -23350,13 +23384,23 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); }; pp$3.parseExprImport = function () { - var node = this.startNode(); - this.next(); // skip `import` + var node = this.startNode(); // Consume `import` as an identifier for `import.meta`. + // Because `this.parseIdent(true)` doesn't check escape sequences, it needs the check of `this.containsEsc`. + + if (this.containsEsc) { + this.raiseRecoverable(this.start, "Escape sequence in keyword import"); + } + + var meta = this.parseIdent(true); switch (this.type) { case types.parenL: return this.parseDynamicImport(node); + case types.dot: + node.meta = meta; + return this.parseImportMeta(node); + default: this.unexpected(); } @@ -23381,6 +23425,27 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); return this.finishNode(node, "ImportExpression"); }; + pp$3.parseImportMeta = function (node) { + this.next(); // skip `.` + + var containsEsc = this.containsEsc; + node.property = this.parseIdent(true); + + if (node.property.name !== "meta") { + this.raiseRecoverable(node.property.start, "The only valid meta property for import is 'import.meta'"); + } + + if (containsEsc) { + this.raiseRecoverable(node.start, "'import.meta' must not contain escaped characters"); + } + + if (this.options.sourceType !== "module") { + this.raiseRecoverable(node.start, "Cannot use 'import.meta' outside a module"); + } + + return this.finishNode(node, "MetaProperty"); + }; + pp$3.parseLiteral = function (value) { var node = this.startNode(); node.value = value; @@ -23513,12 +23578,16 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); var containsEsc = this.containsEsc; node.property = this.parseIdent(true); - if (node.property.name !== "target" || containsEsc) { - this.raiseRecoverable(node.property.start, "The only valid meta property for new is new.target"); + if (node.property.name !== "target") { + this.raiseRecoverable(node.property.start, "The only valid meta property for new is 'new.target'"); + } + + if (containsEsc) { + this.raiseRecoverable(node.start, "'new.target' must not contain escaped characters"); } if (!this.inNonArrowFunction()) { - this.raiseRecoverable(node.start, "new.target can only be used in functions"); + this.raiseRecoverable(node.start, "'new.target' can only be used in functions"); } return this.finishNode(node, "MetaProperty"); @@ -23886,20 +23955,19 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); // if a let/const declaration in the function clashes with one of the params. - this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && !isMethod && this.isSimpleParamList(node.params)); - node.body = this.parseBlock(false); + this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && !isMethod && this.isSimpleParamList(node.params)); // Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval' + + if (this.strict && node.id) { + this.checkLVal(node.id, BIND_OUTSIDE); + } + + node.body = this.parseBlock(false, undefined, useStrict && !oldStrict); node.expression = false; this.adaptDirectivePrologue(node.body.body); this.labels = oldLabels; } - this.exitScope(); // Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval' - - if (this.strict && node.id) { - this.checkLVal(node.id, BIND_OUTSIDE); - } - - this.strict = oldStrict; + this.exitScope(); }; pp$3.isSimpleParamList = function (params) { @@ -24497,7 +24565,8 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); // Otherwise, this returns the code unit of the index (can be a part of a surrogate pair). - RegExpValidationState.prototype.at = function at(i) { + RegExpValidationState.prototype.at = function at(i, forceU) { + if (forceU === void 0) forceU = false; var s = this.source; var l = s.length; @@ -24507,7 +24576,7 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); var c = s.charCodeAt(i); - if (!this.switchU || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l) { + if (!(forceU || this.switchU) || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l) { return c; } @@ -24515,7 +24584,8 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); return next >= 0xDC00 && next <= 0xDFFF ? (c << 10) + next - 0x35FDC00 : c; }; - RegExpValidationState.prototype.nextIndex = function nextIndex(i) { + RegExpValidationState.prototype.nextIndex = function nextIndex(i, forceU) { + if (forceU === void 0) forceU = false; var s = this.source; var l = s.length; @@ -24526,28 +24596,33 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); var c = s.charCodeAt(i), next; - if (!this.switchU || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l || (next = s.charCodeAt(i + 1)) < 0xDC00 || next > 0xDFFF) { + if (!(forceU || this.switchU) || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l || (next = s.charCodeAt(i + 1)) < 0xDC00 || next > 0xDFFF) { return i + 1; } return i + 2; }; - RegExpValidationState.prototype.current = function current() { - return this.at(this.pos); + RegExpValidationState.prototype.current = function current(forceU) { + if (forceU === void 0) forceU = false; + return this.at(this.pos, forceU); }; - RegExpValidationState.prototype.lookahead = function lookahead() { - return this.at(this.nextIndex(this.pos)); + RegExpValidationState.prototype.lookahead = function lookahead(forceU) { + if (forceU === void 0) forceU = false; + return this.at(this.nextIndex(this.pos, forceU), forceU); }; - RegExpValidationState.prototype.advance = function advance() { - this.pos = this.nextIndex(this.pos); + RegExpValidationState.prototype.advance = function advance(forceU) { + if (forceU === void 0) forceU = false; + this.pos = this.nextIndex(this.pos, forceU); }; - RegExpValidationState.prototype.eat = function eat(ch) { - if (this.current() === ch) { - this.advance(); + RegExpValidationState.prototype.eat = function eat(ch, forceU) { + if (forceU === void 0) forceU = false; + + if (this.current(forceU) === ch) { + this.advance(forceU); return true; } @@ -24997,9 +25072,9 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); } return false; - }; // GroupSpecifier[U] :: + }; // GroupSpecifier :: // [empty] - // `?` GroupName[?U] + // `?` GroupName pp$8.regexp_groupSpecifier = function (state) { @@ -25017,8 +25092,8 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); state.raise("Invalid group"); } - }; // GroupName[U] :: - // `<` RegExpIdentifierName[?U] `>` + }; // GroupName :: + // `<` RegExpIdentifierName `>` // Note: this updates `state.lastStringValue` property with the eaten name. @@ -25038,9 +25113,9 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); } return false; - }; // RegExpIdentifierName[U] :: - // RegExpIdentifierStart[?U] - // RegExpIdentifierName[?U] RegExpIdentifierPart[?U] + }; // RegExpIdentifierName :: + // RegExpIdentifierStart + // RegExpIdentifierName RegExpIdentifierPart // Note: this updates `state.lastStringValue` property with the eaten name. @@ -25058,21 +25133,22 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); } return false; - }; // RegExpIdentifierStart[U] :: + }; // RegExpIdentifierStart :: // UnicodeIDStart // `$` // `_` - // `\` RegExpUnicodeEscapeSequence[?U] + // `\` RegExpUnicodeEscapeSequence[+U] pp$8.regexp_eatRegExpIdentifierStart = function (state) { var start = state.pos; - var ch = state.current(); - state.advance(); + var forceU = this.options.ecmaVersion >= 11; + var ch = state.current(forceU); + state.advance(forceU); if (ch === 0x5C /* \ */ - && this.regexp_eatRegExpUnicodeEscapeSequence(state)) { + && this.regexp_eatRegExpUnicodeEscapeSequence(state, forceU)) { ch = state.lastIntValue; } @@ -25090,23 +25166,24 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); /* $ */ || ch === 0x5F; /* _ */ - } // RegExpIdentifierPart[U] :: + } // RegExpIdentifierPart :: // UnicodeIDContinue // `$` // `_` - // `\` RegExpUnicodeEscapeSequence[?U] + // `\` RegExpUnicodeEscapeSequence[+U] // // pp$8.regexp_eatRegExpIdentifierPart = function (state) { var start = state.pos; - var ch = state.current(); - state.advance(); + var forceU = this.options.ecmaVersion >= 11; + var ch = state.current(forceU); + state.advance(forceU); if (ch === 0x5C /* \ */ - && this.regexp_eatRegExpUnicodeEscapeSequence(state)) { + && this.regexp_eatRegExpUnicodeEscapeSequence(state, forceU)) { ch = state.lastIntValue; } @@ -25192,7 +25269,7 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); pp$8.regexp_eatCharacterEscape = function (state) { - return this.regexp_eatControlEscape(state) || this.regexp_eatCControlLetter(state) || this.regexp_eatZero(state) || this.regexp_eatHexEscapeSequence(state) || this.regexp_eatRegExpUnicodeEscapeSequence(state) || !state.switchU && this.regexp_eatLegacyOctalEscapeSequence(state) || this.regexp_eatIdentityEscape(state); + return this.regexp_eatControlEscape(state) || this.regexp_eatCControlLetter(state) || this.regexp_eatZero(state) || this.regexp_eatHexEscapeSequence(state) || this.regexp_eatRegExpUnicodeEscapeSequence(state, false) || !state.switchU && this.regexp_eatLegacyOctalEscapeSequence(state) || this.regexp_eatIdentityEscape(state); }; pp$8.regexp_eatCControlLetter = function (state) { @@ -25306,8 +25383,10 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); } // https://www.ecma-international.org/ecma-262/8.0/#prod-RegExpUnicodeEscapeSequence - pp$8.regexp_eatRegExpUnicodeEscapeSequence = function (state) { + pp$8.regexp_eatRegExpUnicodeEscapeSequence = function (state, forceU) { + if (forceU === void 0) forceU = false; var start = state.pos; + var switchU = forceU || state.switchU; if (state.eat(0x75 /* u */ @@ -25315,7 +25394,7 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); if (this.regexp_eatFixedHexDigits(state, 4)) { var lead = state.lastIntValue; - if (state.switchU && lead >= 0xD800 && lead <= 0xDBFF) { + if (switchU && lead >= 0xD800 && lead <= 0xDBFF) { var leadSurrogateEnd = state.pos; if (state.eat(0x5C @@ -25338,7 +25417,7 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); return true; } - if (state.switchU && state.eat(0x7B + if (switchU && state.eat(0x7B /* { */ ) && this.regexp_eatHexDigits(state) && state.eat(0x7D /* } */ @@ -25346,7 +25425,7 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); return true; } - if (state.switchU) { + if (switchU) { state.raise("Invalid unicode escape"); } @@ -26247,6 +26326,19 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); return this.finishOp(code === 61 ? types.eq : types.prefix, 1); }; + pp$9.readToken_question = function () { + // '?' + if (this.options.ecmaVersion >= 11) { + var next = this.input.charCodeAt(this.pos + 1); + + if (next === 63) { + return this.finishOp(types.coalesce, 2); + } + } + + return this.finishOp(types.question, 1); + }; + pp$9.getTokenFromCode = function (code) { switch (code) { // The interpretation of a dot depends on whether it is followed @@ -26292,10 +26384,6 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); ++this.pos; return this.finishToken(types.colon); - case 63: - ++this.pos; - return this.finishToken(types.question); - case 96: // '`' if (this.options.ecmaVersion < 6) { @@ -26384,6 +26472,10 @@ exports.supportedEcmaVersions = getSupportedEcmaVersions(); // '=!' return this.readToken_eq_excl(code); + case 63: + // '?' + return this.readToken_question(); + case 126: // '~' return this.finishOp(types.prefix, 1); @@ -28500,7 +28592,7 @@ module.exports = { /* 402 */ /***/ (function(module) { -module.exports = JSON.parse("{\"_from\":\"espree@^6.2.1\",\"_id\":\"espree@6.2.1\",\"_inBundle\":false,\"_integrity\":\"sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==\",\"_location\":\"/espree\",\"_phantomChildren\":{},\"_requested\":{\"type\":\"range\",\"registry\":true,\"raw\":\"espree@^6.2.1\",\"name\":\"espree\",\"escapedName\":\"espree\",\"rawSpec\":\"^6.2.1\",\"saveSpec\":null,\"fetchSpec\":\"^6.2.1\"},\"_requiredBy\":[\"/\",\"/eslint\"],\"_resolved\":\"https://registry.npmjs.org/espree/-/espree-6.2.1.tgz\",\"_shasum\":\"77fc72e1fd744a2052c20f38a5b575832e82734a\",\"_spec\":\"espree@^6.2.1\",\"_where\":\"/home/dcsapak/git/pve-eslint/eslint-v7.0.0-alpha.3\",\"author\":{\"name\":\"Nicholas C. Zakas\",\"email\":\"nicholas+npm@nczconsulting.com\"},\"bugs\":{\"url\":\"http://github.com/eslint/espree.git\"},\"bundleDependencies\":false,\"dependencies\":{\"acorn\":\"^7.1.1\",\"acorn-jsx\":\"^5.2.0\",\"eslint-visitor-keys\":\"^1.1.0\"},\"deprecated\":false,\"description\":\"An Esprima-compatible JavaScript parser built on Acorn\",\"devDependencies\":{\"browserify\":\"^16.5.0\",\"chai\":\"^4.2.0\",\"eslint\":\"^6.0.1\",\"eslint-config-eslint\":\"^5.0.1\",\"eslint-plugin-node\":\"^9.1.0\",\"eslint-release\":\"^1.0.0\",\"esprima\":\"latest\",\"esprima-fb\":\"^8001.2001.0-dev-harmony-fb\",\"json-diff\":\"^0.5.4\",\"leche\":\"^2.3.0\",\"mocha\":\"^6.2.0\",\"nyc\":\"^14.1.1\",\"regenerate\":\"^1.4.0\",\"shelljs\":\"^0.3.0\",\"shelljs-nodecli\":\"^0.1.1\",\"unicode-6.3.0\":\"^0.7.5\"},\"engines\":{\"node\":\">=6.0.0\"},\"files\":[\"lib\",\"espree.js\"],\"homepage\":\"https://github.com/eslint/espree\",\"keywords\":[\"ast\",\"ecmascript\",\"javascript\",\"parser\",\"syntax\",\"acorn\"],\"license\":\"BSD-2-Clause\",\"main\":\"espree.js\",\"name\":\"espree\",\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/eslint/espree.git\"},\"scripts\":{\"browserify\":\"node Makefile.js browserify\",\"generate-alpharelease\":\"eslint-generate-prerelease alpha\",\"generate-betarelease\":\"eslint-generate-prerelease beta\",\"generate-rcrelease\":\"eslint-generate-prerelease rc\",\"generate-regex\":\"node tools/generate-identifier-regex.js\",\"generate-release\":\"eslint-generate-release\",\"lint\":\"node Makefile.js lint\",\"publish-release\":\"eslint-publish-release\",\"test\":\"npm run-script lint && node Makefile.js test\"},\"version\":\"6.2.1\"}"); +module.exports = JSON.parse("{\"_from\":\"espree@^7.0.0\",\"_id\":\"espree@7.0.0\",\"_inBundle\":false,\"_integrity\":\"sha512-/r2XEx5Mw4pgKdyb7GNLQNsu++asx/dltf/CI8RFi9oGHxmQFgvLbc5Op4U6i8Oaj+kdslhJtVlEZeAqH5qOTw==\",\"_location\":\"/espree\",\"_phantomChildren\":{},\"_requested\":{\"type\":\"range\",\"registry\":true,\"raw\":\"espree@^7.0.0\",\"name\":\"espree\",\"escapedName\":\"espree\",\"rawSpec\":\"^7.0.0\",\"saveSpec\":null,\"fetchSpec\":\"^7.0.0\"},\"_requiredBy\":[\"/\",\"/eslint\"],\"_resolved\":\"https://registry.npmjs.org/espree/-/espree-7.0.0.tgz\",\"_shasum\":\"8a7a60f218e69f120a842dc24c5a88aa7748a74e\",\"_spec\":\"espree@^7.0.0\",\"_where\":\"/home/dcsapak/git/pve-eslint/eslint-v7.0.0\",\"author\":{\"name\":\"Nicholas C. Zakas\",\"email\":\"nicholas+npm@nczconsulting.com\"},\"bugs\":{\"url\":\"http://github.com/eslint/espree.git\"},\"bundleDependencies\":false,\"dependencies\":{\"acorn\":\"^7.1.1\",\"acorn-jsx\":\"^5.2.0\",\"eslint-visitor-keys\":\"^1.1.0\"},\"deprecated\":false,\"description\":\"An Esprima-compatible JavaScript parser built on Acorn\",\"devDependencies\":{\"browserify\":\"^16.5.0\",\"chai\":\"^4.2.0\",\"eslint\":\"^6.0.1\",\"eslint-config-eslint\":\"^5.0.1\",\"eslint-plugin-node\":\"^9.1.0\",\"eslint-release\":\"^1.0.0\",\"esprima\":\"latest\",\"esprima-fb\":\"^8001.2001.0-dev-harmony-fb\",\"json-diff\":\"^0.5.4\",\"leche\":\"^2.3.0\",\"mocha\":\"^6.2.0\",\"nyc\":\"^14.1.1\",\"regenerate\":\"^1.4.0\",\"shelljs\":\"^0.3.0\",\"shelljs-nodecli\":\"^0.1.1\",\"unicode-6.3.0\":\"^0.7.5\"},\"engines\":{\"node\":\"^10.12.0 || >=12.0.0\"},\"files\":[\"lib\",\"espree.js\"],\"homepage\":\"https://github.com/eslint/espree\",\"keywords\":[\"ast\",\"ecmascript\",\"javascript\",\"parser\",\"syntax\",\"acorn\"],\"license\":\"BSD-2-Clause\",\"main\":\"espree.js\",\"name\":\"espree\",\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/eslint/espree.git\"},\"scripts\":{\"browserify\":\"node Makefile.js browserify\",\"generate-alpharelease\":\"eslint-generate-prerelease alpha\",\"generate-betarelease\":\"eslint-generate-prerelease beta\",\"generate-rcrelease\":\"eslint-generate-prerelease rc\",\"generate-regex\":\"node tools/generate-identifier-regex.js\",\"generate-release\":\"eslint-generate-release\",\"lint\":\"node Makefile.js lint\",\"publish-release\":\"eslint-publish-release\",\"sync-docs\":\"node Makefile.js docs\",\"test\":\"npm run-script lint && node Makefile.js test\"},\"version\":\"7.0.0\"}"); /***/ }), /* 403 */ @@ -45720,13 +45812,13 @@ module.exports = new Map(Object.entries({ } }, es2017: { - globals: _objectSpread({}, newGlobals2015, {}, newGlobals2017), + globals: _objectSpread(_objectSpread({}, newGlobals2015), newGlobals2017), parserOptions: { ecmaVersion: 8 } }, es2020: { - globals: _objectSpread({}, newGlobals2015, {}, newGlobals2017, {}, newGlobals2020), + globals: _objectSpread(_objectSpread(_objectSpread({}, newGlobals2015), newGlobals2017), newGlobals2020), parserOptions: { ecmaVersion: 11 } @@ -45836,7 +45928,7 @@ module.exports = JSON.parse("{\"builtin\":{\"Array\":false,\"ArrayBuffer\":false /* 408 */ /***/ (function(module) { -module.exports = JSON.parse("{\"name\":\"eslint\",\"version\":\"7.0.0-alpha.3\",\"author\":\"Nicholas C. Zakas \",\"description\":\"An AST-based pattern checker for JavaScript.\",\"bin\":{\"eslint\":\"./bin/eslint.js\"},\"main\":\"./lib/api.js\",\"scripts\":{\"test\":\"node Makefile.js test\",\"test:cli\":\"mocha\",\"lint\":\"node Makefile.js lint\",\"fix\":\"node Makefile.js lint -- fix\",\"fuzz\":\"node Makefile.js fuzz\",\"generate-release\":\"node Makefile.js generateRelease\",\"generate-alpharelease\":\"node Makefile.js generatePrerelease -- alpha\",\"generate-betarelease\":\"node Makefile.js generatePrerelease -- beta\",\"generate-rcrelease\":\"node Makefile.js generatePrerelease -- rc\",\"publish-release\":\"node Makefile.js publishRelease\",\"docs\":\"node Makefile.js docs\",\"gensite\":\"node Makefile.js gensite\",\"webpack\":\"node Makefile.js webpack\",\"perf\":\"node Makefile.js perf\"},\"gitHooks\":{\"pre-commit\":\"lint-staged\"},\"lint-staged\":{\"*.js\":[\"eslint --fix\",\"git add\"],\"*.md\":\"markdownlint\"},\"files\":[\"LICENSE\",\"README.md\",\"bin\",\"conf\",\"lib\",\"messages\"],\"repository\":\"eslint/eslint\",\"funding\":\"https://opencollective.com/eslint\",\"homepage\":\"https://eslint.org\",\"bugs\":\"https://github.com/eslint/eslint/issues/\",\"dependencies\":{\"@babel/code-frame\":\"^7.0.0\",\"ajv\":\"^6.10.0\",\"chalk\":\"^3.0.0\",\"cross-spawn\":\"^7.0.1\",\"debug\":\"^4.0.1\",\"doctrine\":\"^3.0.0\",\"eslint-scope\":\"^5.0.0\",\"eslint-utils\":\"^2.0.0\",\"eslint-visitor-keys\":\"^1.1.0\",\"espree\":\"^6.2.1\",\"esquery\":\"^1.2.0\",\"esutils\":\"^2.0.2\",\"file-entry-cache\":\"^5.0.1\",\"functional-red-black-tree\":\"^1.0.1\",\"glob-parent\":\"^5.0.0\",\"globals\":\"^12.1.0\",\"ignore\":\"^4.0.6\",\"import-fresh\":\"^3.0.0\",\"imurmurhash\":\"^0.1.4\",\"inquirer\":\"^7.0.0\",\"is-glob\":\"^4.0.0\",\"js-yaml\":\"^3.13.1\",\"json-stable-stringify-without-jsonify\":\"^1.0.1\",\"levn\":\"^0.3.0\",\"lodash\":\"^4.17.14\",\"minimatch\":\"^3.0.4\",\"natural-compare\":\"^1.4.0\",\"optionator\":\"^0.8.3\",\"progress\":\"^2.0.0\",\"regexpp\":\"^3.0.0\",\"semver\":\"^7.1.1\",\"strip-ansi\":\"^6.0.0\",\"strip-json-comments\":\"^3.0.1\",\"table\":\"^5.2.3\",\"text-table\":\"^0.2.0\",\"v8-compile-cache\":\"^2.0.3\"},\"devDependencies\":{\"@babel/core\":\"^7.4.3\",\"@babel/preset-env\":\"^7.4.3\",\"acorn\":\"^7.1.1\",\"babel-loader\":\"^8.0.5\",\"chai\":\"^4.0.1\",\"cheerio\":\"^0.22.0\",\"common-tags\":\"^1.8.0\",\"core-js\":\"^3.1.3\",\"dateformat\":\"^3.0.3\",\"ejs\":\"^2.6.1\",\"eslint\":\"file:.\",\"eslint-config-eslint\":\"file:packages/eslint-config-eslint\",\"eslint-plugin-eslint-plugin\":\"^2.2.1\",\"eslint-plugin-internal-rules\":\"file:tools/internal-rules\",\"eslint-plugin-jsdoc\":\"^15.9.5\",\"eslint-plugin-node\":\"^9.0.0\",\"eslint-release\":\"^1.2.0\",\"eslump\":\"^2.0.0\",\"esprima\":\"^4.0.1\",\"glob\":\"^7.1.3\",\"jsdoc\":\"^3.5.5\",\"karma\":\"^4.0.1\",\"karma-chrome-launcher\":\"^2.2.0\",\"karma-mocha\":\"^1.3.0\",\"karma-mocha-reporter\":\"^2.2.3\",\"karma-webpack\":\"^4.0.0-rc.6\",\"leche\":\"^2.2.3\",\"lint-staged\":\"^8.1.5\",\"load-perf\":\"^0.2.0\",\"markdownlint\":\"^0.15.0\",\"markdownlint-cli\":\"^0.17.0\",\"memfs\":\"^3.0.1\",\"mocha\":\"^6.1.2\",\"mocha-junit-reporter\":\"^1.23.0\",\"npm-license\":\"^0.3.3\",\"nyc\":\"^14.1.1\",\"proxyquire\":\"^2.0.1\",\"puppeteer\":\"^1.18.0\",\"recast\":\"^0.18.1\",\"regenerator-runtime\":\"^0.13.2\",\"shelljs\":\"^0.8.2\",\"sinon\":\"^7.3.2\",\"temp\":\"^0.9.0\",\"webpack\":\"^4.35.0\",\"webpack-cli\":\"^3.3.5\",\"yorkie\":\"^2.0.0\"},\"keywords\":[\"ast\",\"lint\",\"javascript\",\"ecmascript\",\"espree\"],\"license\":\"MIT\",\"engines\":{\"node\":\"^10.12.0 || >=12.0.0\"}}"); +module.exports = JSON.parse("{\"name\":\"eslint\",\"version\":\"7.0.0\",\"author\":\"Nicholas C. Zakas \",\"description\":\"An AST-based pattern checker for JavaScript.\",\"bin\":{\"eslint\":\"./bin/eslint.js\"},\"main\":\"./lib/api.js\",\"scripts\":{\"test\":\"node Makefile.js test\",\"test:cli\":\"mocha\",\"lint\":\"node Makefile.js lint\",\"fix\":\"node Makefile.js lint -- fix\",\"fuzz\":\"node Makefile.js fuzz\",\"generate-release\":\"node Makefile.js generateRelease\",\"generate-alpharelease\":\"node Makefile.js generatePrerelease -- alpha\",\"generate-betarelease\":\"node Makefile.js generatePrerelease -- beta\",\"generate-rcrelease\":\"node Makefile.js generatePrerelease -- rc\",\"publish-release\":\"node Makefile.js publishRelease\",\"docs\":\"node Makefile.js docs\",\"gensite\":\"node Makefile.js gensite\",\"webpack\":\"node Makefile.js webpack\",\"perf\":\"node Makefile.js perf\"},\"gitHooks\":{\"pre-commit\":\"lint-staged\"},\"lint-staged\":{\"*.js\":[\"eslint --fix\",\"git add\"],\"*.md\":\"markdownlint\"},\"files\":[\"LICENSE\",\"README.md\",\"bin\",\"conf\",\"lib\",\"messages\"],\"repository\":\"eslint/eslint\",\"funding\":\"https://opencollective.com/eslint\",\"homepage\":\"https://eslint.org\",\"bugs\":\"https://github.com/eslint/eslint/issues/\",\"dependencies\":{\"@babel/code-frame\":\"^7.0.0\",\"ajv\":\"^6.10.0\",\"chalk\":\"^4.0.0\",\"cross-spawn\":\"^7.0.2\",\"debug\":\"^4.0.1\",\"doctrine\":\"^3.0.0\",\"eslint-scope\":\"^5.0.0\",\"eslint-utils\":\"^2.0.0\",\"eslint-visitor-keys\":\"^1.1.0\",\"espree\":\"^7.0.0\",\"esquery\":\"^1.2.0\",\"esutils\":\"^2.0.2\",\"file-entry-cache\":\"^5.0.1\",\"functional-red-black-tree\":\"^1.0.1\",\"glob-parent\":\"^5.0.0\",\"globals\":\"^12.1.0\",\"ignore\":\"^4.0.6\",\"import-fresh\":\"^3.0.0\",\"imurmurhash\":\"^0.1.4\",\"inquirer\":\"^7.0.0\",\"is-glob\":\"^4.0.0\",\"js-yaml\":\"^3.13.1\",\"json-stable-stringify-without-jsonify\":\"^1.0.1\",\"levn\":\"^0.4.1\",\"lodash\":\"^4.17.14\",\"minimatch\":\"^3.0.4\",\"natural-compare\":\"^1.4.0\",\"optionator\":\"^0.9.1\",\"progress\":\"^2.0.0\",\"regexpp\":\"^3.1.0\",\"semver\":\"^7.2.1\",\"strip-ansi\":\"^6.0.0\",\"strip-json-comments\":\"^3.1.0\",\"table\":\"^5.2.3\",\"text-table\":\"^0.2.0\",\"v8-compile-cache\":\"^2.0.3\"},\"devDependencies\":{\"@babel/core\":\"^7.4.3\",\"@babel/preset-env\":\"^7.4.3\",\"acorn\":\"^7.1.1\",\"babel-loader\":\"^8.0.5\",\"chai\":\"^4.0.1\",\"cheerio\":\"^0.22.0\",\"common-tags\":\"^1.8.0\",\"core-js\":\"^3.1.3\",\"dateformat\":\"^3.0.3\",\"ejs\":\"^3.0.2\",\"escape-string-regexp\":\"^3.0.0\",\"eslint\":\"file:.\",\"eslint-config-eslint\":\"file:packages/eslint-config-eslint\",\"eslint-plugin-eslint-plugin\":\"^2.2.1\",\"eslint-plugin-internal-rules\":\"file:tools/internal-rules\",\"eslint-plugin-jsdoc\":\"^22.1.0\",\"eslint-plugin-node\":\"^11.1.0\",\"eslint-release\":\"^2.0.0\",\"eslump\":\"^2.0.0\",\"esprima\":\"^4.0.1\",\"glob\":\"^7.1.6\",\"jsdoc\":\"^3.5.5\",\"karma\":\"^4.0.1\",\"karma-chrome-launcher\":\"^3.1.0\",\"karma-mocha\":\"^1.3.0\",\"karma-mocha-reporter\":\"^2.2.3\",\"karma-webpack\":\"^4.0.0-rc.6\",\"leche\":\"^2.2.3\",\"lint-staged\":\"^10.1.2\",\"load-perf\":\"^0.2.0\",\"markdownlint\":\"^0.19.0\",\"markdownlint-cli\":\"^0.22.0\",\"memfs\":\"^3.0.1\",\"mocha\":\"^7.1.1\",\"mocha-junit-reporter\":\"^1.23.0\",\"npm-license\":\"^0.3.3\",\"nyc\":\"^15.0.1\",\"proxyquire\":\"^2.0.1\",\"puppeteer\":\"^2.1.1\",\"recast\":\"^0.19.0\",\"regenerator-runtime\":\"^0.13.2\",\"shelljs\":\"^0.8.2\",\"sinon\":\"^9.0.1\",\"temp\":\"^0.9.0\",\"webpack\":\"^4.35.0\",\"webpack-cli\":\"^3.3.5\",\"yorkie\":\"^2.0.0\"},\"keywords\":[\"ast\",\"lint\",\"javascript\",\"ecmascript\",\"espree\"],\"license\":\"MIT\",\"engines\":{\"node\":\"^10.12.0 || >=12.0.0\"}}"); /***/ }), /* 409 */ @@ -50735,24 +50827,6 @@ const TARGET_METHODS = /^(?:every|filter|find(?:Index)?|flatMap|forEach|map|redu function isReachable(segment) { return segment.reachable; } -/** - * Gets a readable location. - * - * - FunctionExpression -> the function name or `function` keyword. - * - ArrowFunctionExpression -> `=>` token. - * @param {ASTNode} node A function node to get. - * @param {SourceCode} sourceCode A source code to get tokens. - * @returns {ASTNode|Token} The node or the token of a location. - */ - - -function getLocation(node, sourceCode) { - if (node.type === "ArrowFunctionExpression") { - return sourceCode.getTokenBefore(node.body); - } - - return node.id || node; -} /** * Checks a given node is a MemberExpression node which has the specified name's * property. @@ -50883,6 +50957,7 @@ module.exports = { allowImplicit: false, checkForEach: false }; + const sourceCode = context.getSourceCode(); let funcInfo = { arrayMethodName: null, upper: null, @@ -50919,11 +50994,11 @@ module.exports = { } if (messageId) { - let name = astUtils.getFunctionNameWithKind(funcInfo.node); + let name = astUtils.getFunctionNameWithKind(node); name = messageId === "expectedNoReturnValue" ? lodash.upperFirst(name) : name; context.report({ node, - loc: getLocation(node, context.getSourceCode()).loc.start, + loc: astUtils.getFunctionHeadLoc(node, sourceCode), messageId, data: { name @@ -52332,6 +52407,8 @@ module.exports = { module.exports = { meta: { + deprecated: true, + replacedBy: ["node/callback-return"], type: "suggestion", docs: { description: "require `return` statements after callbacks", @@ -53861,26 +53938,21 @@ module.exports = { context.report({ node: reportItem, - loc: { - line: commaToken.loc.end.line, - column: commaToken.loc.start.column - }, + loc: commaToken.loc, messageId: "unexpectedLineBeforeAndAfterComma", fix: getFixerFunction(styleType, previousItemToken, commaToken, currentItemToken) }); } else if (style === "first" && !astUtils.isTokenOnSameLine(commaToken, currentItemToken)) { context.report({ node: reportItem, + loc: commaToken.loc, messageId: "expectedCommaFirst", fix: getFixerFunction(style, previousItemToken, commaToken, currentItemToken) }); } else if (style === "last" && astUtils.isTokenOnSameLine(commaToken, currentItemToken)) { context.report({ node: reportItem, - loc: { - line: commaToken.loc.end.line, - column: commaToken.loc.end.column - }, + loc: commaToken.loc, messageId: "expectedCommaLast", fix: getFixerFunction(style, previousItemToken, commaToken, currentItemToken) }); @@ -56521,7 +56593,8 @@ module.exports = { }] }, messages: { - unexpected: "Unexpected newline between function name and paren.", + unexpectedWhitespace: "Unexpected whitespace between function name and paren.", + unexpectedNewline: "Unexpected newline between function name and paren.", missing: "Missing space between function name and paren." } }, @@ -56572,7 +56645,7 @@ module.exports = { context.report({ node, loc: leftToken.loc.start, - messageId: "unexpected", + messageId: "unexpectedWhitespace", fix(fixer) { /* @@ -56602,7 +56675,7 @@ module.exports = { context.report({ node, loc: leftToken.loc.start, - messageId: "unexpected", + messageId: "unexpectedNewline", fix(fixer) { return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " "); @@ -57803,18 +57876,6 @@ const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/u; function isReachable(segment) { return segment.reachable; -} -/** - * Gets a readable location. - * - * - FunctionExpression -> the function name or `function` keyword. - * @param {ASTNode} node A function node to get. - * @returns {ASTNode|Token} The node or the token of a location. - */ - - -function getId(node) { - return node.id || node; } //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -57850,6 +57911,7 @@ module.exports = { const options = context.options[0] || { allowImplicit: false }; + const sourceCode = context.getSourceCode(); let funcInfo = { upper: null, codePath: null, @@ -57871,7 +57933,7 @@ module.exports = { if (funcInfo.shouldCheck && funcInfo.codePath.currentSegments.some(isReachable)) { context.report({ node, - loc: getId(node).loc.start, + loc: astUtils.getFunctionHeadLoc(node, sourceCode), messageId: funcInfo.hasReturn ? "expectedAlways" : "expected", data: { name: astUtils.getFunctionNameWithKind(funcInfo.node) @@ -57996,6 +58058,8 @@ function isShadowed(scope, node) { module.exports = { meta: { + deprecated: true, + replacedBy: ["node/global-require"], type: "suggestion", docs: { description: "require `require()` calls to be placed at top-level module scope", @@ -58344,6 +58408,8 @@ module.exports = { module.exports = { meta: { + deprecated: true, + replacedBy: ["node/handle-callback-err"], type: "suggestion", docs: { description: "require error handling in callbacks", @@ -63010,7 +63076,7 @@ function isSingleLine(node) { /** * Checks whether the properties on a single line. * @param {ASTNode[]} properties List of Property AST nodes. - * @returns {boolean} True if all properies is on a single line. + * @returns {boolean} True if all properties is on a single line. */ @@ -67469,7 +67535,7 @@ module.exports = { context.report({ node, - loc: callee.loc.start, + loc: callee.loc, messageId }); } //-------------------------------------------------------------------------- @@ -68163,16 +68229,19 @@ module.exports = { } if (depth > ignoreChainWithDepth && astUtils.isTokenOnSameLine(callee.object, callee.property)) { + const firstTokenAfterObject = sourceCode.getTokenAfter(callee.object, astUtils.isNotClosingParenToken); context.report({ node: callee.property, - loc: callee.property.loc.start, + loc: { + start: firstTokenAfterObject.loc.start, + end: callee.loc.end + }, messageId: "expected", data: { callee: getPropertyText(callee) }, fix(fixer) { - const firstTokenAfterObject = sourceCode.getTokenAfter(callee.object, astUtils.isNotClosingParenToken); return fixer.insertTextBefore(firstTokenAfterObject, "\n"); } @@ -68654,6 +68723,8 @@ module.exports = { module.exports = { meta: { + deprecated: true, + replacedBy: ["node/no-deprecated-api"], type: "problem", docs: { description: "disallow use of the `Buffer()` constructor", @@ -69896,8 +69967,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "visitRegExpAST", function() { return visitRegExpAST; }); /*! @author Toru Nagashima */ var ast = /*#__PURE__*/Object.freeze({}); -let largeIdStartPattern = null; -let largeIdContinuePattern = null; +let largeIdStartRanges = undefined; +let largeIdContinueRanges = undefined; function isIdStart(cp) { if (cp < 0x41) return false; @@ -69919,111 +69990,93 @@ function isIdContinue(cp) { } function isLargeIdStart(cp) { - if (!largeIdStartPattern) { - largeIdStartPattern = new RegExp("^[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1" + "\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377" + "\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1" + "\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559" + "\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a" + "\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef" + "\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1" + "\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824" + "\u0828\u0840-\u0858\u0860-\u086a\u08a0-\u08b4\u08b6-\u08bd" + "\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980" + "\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2" + "\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1" + "\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28" + "\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39" + "\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91" + "\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd" + "\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10" + "\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d" + "\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a" + "\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f" + "\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c" + "\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a" + "\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8" + "\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1" + "\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d" + "\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96" + "\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30" + "\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a" + "\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd" + "\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47" + "\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055" + "\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081" + "\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248" + "\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288" + "\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0" + "\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315" + "\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd" + "\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea" + "\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731" + "\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7" + "\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5" + "\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab" + "\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33" + "\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5" + "\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88" + "\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3" + "\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d" + "\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d" + "\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4" + "\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec" + "\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102" + "\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128" + "\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188" + "\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee" + "\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f" + "\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6" + "\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6" + "\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035" + "\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa" + "\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31ba" + "\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fef\ua000-\ua48c" + "\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b" + "\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f" + "\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7c6\ua7f7-\ua801" + "\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873" + "\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925" + "\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4" + "\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42" + "\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1" + "\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd" + "\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e" + "\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a" + "\uab5c-\uab67\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6" + "\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06" + "\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c" + "\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d" + "\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74" + "\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe" + "\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc" + "\u{10000}-\u{1000b}\u{1000d}-\u{10026}\u{10028}-\u{1003a}" + "\u{1003c}\u{1003d}\u{1003f}-\u{1004d}\u{10050}-\u{1005d}" + "\u{10080}-\u{100fa}\u{10140}-\u{10174}\u{10280}-\u{1029c}" + "\u{102a0}-\u{102d0}\u{10300}-\u{1031f}\u{1032d}-\u{1034a}" + "\u{10350}-\u{10375}\u{10380}-\u{1039d}\u{103a0}-\u{103c3}" + "\u{103c8}-\u{103cf}\u{103d1}-\u{103d5}\u{10400}-\u{1049d}" + "\u{104b0}-\u{104d3}\u{104d8}-\u{104fb}\u{10500}-\u{10527}" + "\u{10530}-\u{10563}\u{10600}-\u{10736}\u{10740}-\u{10755}" + "\u{10760}-\u{10767}\u{10800}-\u{10805}\u{10808}" + "\u{1080a}-\u{10835}\u{10837}\u{10838}\u{1083c}" + "\u{1083f}-\u{10855}\u{10860}-\u{10876}\u{10880}-\u{1089e}" + "\u{108e0}-\u{108f2}\u{108f4}\u{108f5}\u{10900}-\u{10915}" + "\u{10920}-\u{10939}\u{10980}-\u{109b7}\u{109be}\u{109bf}" + "\u{10a00}\u{10a10}-\u{10a13}\u{10a15}-\u{10a17}" + "\u{10a19}-\u{10a35}\u{10a60}-\u{10a7c}\u{10a80}-\u{10a9c}" + "\u{10ac0}-\u{10ac7}\u{10ac9}-\u{10ae4}\u{10b00}-\u{10b35}" + "\u{10b40}-\u{10b55}\u{10b60}-\u{10b72}\u{10b80}-\u{10b91}" + "\u{10c00}-\u{10c48}\u{10c80}-\u{10cb2}\u{10cc0}-\u{10cf2}" + "\u{10d00}-\u{10d23}\u{10f00}-\u{10f1c}\u{10f27}" + "\u{10f30}-\u{10f45}\u{10fe0}-\u{10ff6}\u{11003}-\u{11037}" + "\u{11083}-\u{110af}\u{110d0}-\u{110e8}\u{11103}-\u{11126}" + "\u{11144}\u{11150}-\u{11172}\u{11176}\u{11183}-\u{111b2}" + "\u{111c1}-\u{111c4}\u{111da}\u{111dc}\u{11200}-\u{11211}" + "\u{11213}-\u{1122b}\u{11280}-\u{11286}\u{11288}" + "\u{1128a}-\u{1128d}\u{1128f}-\u{1129d}\u{1129f}-\u{112a8}" + "\u{112b0}-\u{112de}\u{11305}-\u{1130c}\u{1130f}\u{11310}" + "\u{11313}-\u{11328}\u{1132a}-\u{11330}\u{11332}\u{11333}" + "\u{11335}-\u{11339}\u{1133d}\u{11350}\u{1135d}-\u{11361}" + "\u{11400}-\u{11434}\u{11447}-\u{1144a}\u{1145f}" + "\u{11480}-\u{114af}\u{114c4}\u{114c5}\u{114c7}" + "\u{11580}-\u{115ae}\u{115d8}-\u{115db}\u{11600}-\u{1162f}" + "\u{11644}\u{11680}-\u{116aa}\u{116b8}\u{11700}-\u{1171a}" + "\u{11800}-\u{1182b}\u{118a0}-\u{118df}\u{118ff}" + "\u{119a0}-\u{119a7}\u{119aa}-\u{119d0}\u{119e1}\u{119e3}" + "\u{11a00}\u{11a0b}-\u{11a32}\u{11a3a}\u{11a50}" + "\u{11a5c}-\u{11a89}\u{11a9d}\u{11ac0}-\u{11af8}" + "\u{11c00}-\u{11c08}\u{11c0a}-\u{11c2e}\u{11c40}" + "\u{11c72}-\u{11c8f}\u{11d00}-\u{11d06}\u{11d08}\u{11d09}" + "\u{11d0b}-\u{11d30}\u{11d46}\u{11d60}-\u{11d65}" + "\u{11d67}\u{11d68}\u{11d6a}-\u{11d89}\u{11d98}" + "\u{11ee0}-\u{11ef2}\u{12000}-\u{12399}\u{12400}-\u{1246e}" + "\u{12480}-\u{12543}\u{13000}-\u{1342e}\u{14400}-\u{14646}" + "\u{16800}-\u{16a38}\u{16a40}-\u{16a5e}\u{16ad0}-\u{16aed}" + "\u{16b00}-\u{16b2f}\u{16b40}-\u{16b43}\u{16b63}-\u{16b77}" + "\u{16b7d}-\u{16b8f}\u{16e40}-\u{16e7f}\u{16f00}-\u{16f4a}" + "\u{16f50}\u{16f93}-\u{16f9f}\u{16fe0}\u{16fe1}\u{16fe3}" + "\u{17000}-\u{187f7}\u{18800}-\u{18af2}\u{1b000}-\u{1b11e}" + "\u{1b150}-\u{1b152}\u{1b164}-\u{1b167}\u{1b170}-\u{1b2fb}" + "\u{1bc00}-\u{1bc6a}\u{1bc70}-\u{1bc7c}\u{1bc80}-\u{1bc88}" + "\u{1bc90}-\u{1bc99}\u{1d400}-\u{1d454}\u{1d456}-\u{1d49c}" + "\u{1d49e}\u{1d49f}\u{1d4a2}\u{1d4a5}\u{1d4a6}" + "\u{1d4a9}-\u{1d4ac}\u{1d4ae}-\u{1d4b9}\u{1d4bb}" + "\u{1d4bd}-\u{1d4c3}\u{1d4c5}-\u{1d505}\u{1d507}-\u{1d50a}" + "\u{1d50d}-\u{1d514}\u{1d516}-\u{1d51c}\u{1d51e}-\u{1d539}" + "\u{1d53b}-\u{1d53e}\u{1d540}-\u{1d544}\u{1d546}" + "\u{1d54a}-\u{1d550}\u{1d552}-\u{1d6a5}\u{1d6a8}-\u{1d6c0}" + "\u{1d6c2}-\u{1d6da}\u{1d6dc}-\u{1d6fa}\u{1d6fc}-\u{1d714}" + "\u{1d716}-\u{1d734}\u{1d736}-\u{1d74e}\u{1d750}-\u{1d76e}" + "\u{1d770}-\u{1d788}\u{1d78a}-\u{1d7a8}\u{1d7aa}-\u{1d7c2}" + "\u{1d7c4}-\u{1d7cb}\u{1e100}-\u{1e12c}\u{1e137}-\u{1e13d}" + "\u{1e14e}\u{1e2c0}-\u{1e2eb}\u{1e800}-\u{1e8c4}" + "\u{1e900}-\u{1e943}\u{1e94b}\u{1ee00}-\u{1ee03}" + "\u{1ee05}-\u{1ee1f}\u{1ee21}\u{1ee22}\u{1ee24}\u{1ee27}" + "\u{1ee29}-\u{1ee32}\u{1ee34}-\u{1ee37}\u{1ee39}\u{1ee3b}" + "\u{1ee42}\u{1ee47}\u{1ee49}\u{1ee4b}\u{1ee4d}-\u{1ee4f}" + "\u{1ee51}\u{1ee52}\u{1ee54}\u{1ee57}\u{1ee59}\u{1ee5b}" + "\u{1ee5d}\u{1ee5f}\u{1ee61}\u{1ee62}\u{1ee64}" + "\u{1ee67}-\u{1ee6a}\u{1ee6c}-\u{1ee72}\u{1ee74}-\u{1ee77}" + "\u{1ee79}-\u{1ee7c}\u{1ee7e}\u{1ee80}-\u{1ee89}" + "\u{1ee8b}-\u{1ee9b}\u{1eea1}-\u{1eea3}\u{1eea5}-\u{1eea9}" + "\u{1eeab}-\u{1eebb}\u{20000}-\u{2a6d6}\u{2a700}-\u{2b734}" + "\u{2b740}-\u{2b81d}\u{2b820}-\u{2cea1}\u{2ceb0}-\u{2ebe0}" + "\u{2f800}-\u{2fa1d}]$", "u"); - } - - return largeIdStartPattern.test(String.fromCodePoint(cp)); + return isInRange(cp, largeIdStartRanges || (largeIdStartRanges = initLargeIdStartRanges())); } function isLargeIdContinue(cp) { - if (!largeIdContinuePattern) { - largeIdContinuePattern = new RegExp("^[\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf" + "\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669" + "\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed" + "\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9" + "\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827" + "\u0829-\u082d\u0859-\u085b\u08d3-\u08e1\u08e3-\u0903" + "\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963" + "\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8" + "\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe" + "\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d" + "\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5" + "\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef" + "\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48" + "\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82" + "\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef" + "\u0c00-\u0c04\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d" + "\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc" + "\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6" + "\u0ce2\u0ce3\u0ce6-\u0cef\u0d00-\u0d03\u0d3b\u0d3c" + "\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63" + "\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6" + "\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a" + "\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ecd" + "\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39" + "\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97" + "\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059" + "\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074" + "\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371" + "\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773" + "\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819" + "\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da" + "\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89" + "\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44" + "\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad" + "\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49" + "\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4" + "\u1cf7-\u1cf9\u1dc0-\u1df9\u1dfb-\u1dff\u203f\u2040\u2054" + "\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f" + "\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f" + "\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b" + "\ua823-\ua827\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9" + "\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953" + "\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9" + "\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d" + "\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1" + "\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed" + "\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34" + "\ufe4d-\ufe4f\uff10-\uff19\uff3f\u{101fd}\u{102e0}" + "\u{10376}-\u{1037a}\u{104a0}-\u{104a9}\u{10a01}-\u{10a03}" + "\u{10a05}\u{10a06}\u{10a0c}-\u{10a0f}\u{10a38}-\u{10a3a}" + "\u{10a3f}\u{10ae5}\u{10ae6}\u{10d24}-\u{10d27}" + "\u{10d30}-\u{10d39}\u{10f46}-\u{10f50}\u{11000}-\u{11002}" + "\u{11038}-\u{11046}\u{11066}-\u{1106f}\u{1107f}-\u{11082}" + "\u{110b0}-\u{110ba}\u{110f0}-\u{110f9}\u{11100}-\u{11102}" + "\u{11127}-\u{11134}\u{11136}-\u{1113f}\u{11145}\u{11146}" + "\u{11173}\u{11180}-\u{11182}\u{111b3}-\u{111c0}" + "\u{111c9}-\u{111cc}\u{111d0}-\u{111d9}\u{1122c}-\u{11237}" + "\u{1123e}\u{112df}-\u{112ea}\u{112f0}-\u{112f9}" + "\u{11300}-\u{11303}\u{1133b}\u{1133c}\u{1133e}-\u{11344}" + "\u{11347}\u{11348}\u{1134b}-\u{1134d}\u{11357}" + "\u{11362}\u{11363}\u{11366}-\u{1136c}\u{11370}-\u{11374}" + "\u{11435}-\u{11446}\u{11450}-\u{11459}\u{1145e}" + "\u{114b0}-\u{114c3}\u{114d0}-\u{114d9}\u{115af}-\u{115b5}" + "\u{115b8}-\u{115c0}\u{115dc}\u{115dd}\u{11630}-\u{11640}" + "\u{11650}-\u{11659}\u{116ab}-\u{116b7}\u{116c0}-\u{116c9}" + "\u{1171d}-\u{1172b}\u{11730}-\u{11739}\u{1182c}-\u{1183a}" + "\u{118e0}-\u{118e9}\u{119d1}-\u{119d7}\u{119da}-\u{119e0}" + "\u{119e4}\u{11a01}-\u{11a0a}\u{11a33}-\u{11a39}" + "\u{11a3b}-\u{11a3e}\u{11a47}\u{11a51}-\u{11a5b}" + "\u{11a8a}-\u{11a99}\u{11c2f}-\u{11c36}\u{11c38}-\u{11c3f}" + "\u{11c50}-\u{11c59}\u{11c92}-\u{11ca7}\u{11ca9}-\u{11cb6}" + "\u{11d31}-\u{11d36}\u{11d3a}\u{11d3c}\u{11d3d}" + "\u{11d3f}-\u{11d45}\u{11d47}\u{11d50}-\u{11d59}" + "\u{11d8a}-\u{11d8e}\u{11d90}\u{11d91}\u{11d93}-\u{11d97}" + "\u{11da0}-\u{11da9}\u{11ef3}-\u{11ef6}\u{16a60}-\u{16a69}" + "\u{16af0}-\u{16af4}\u{16b30}-\u{16b36}\u{16b50}-\u{16b59}" + "\u{16f4f}\u{16f51}-\u{16f87}\u{16f8f}-\u{16f92}" + "\u{1bc9d}\u{1bc9e}\u{1d165}-\u{1d169}\u{1d16d}-\u{1d172}" + "\u{1d17b}-\u{1d182}\u{1d185}-\u{1d18b}\u{1d1aa}-\u{1d1ad}" + "\u{1d242}-\u{1d244}\u{1d7ce}-\u{1d7ff}\u{1da00}-\u{1da36}" + "\u{1da3b}-\u{1da6c}\u{1da75}\u{1da84}\u{1da9b}-\u{1da9f}" + "\u{1daa1}-\u{1daaf}\u{1e000}-\u{1e006}\u{1e008}-\u{1e018}" + "\u{1e01b}-\u{1e021}\u{1e023}\u{1e024}\u{1e026}-\u{1e02a}" + "\u{1e130}-\u{1e136}\u{1e140}-\u{1e149}\u{1e2ec}-\u{1e2f9}" + "\u{1e8d0}-\u{1e8d6}\u{1e944}-\u{1e94a}\u{1e950}-\u{1e959}" + "\u{e0100}-\u{e01ef}]$", "u"); - } + return isInRange(cp, largeIdContinueRanges || (largeIdContinueRanges = initLargeIdContinueRanges())); +} - return largeIdContinuePattern.test(String.fromCodePoint(cp)); +function initLargeIdStartRanges() { + return restoreRanges("170 0 11 0 5 0 6 22 2 30 2 457 5 11 15 4 8 0 2 0 130 4 2 1 3 3 2 0 7 0 2 2 2 0 2 19 2 82 2 138 9 165 2 37 3 0 7 40 72 26 5 3 46 42 36 1 2 98 2 0 16 1 8 1 11 2 3 0 17 0 2 29 30 88 12 0 25 32 10 1 5 0 6 21 5 0 10 0 4 0 24 24 8 10 54 20 2 17 61 53 4 0 19 0 8 9 16 15 5 7 3 1 3 21 2 6 2 0 4 3 4 0 17 0 14 1 2 2 15 1 11 0 9 5 5 1 3 21 2 6 2 1 2 1 2 1 32 3 2 0 20 2 17 8 2 2 2 21 2 6 2 1 2 4 4 0 19 0 16 1 24 0 12 7 3 1 3 21 2 6 2 1 2 4 4 0 31 1 2 2 16 0 18 0 2 5 4 2 2 3 4 1 2 0 2 1 4 1 4 2 4 11 23 0 53 7 2 2 2 22 2 15 4 0 27 2 6 1 31 0 5 7 2 2 2 22 2 9 2 4 4 0 33 0 2 1 16 1 18 8 2 2 2 40 3 0 17 0 6 2 9 2 25 5 6 17 4 23 2 8 2 0 3 6 59 47 2 1 13 6 59 1 2 0 2 4 2 23 2 0 2 9 2 1 10 0 3 4 2 0 22 3 33 0 64 7 2 35 28 4 116 42 21 0 17 5 5 3 4 0 4 1 8 2 5 12 13 0 18 37 2 0 6 0 3 42 2 332 2 3 3 6 2 0 2 3 3 40 2 3 3 32 2 3 3 6 2 0 2 3 3 14 2 56 2 3 3 66 38 15 17 85 3 5 4 619 3 16 2 25 6 74 4 10 8 12 2 3 15 17 15 17 15 12 2 2 16 51 36 0 5 0 68 88 8 40 2 0 6 69 11 30 50 29 3 4 12 43 5 25 55 22 10 52 83 0 94 46 18 6 56 29 14 1 11 43 27 35 42 2 11 35 3 8 8 42 3 2 42 3 2 5 2 1 4 0 6 191 65 277 3 5 3 37 3 5 3 7 2 0 2 0 2 0 2 30 3 52 2 6 2 0 4 2 2 6 4 3 3 5 5 12 6 2 2 6 117 0 14 0 17 12 102 0 5 0 3 9 2 0 3 5 7 0 2 0 2 0 2 15 3 3 6 4 5 0 18 40 2680 46 2 46 2 132 7 3 4 1 13 37 2 0 6 0 3 55 8 0 17 22 10 6 2 6 2 6 2 6 2 6 2 6 2 6 2 6 551 2 26 8 8 4 3 4 5 85 5 4 2 89 2 3 6 42 2 93 18 31 49 15 513 6591 65 20988 4 1164 68 45 3 268 4 15 11 1 21 46 17 30 3 79 40 8 3 102 3 52 3 8 43 12 2 2 2 3 2 22 30 51 15 49 63 5 4 0 2 1 12 27 11 22 26 28 8 46 29 0 17 4 2 9 11 4 2 40 24 2 2 7 21 22 4 0 4 49 2 0 4 1 3 4 3 0 2 0 25 2 3 10 8 2 13 5 3 5 3 5 10 6 2 6 2 42 2 13 7 114 30 11171 13 22 5 48 8453 365 3 105 39 6 13 4 6 0 2 9 2 12 2 4 2 0 2 1 2 1 2 107 34 362 19 63 3 53 41 11 117 4 2 134 37 25 7 25 12 88 4 5 3 5 3 5 3 2 36 11 2 25 2 18 2 1 2 14 3 13 35 122 70 52 268 28 4 48 48 31 14 29 6 37 11 29 3 35 5 7 2 4 43 157 19 35 5 35 5 39 9 51 157 310 10 21 11 7 153 5 3 0 2 43 2 1 4 0 3 22 11 22 10 30 66 18 2 1 11 21 11 25 71 55 7 1 65 0 16 3 2 2 2 28 43 28 4 28 36 7 2 27 28 53 11 21 11 18 14 17 111 72 56 50 14 50 14 35 349 41 7 1 79 28 11 0 9 21 107 20 28 22 13 52 76 44 33 24 27 35 30 0 3 0 9 34 4 0 13 47 15 3 22 0 2 0 36 17 2 24 85 6 2 0 2 3 2 14 2 9 8 46 39 7 3 1 3 21 2 6 2 1 2 4 4 0 19 0 13 4 159 52 19 3 21 2 31 47 21 1 2 0 185 46 42 3 37 47 21 0 60 42 14 0 72 26 230 43 117 63 32 7 3 0 3 7 2 1 2 23 16 0 2 0 95 7 3 38 17 0 2 0 29 0 11 39 8 0 22 0 12 45 20 0 35 56 264 8 2 36 18 0 50 29 113 6 2 1 2 37 22 0 26 5 2 1 2 31 15 0 328 18 190 0 80 921 103 110 18 195 2749 1070 4050 582 8634 568 8 30 114 29 19 47 17 3 32 20 6 18 689 63 129 74 6 0 67 12 65 1 2 0 29 6135 9 1237 43 8 8952 286 50 2 18 3 9 395 2309 106 6 12 4 8 8 9 5991 84 2 70 2 1 3 0 3 1 3 3 2 11 2 0 2 6 2 64 2 3 3 7 2 6 2 27 2 3 2 4 2 0 4 6 2 339 3 24 2 24 2 30 2 24 2 30 2 24 2 30 2 24 2 30 2 24 2 7 2357 44 11 6 17 0 370 43 1301 196 60 67 8 0 1205 3 2 26 2 1 2 0 3 0 2 9 2 3 2 0 2 0 7 0 5 0 2 0 2 0 2 2 2 1 2 0 3 0 2 0 2 0 2 0 2 0 2 1 2 0 3 3 2 6 2 3 2 3 2 0 2 9 2 16 6 2 2 4 2 16 4421 42717 35 4148 12 221 3 5761 15 7472 3104 541 1507 4938"); } -const gcNamePattern = /^(?:General_Category|gc)$/u; -const scNamePattern = /^(?:Script(?:_Extensions)?|scx?)$/u; -const gcValuePatterns = { - es2018: null, - es2019: null, - es2020: null -}; -const scValuePatterns = { - es2018: null, - es2019: null, - es2020: null -}; -const binPropertyPatterns = { - es2018: null, - es2019: null, - es2020: null -}; +function initLargeIdContinueRanges() { + return restoreRanges("183 0 585 111 24 0 252 4 266 44 2 0 2 1 2 1 2 0 73 10 49 30 7 0 102 6 3 5 3 1 2 3 3 9 24 0 31 26 92 10 16 9 34 8 10 0 25 3 2 8 2 2 2 4 44 2 120 14 2 32 55 2 2 17 2 6 11 1 3 9 18 2 57 0 2 6 3 1 3 2 10 0 11 1 3 9 15 0 3 2 57 0 2 4 5 1 3 2 4 0 21 11 4 0 12 2 57 0 2 7 2 2 2 2 21 1 3 9 11 5 2 2 57 0 2 6 3 1 3 2 8 2 11 1 3 9 19 0 60 4 4 2 2 3 10 0 15 9 17 4 58 6 2 2 2 3 8 1 12 1 3 9 18 2 57 0 2 6 2 2 2 3 8 1 12 1 3 9 17 3 56 1 2 6 2 2 2 3 10 0 11 1 3 9 18 2 71 0 5 5 2 0 2 7 7 9 3 1 62 0 3 6 13 7 2 9 88 0 3 8 12 5 3 9 63 1 7 9 12 0 2 0 2 0 5 1 50 19 2 1 6 10 2 35 10 0 101 19 2 9 13 3 5 2 2 2 3 6 4 3 14 11 2 14 704 2 10 8 929 2 30 2 30 1 31 1 65 31 10 0 3 9 34 2 3 9 144 0 119 11 5 11 11 9 129 10 61 4 58 9 2 28 3 10 7 9 23 13 2 1 64 4 48 16 12 9 18 8 13 2 31 12 3 9 45 13 49 19 9 9 7 9 119 2 2 20 5 0 7 0 3 2 199 57 2 4 576 1 20 0 124 12 5 0 4 11 3071 2 142 0 97 31 555 5 106 1 30086 9 70 0 5 9 33 1 81 1 273 0 4 0 5 0 24 4 5 0 84 1 51 17 11 9 7 17 14 10 29 7 26 12 45 3 48 13 16 9 12 0 11 9 48 13 13 0 9 1 3 9 34 2 51 0 2 2 3 1 6 1 2 0 42 4 6 1 237 7 2 1 3 9 20261 0 738 15 17 15 4 1 25 2 193 9 38 0 702 0 227 0 150 4 294 9 1368 2 2 1 6 3 41 2 5 0 166 1 574 3 9 9 370 1 154 10 176 2 54 14 32 9 16 3 46 10 54 9 7 2 37 13 2 9 6 1 45 0 13 2 49 13 9 3 2 11 83 11 7 0 161 11 6 9 7 3 56 1 2 6 3 1 3 2 10 0 11 1 3 6 4 4 193 17 10 9 5 0 82 19 13 9 214 6 3 8 28 1 83 16 16 9 82 12 9 9 84 14 5 9 243 14 166 9 71 5 2 1 3 3 2 0 2 1 13 9 120 6 3 6 4 0 29 9 41 6 2 3 9 0 10 10 47 15 406 7 2 7 17 9 57 21 2 13 123 5 4 0 2 1 2 6 2 0 9 9 49 4 2 1 2 4 9 9 330 3 19306 9 135 4 60 6 26 9 1014 0 2 54 8 3 82 0 12 1 19628 1 5319 4 4 5 9 7 3 6 31 3 149 2 1418 49 513 54 5 49 9 0 15 0 23 4 2 14 1361 6 2 16 3 6 2 1 2 4 262 6 10 9 419 13 1495 6 110 6 6 9 4759 9 787719 239"); +} -function isValidUnicodeProperty(version, name, value) { - if (gcNamePattern.test(name)) { - if (version >= 2018) { - if (!gcValuePatterns.es2018) { - gcValuePatterns.es2018 = new RegExp("^(?:C|Cased_Letter|Cc|Cf|Close_Punctuation|Cn|Co|" + "Combining_Mark|Connector_Punctuation|Control|Cs|" + "Currency_Symbol|Dash_Punctuation|Decimal_Number|" + "Enclosing_Mark|Final_Punctuation|Format|" + "Initial_Punctuation|L|LC|Letter|Letter_Number|" + "Line_Separator|Ll|Lm|Lo|Lowercase_Letter|Lt|Lu|M|" + "Mark|Math_Symbol|Mc|Me|Mn|Modifier_Letter|" + "Modifier_Symbol|N|Nd|Nl|No|Nonspacing_Mark|Number|" + "Open_Punctuation|Other|Other_Letter|Other_Number|" + "Other_Punctuation|Other_Symbol|P|" + "Paragraph_Separator|Pc|Pd|Pe|Pf|Pi|Po|Private_Use|" + "Ps|Punctuation|S|Sc|Separator|Sk|Sm|So|" + "Space_Separator|Spacing_Mark|Surrogate|Symbol|" + "Titlecase_Letter|Unassigned|Uppercase_Letter|Z|Zl|" + "Zp|Zs|cntrl|digit|punct)$", "u"); - } +function isInRange(cp, ranges) { + let l = 0, + r = ranges.length / 2 | 0, + i = 0, + min = 0, + max = 0; - if (gcValuePatterns.es2018.test(value)) { - return true; - } + while (l < r) { + i = (l + r) / 2 | 0; + min = ranges[2 * i]; + max = ranges[2 * i + 1]; + + if (cp < min) { + r = i; + } else if (cp > max) { + l = i + 1; + } else { + return true; } } - if (scNamePattern.test(name)) { - if (version >= 2018) { - if (!scValuePatterns.es2018) { - scValuePatterns.es2018 = new RegExp("^(?:Adlam|Adlm|Aghb|Ahom|Anatolian_Hieroglyphs|Arab|" + "Arabic|Armenian|Armi|Armn|Avestan|Avst|Bali|" + "Balinese|Bamu|Bamum|Bass|Bassa_Vah|Batak|Batk|Beng|" + "Bengali|Bhaiksuki|Bhks|Bopo|Bopomofo|Brah|Brahmi|" + "Brai|Braille|Bugi|Buginese|Buhd|Buhid|Cakm|" + "Canadian_Aboriginal|Cans|Cari|Carian|" + "Caucasian_Albanian|Chakma|Cham|Cher|Cherokee|Common|" + "Copt|Coptic|Cprt|Cuneiform|Cypriot|Cyrillic|Cyrl|" + "Deseret|Deva|Devanagari|Dsrt|Dupl|Duployan|Egyp|" + "Egyptian_Hieroglyphs|Elba|Elbasan|Ethi|Ethiopic|" + "Geor|Georgian|Glag|Glagolitic|Gonm|Goth|Gothic|Gran|" + "Grantha|Greek|Grek|Gujarati|Gujr|Gurmukhi|Guru|Han|" + "Hang|Hangul|Hani|Hano|Hanunoo|Hatr|Hatran|Hebr|" + "Hebrew|Hira|Hiragana|Hluw|Hmng|Hung|" + "Imperial_Aramaic|Inherited|Inscriptional_Pahlavi|" + "Inscriptional_Parthian|Ital|Java|Javanese|Kaithi|" + "Kali|Kana|Kannada|Katakana|Kayah_Li|Khar|Kharoshthi|" + "Khmer|Khmr|Khoj|Khojki|Khudawadi|Knda|Kthi|Lana|Lao|" + "Laoo|Latin|Latn|Lepc|Lepcha|Limb|Limbu|Lina|Linb|" + "Linear_A|Linear_B|Lisu|Lyci|Lycian|Lydi|Lydian|" + "Mahajani|Mahj|Malayalam|Mand|Mandaic|Mani|" + "Manichaean|Marc|Marchen|Masaram_Gondi|Meetei_Mayek|" + "Mend|Mende_Kikakui|Merc|Mero|Meroitic_Cursive|" + "Meroitic_Hieroglyphs|Miao|Mlym|Modi|Mong|Mongolian|" + "Mro|Mroo|Mtei|Mult|Multani|Myanmar|Mymr|Nabataean|" + "Narb|Nbat|New_Tai_Lue|Newa|Nko|Nkoo|Nshu|Nushu|Ogam|" + "Ogham|Ol_Chiki|Olck|Old_Hungarian|Old_Italic|" + "Old_North_Arabian|Old_Permic|Old_Persian|" + "Old_South_Arabian|Old_Turkic|Oriya|Orkh|Orya|Osage|" + "Osge|Osma|Osmanya|Pahawh_Hmong|Palm|Palmyrene|" + "Pau_Cin_Hau|Pauc|Perm|Phag|Phags_Pa|Phli|Phlp|Phnx|" + "Phoenician|Plrd|Prti|Psalter_Pahlavi|Qaac|Qaai|" + "Rejang|Rjng|Runic|Runr|Samaritan|Samr|Sarb|Saur|" + "Saurashtra|Sgnw|Sharada|Shavian|Shaw|Shrd|Sidd|" + "Siddham|SignWriting|Sind|Sinh|Sinhala|Sora|" + "Sora_Sompeng|Soyo|Soyombo|Sund|Sundanese|Sylo|" + "Syloti_Nagri|Syrc|Syriac|Tagalog|Tagb|Tagbanwa|" + "Tai_Le|Tai_Tham|Tai_Viet|Takr|Takri|Tale|Talu|Tamil|" + "Taml|Tang|Tangut|Tavt|Telu|Telugu|Tfng|Tglg|Thaa|" + "Thaana|Thai|Tibetan|Tibt|Tifinagh|Tirh|Tirhuta|Ugar|" + "Ugaritic|Vai|Vaii|Wara|Warang_Citi|Xpeo|Xsux|Yi|" + "Yiii|Zanabazar_Square|Zanb|Zinh|Zyyy)$", "u"); - } + return false; +} - if (scValuePatterns.es2018.test(value)) { - return true; - } - } +function restoreRanges(data) { + let last = 0; + return data.split(" ").map(s => last += parseInt(s, 10) | 0); +} - if (version >= 2019) { - if (!scValuePatterns.es2019) { - scValuePatterns.es2019 = new RegExp("^(?:Dogr|Dogra|Gong|Gunjala_Gondi|Hanifi_Rohingya|" + "Maka|Makasar|Medefaidrin|Medf|Old_Sogdian|Rohg|Sogd|" + "Sogdian|Sogo)$", "u"); - } +class DataSet { + constructor(raw2018, raw2019, raw2020) { + this._raw2018 = raw2018; + this._raw2019 = raw2019; + this._raw2020 = raw2020; + } - if (scValuePatterns.es2019.test(value)) { - return true; - } - } + get es2018() { + return this._set2018 || (this._set2018 = new Set(this._raw2018.split(" "))); + } - if (version >= 2020) { - if (!scValuePatterns.es2020) { - scValuePatterns.es2020 = new RegExp("^(?:Hmnp|Nand|Nandinagari|Nyiakeng_Puachue_Hmong|" + "Wancho|Wcho)$", "u"); - } + get es2019() { + return this._set2019 || (this._set2019 = new Set(this._raw2019.split(" "))); + } - if (scValuePatterns.es2020.test(value)) { - return true; - } - } + get es2020() { + return this._set2020 || (this._set2020 = new Set(this._raw2020.split(" "))); } - return false; } -function isValidLoneUnicodeProperty(version, value) { - if (version >= 2018) { - if (!binPropertyPatterns.es2018) { - binPropertyPatterns.es2018 = new RegExp("^(?:AHex|ASCII|ASCII_Hex_Digit|Alpha|Alphabetic|Any|" + "Assigned|Bidi_C|Bidi_Control|Bidi_M|Bidi_Mirrored|CI|" + "CWCF|CWCM|CWKCF|CWL|CWT|CWU|Case_Ignorable|Cased|" + "Changes_When_Casefolded|Changes_When_Casemapped|" + "Changes_When_Lowercased|Changes_When_NFKC_Casefolded|" + "Changes_When_Titlecased|Changes_When_Uppercased|DI|Dash|" + "Default_Ignorable_Code_Point|Dep|Deprecated|Dia|" + "Diacritic|Emoji|Emoji_Component|Emoji_Modifier|" + "Emoji_Modifier_Base|Emoji_Presentation|Ext|Extender|" + "Gr_Base|Gr_Ext|Grapheme_Base|Grapheme_Extend|Hex|" + "Hex_Digit|IDC|IDS|IDSB|IDST|IDS_Binary_Operator|" + "IDS_Trinary_Operator|ID_Continue|ID_Start|Ideo|" + "Ideographic|Join_C|Join_Control|LOE|" + "Logical_Order_Exception|Lower|Lowercase|Math|NChar|" + "Noncharacter_Code_Point|Pat_Syn|Pat_WS|Pattern_Syntax|" + "Pattern_White_Space|QMark|Quotation_Mark|RI|Radical|" + "Regional_Indicator|SD|STerm|Sentence_Terminal|" + "Soft_Dotted|Term|Terminal_Punctuation|UIdeo|" + "Unified_Ideograph|Upper|Uppercase|VS|Variation_Selector|" + "White_Space|XIDC|XIDS|XID_Continue|XID_Start|space)$", "u"); - } +const gcNameSet = new Set(["General_Category", "gc"]); +const scNameSet = new Set(["Script", "Script_Extensions", "sc", "scx"]); +const gcValueSets = new DataSet("C Cased_Letter Cc Cf Close_Punctuation Cn Co Combining_Mark Connector_Punctuation Control Cs Currency_Symbol Dash_Punctuation Decimal_Number Enclosing_Mark Final_Punctuation Format Initial_Punctuation L LC Letter Letter_Number Line_Separator Ll Lm Lo Lowercase_Letter Lt Lu M Mark Math_Symbol Mc Me Mn Modifier_Letter Modifier_Symbol N Nd Nl No Nonspacing_Mark Number Open_Punctuation Other Other_Letter Other_Number Other_Punctuation Other_Symbol P Paragraph_Separator Pc Pd Pe Pf Pi Po Private_Use Ps Punctuation S Sc Separator Sk Sm So Space_Separator Spacing_Mark Surrogate Symbol Titlecase_Letter Unassigned Uppercase_Letter Z Zl Zp Zs cntrl digit punct", "", ""); +const scValueSets = new DataSet("Adlam Adlm Aghb Ahom Anatolian_Hieroglyphs Arab Arabic Armenian Armi Armn Avestan Avst Bali Balinese Bamu Bamum Bass Bassa_Vah Batak Batk Beng Bengali Bhaiksuki Bhks Bopo Bopomofo Brah Brahmi Brai Braille Bugi Buginese Buhd Buhid Cakm Canadian_Aboriginal Cans Cari Carian Caucasian_Albanian Chakma Cham Cher Cherokee Common Copt Coptic Cprt Cuneiform Cypriot Cyrillic Cyrl Deseret Deva Devanagari Dsrt Dupl Duployan Egyp Egyptian_Hieroglyphs Elba Elbasan Ethi Ethiopic Geor Georgian Glag Glagolitic Gonm Goth Gothic Gran Grantha Greek Grek Gujarati Gujr Gurmukhi Guru Han Hang Hangul Hani Hano Hanunoo Hatr Hatran Hebr Hebrew Hira Hiragana Hluw Hmng Hung Imperial_Aramaic Inherited Inscriptional_Pahlavi Inscriptional_Parthian Ital Java Javanese Kaithi Kali Kana Kannada Katakana Kayah_Li Khar Kharoshthi Khmer Khmr Khoj Khojki Khudawadi Knda Kthi Lana Lao Laoo Latin Latn Lepc Lepcha Limb Limbu Lina Linb Linear_A Linear_B Lisu Lyci Lycian Lydi Lydian Mahajani Mahj Malayalam Mand Mandaic Mani Manichaean Marc Marchen Masaram_Gondi Meetei_Mayek Mend Mende_Kikakui Merc Mero Meroitic_Cursive Meroitic_Hieroglyphs Miao Mlym Modi Mong Mongolian Mro Mroo Mtei Mult Multani Myanmar Mymr Nabataean Narb Nbat New_Tai_Lue Newa Nko Nkoo Nshu Nushu Ogam Ogham Ol_Chiki Olck Old_Hungarian Old_Italic Old_North_Arabian Old_Permic Old_Persian Old_South_Arabian Old_Turkic Oriya Orkh Orya Osage Osge Osma Osmanya Pahawh_Hmong Palm Palmyrene Pau_Cin_Hau Pauc Perm Phag Phags_Pa Phli Phlp Phnx Phoenician Plrd Prti Psalter_Pahlavi Qaac Qaai Rejang Rjng Runic Runr Samaritan Samr Sarb Saur Saurashtra Sgnw Sharada Shavian Shaw Shrd Sidd Siddham SignWriting Sind Sinh Sinhala Sora Sora_Sompeng Soyo Soyombo Sund Sundanese Sylo Syloti_Nagri Syrc Syriac Tagalog Tagb Tagbanwa Tai_Le Tai_Tham Tai_Viet Takr Takri Tale Talu Tamil Taml Tang Tangut Tavt Telu Telugu Tfng Tglg Thaa Thaana Thai Tibetan Tibt Tifinagh Tirh Tirhuta Ugar Ugaritic Vai Vaii Wara Warang_Citi Xpeo Xsux Yi Yiii Zanabazar_Square Zanb Zinh Zyyy", "Dogr Dogra Gong Gunjala_Gondi Hanifi_Rohingya Maka Makasar Medefaidrin Medf Old_Sogdian Rohg Sogd Sogdian Sogo", "Elym Elymaic Hmnp Nand Nandinagari Nyiakeng_Puachue_Hmong Wancho Wcho"); +const binPropertySets = new DataSet("AHex ASCII ASCII_Hex_Digit Alpha Alphabetic Any Assigned Bidi_C Bidi_Control Bidi_M Bidi_Mirrored CI CWCF CWCM CWKCF CWL CWT CWU Case_Ignorable Cased Changes_When_Casefolded Changes_When_Casemapped Changes_When_Lowercased Changes_When_NFKC_Casefolded Changes_When_Titlecased Changes_When_Uppercased DI Dash Default_Ignorable_Code_Point Dep Deprecated Dia Diacritic Emoji Emoji_Component Emoji_Modifier Emoji_Modifier_Base Emoji_Presentation Ext Extender Gr_Base Gr_Ext Grapheme_Base Grapheme_Extend Hex Hex_Digit IDC IDS IDSB IDST IDS_Binary_Operator IDS_Trinary_Operator ID_Continue ID_Start Ideo Ideographic Join_C Join_Control LOE Logical_Order_Exception Lower Lowercase Math NChar Noncharacter_Code_Point Pat_Syn Pat_WS Pattern_Syntax Pattern_White_Space QMark Quotation_Mark RI Radical Regional_Indicator SD STerm Sentence_Terminal Soft_Dotted Term Terminal_Punctuation UIdeo Unified_Ideograph Upper Uppercase VS Variation_Selector White_Space XIDC XIDS XID_Continue XID_Start space", "Extended_Pictographic", ""); - if (binPropertyPatterns.es2018.test(value)) { - return true; - } +function isValidUnicodeProperty(version, name, value) { + if (gcNameSet.has(name)) { + return version >= 2018 && gcValueSets.es2018.has(value); } - if (version >= 2019) { - if (!binPropertyPatterns.es2019) { - binPropertyPatterns.es2019 = new RegExp("^(?:Extended_Pictographic)$", "u"); - } - - if (binPropertyPatterns.es2019.test(value)) { - return true; - } + if (scNameSet.has(name)) { + return version >= 2018 && scValueSets.es2018.has(value) || version >= 2019 && scValueSets.es2019.has(value) || version >= 2020 && scValueSets.es2020.has(value); } return false; } +function isValidLoneUnicodeProperty(version, value) { + return version >= 2018 && binPropertySets.es2018.has(value) || version >= 2019 && binPropertySets.es2019.has(value); +} + const Backspace = 0x08; const CharacterTabulation = 0x09; const LineFeed = 0x0a; @@ -70128,6 +70181,18 @@ function digitToInt(code) { return code - DigitZero; } +function isLeadSurrogate(code) { + return code >= 0xd800 && code <= 0xdbff; +} + +function isTrailSurrogate(code) { + return code >= 0xdc00 && code <= 0xdfff; +} + +function combineSurrogatePair(lead, trail) { + return (lead - 0xd800) * 0x400 + (trail - 0xdc00) + 0x10000; +} + const legacyImpl = { at(s, end, i) { return i < end ? s.charCodeAt(i) : -1; @@ -70369,12 +70434,12 @@ class RegExpValidator { this._uFlag = uFlag && this.ecmaVersion >= 2015; this._nFlag = uFlag && this.ecmaVersion >= 2018; this.reset(source, start, end); - this.pattern(); + this.consumePattern(); if (!this._nFlag && this.ecmaVersion >= 2018 && this._groupNames.size > 0) { this._nFlag = true; this.rewind(start); - this.pattern(); + this.consumePattern(); } } @@ -70625,7 +70690,7 @@ class RegExpValidator { return this.index !== start; } - pattern() { + consumePattern() { const start = this.index; this._numCapturingParens = this.countCapturingParens(); @@ -70634,7 +70699,7 @@ class RegExpValidator { this._backreferenceNames.clear(); this.onPatternEnter(start); - this.disjunction(); + this.consumeDisjunction(); const cp = this.currentCodePoint; if (this.currentCodePoint !== -1) { @@ -70690,17 +70755,16 @@ class RegExpValidator { return count; } - disjunction() { + consumeDisjunction() { const start = this.index; let i = 0; this.onDisjunctionEnter(start); - this.alternative(i++); - while (this.eat(VerticalLine)) { - this.alternative(i++); - } + do { + this.consumeAlternative(i++); + } while (this.eat(VerticalLine)); - if (this.eatQuantifier(true)) { + if (this.consumeQuantifier(true)) { this.raise("Nothing to repeat"); } @@ -70711,33 +70775,29 @@ class RegExpValidator { this.onDisjunctionLeave(start, this.index); } - alternative(i) { + consumeAlternative(i) { const start = this.index; this.onAlternativeEnter(start, i); - while (this.currentCodePoint !== -1 && this.eatTerm()) {} + while (this.currentCodePoint !== -1 && this.consumeTerm()) {} this.onAlternativeLeave(start, this.index, i); } - eatTerm() { - if (this.eatAssertion()) { - if (this._lastAssertionIsQuantifiable) { - this.eatQuantifier(); - } - - return true; + consumeTerm() { + if (this._uFlag || this.strict) { + return this.consumeAssertion() || this.consumeAtom() && this.consumeOptionalQuantifier(); } - if (this.strict ? this.eatAtom() : this.eatExtendedAtom()) { - this.eatQuantifier(); - return true; - } + return this.consumeAssertion() && (!this._lastAssertionIsQuantifiable || this.consumeOptionalQuantifier()) || this.consumeExtendedAtom() && this.consumeOptionalQuantifier(); + } - return false; + consumeOptionalQuantifier() { + this.consumeQuantifier(); + return true; } - eatAssertion() { + consumeAssertion() { const start = this.index; this._lastAssertionIsQuantifiable = false; @@ -70768,7 +70828,7 @@ class RegExpValidator { if (this.eat(EqualsSign) || (negate = this.eat(ExclamationMark))) { const kind = lookbehind ? "lookbehind" : "lookahead"; this.onLookaroundAssertionEnter(start, kind, negate); - this.disjunction(); + this.consumeDisjunction(); if (!this.eat(RightParenthesis)) { this.raise("Unterminated group"); @@ -70785,7 +70845,7 @@ class RegExpValidator { return false; } - eatQuantifier(noError = false) { + consumeQuantifier(noConsume = false) { const start = this.index; let min = 0; let max = 0; @@ -70800,7 +70860,7 @@ class RegExpValidator { } else if (this.eat(QuestionMark)) { min = 0; max = 1; - } else if (this.eatBracedQuantifier(noError)) { + } else if (this.eatBracedQuantifier(noConsume)) { min = this._lastMinValue; max = this._lastMaxValue; } else { @@ -70809,7 +70869,7 @@ class RegExpValidator { greedy = !this.eat(QuestionMark); - if (!noError) { + if (!noConsume) { this.onQuantifier(start, this.index, min, max, greedy); } @@ -70839,7 +70899,7 @@ class RegExpValidator { } } - if (!noError && this.strict) { + if (!noError && (this._uFlag || this.strict)) { this.raise("Incomplete quantifier"); } @@ -70849,11 +70909,11 @@ class RegExpValidator { return false; } - eatAtom() { - return this.eatPatternCharacter() || this.eatDot() || this.eatReverseSolidusAtomEscape() || this.eatCharacterClass() || this.eatUncapturingGroup() || this.eatCapturingGroup(); + consumeAtom() { + return this.consumePatternCharacter() || this.consumeDot() || this.consumeReverseSolidusAtomEscape() || this.consumeCharacterClass() || this.consumeUncapturingGroup() || this.consumeCapturingGroup(); } - eatDot() { + consumeDot() { if (this.eat(FullStop)) { this.onAnyCharacterSet(this.index - 1, this.index, "any"); return true; @@ -70862,11 +70922,11 @@ class RegExpValidator { return false; } - eatReverseSolidusAtomEscape() { + consumeReverseSolidusAtomEscape() { const start = this.index; if (this.eat(ReverseSolidus)) { - if (this.eatAtomEscape()) { + if (this.consumeAtomEscape()) { return true; } @@ -70876,12 +70936,12 @@ class RegExpValidator { return false; } - eatUncapturingGroup() { + consumeUncapturingGroup() { const start = this.index; if (this.eat3(LeftParenthesis, QuestionMark, Colon)) { this.onGroupEnter(start); - this.disjunction(); + this.consumeDisjunction(); if (!this.eat(RightParenthesis)) { this.raise("Unterminated group"); @@ -70894,21 +70954,22 @@ class RegExpValidator { return false; } - eatCapturingGroup() { + consumeCapturingGroup() { const start = this.index; if (this.eat(LeftParenthesis)) { - this._lastStrValue = ""; + let name = null; if (this.ecmaVersion >= 2018) { - this.groupSpecifier(); + if (this.consumeGroupSpecifier()) { + name = this._lastStrValue; + } } else if (this.currentCodePoint === QuestionMark) { this.raise("Invalid group"); } - const name = this._lastStrValue || null; this.onCapturingGroupEnter(start, name); - this.disjunction(); + this.consumeDisjunction(); if (!this.eat(RightParenthesis)) { this.raise("Unterminated group"); @@ -70921,22 +70982,24 @@ class RegExpValidator { return false; } - eatExtendedAtom() { - return this.eatDot() || this.eatReverseSolidusAtomEscape() || this.eatReverseSolidusFollowedByC() || this.eatCharacterClass() || this.eatUncapturingGroup() || this.eatCapturingGroup() || this.eatInvalidBracedQuantifier() || this.eatExtendedPatternCharacter(); + consumeExtendedAtom() { + return this.consumeDot() || this.consumeReverseSolidusAtomEscape() || this.consumeReverseSolidusFollowedByC() || this.consumeCharacterClass() || this.consumeUncapturingGroup() || this.consumeCapturingGroup() || this.consumeInvalidBracedQuantifier() || this.consumeExtendedPatternCharacter(); } - eatReverseSolidusFollowedByC() { + consumeReverseSolidusFollowedByC() { + const start = this.index; + if (this.currentCodePoint === ReverseSolidus && this.nextCodePoint === LatinSmallLetterC) { this._lastIntValue = this.currentCodePoint; this.advance(); - this.onCharacter(this.index - 1, this.index, ReverseSolidus); + this.onCharacter(start, this.index, ReverseSolidus); return true; } return false; } - eatInvalidBracedQuantifier() { + consumeInvalidBracedQuantifier() { if (this.eatBracedQuantifier(true)) { this.raise("Nothing to repeat"); } @@ -70944,17 +71007,7 @@ class RegExpValidator { return false; } - eatSyntaxCharacter() { - if (isSyntaxCharacter(this.currentCodePoint)) { - this._lastIntValue = this.currentCodePoint; - this.advance(); - return true; - } - - return false; - } - - eatPatternCharacter() { + consumePatternCharacter() { const start = this.index; const cp = this.currentCodePoint; @@ -70967,7 +71020,7 @@ class RegExpValidator { return false; } - eatExtendedPatternCharacter() { + consumeExtendedPatternCharacter() { const start = this.index; const cp = this.currentCodePoint; @@ -70980,15 +71033,13 @@ class RegExpValidator { return false; } - groupSpecifier() { - this._lastStrValue = ""; - + consumeGroupSpecifier() { if (this.eat(QuestionMark)) { if (this.eatGroupName()) { if (!this._groupNames.has(this._lastStrValue)) { this._groupNames.add(this._lastStrValue); - return; + return true; } this.raise("Duplicate capture group name"); @@ -70996,114 +71047,110 @@ class RegExpValidator { this.raise("Invalid group"); } - } - eatGroupName() { - this._lastStrValue = ""; + return false; + } - if (this.eat(LessThanSign)) { - if (this.eatRegExpIdentifierName() && this.eat(GreaterThanSign)) { - return true; - } + consumeAtomEscape() { + if (this.consumeBackreference() || this.consumeCharacterClassEscape() || this.consumeCharacterEscape() || this._nFlag && this.consumeKGroupName()) { + return true; + } - this.raise("Invalid capture group name"); + if (this.strict || this._uFlag) { + this.raise("Invalid escape"); } return false; } - eatRegExpIdentifierName() { - this._lastStrValue = ""; + consumeBackreference() { + const start = this.index; - if (this.eatRegExpIdentifierStart()) { - this._lastStrValue += String.fromCodePoint(this._lastIntValue); + if (this.eatDecimalEscape()) { + const n = this._lastIntValue; - while (this.eatRegExpIdentifierPart()) { - this._lastStrValue += String.fromCodePoint(this._lastIntValue); + if (n <= this._numCapturingParens) { + this.onBackreference(start - 1, this.index, n); + return true; } - return true; + if (this.strict || this._uFlag) { + this.raise("Invalid escape"); + } + + this.rewind(start); } return false; } - eatRegExpIdentifierStart() { + consumeCharacterClassEscape() { const start = this.index; - let cp = this.currentCodePoint; - this.advance(); - if (cp === ReverseSolidus && this.eatRegExpUnicodeEscapeSequence()) { - cp = this._lastIntValue; + if (this.eat(LatinSmallLetterD)) { + this._lastIntValue = -1; + this.onEscapeCharacterSet(start - 1, this.index, "digit", false); + return true; } - if (isRegExpIdentifierStart(cp)) { - this._lastIntValue = cp; + if (this.eat(LatinCapitalLetterD)) { + this._lastIntValue = -1; + this.onEscapeCharacterSet(start - 1, this.index, "digit", true); return true; } - if (this.index !== start) { - this.rewind(start); + if (this.eat(LatinSmallLetterS)) { + this._lastIntValue = -1; + this.onEscapeCharacterSet(start - 1, this.index, "space", false); + return true; } - return false; - } - - eatRegExpIdentifierPart() { - const start = this.index; - let cp = this.currentCodePoint; - this.advance(); - - if (cp === ReverseSolidus && this.eatRegExpUnicodeEscapeSequence()) { - cp = this._lastIntValue; + if (this.eat(LatinCapitalLetterS)) { + this._lastIntValue = -1; + this.onEscapeCharacterSet(start - 1, this.index, "space", true); + return true; } - if (isRegExpIdentifierPart(cp)) { - this._lastIntValue = cp; + if (this.eat(LatinSmallLetterW)) { + this._lastIntValue = -1; + this.onEscapeCharacterSet(start - 1, this.index, "word", false); return true; } - if (this.index !== start) { - this.rewind(start); + if (this.eat(LatinCapitalLetterW)) { + this._lastIntValue = -1; + this.onEscapeCharacterSet(start - 1, this.index, "word", true); + return true; } - return false; - } + let negate = false; - eatAtomEscape() { - if (this.eatBackreference() || this.eatCharacterClassEscape() || this.eatCharacterEscape() || this._nFlag && this.eatKGroupName()) { - return true; - } + if (this._uFlag && this.ecmaVersion >= 2018 && (this.eat(LatinSmallLetterP) || (negate = this.eat(LatinCapitalLetterP)))) { + this._lastIntValue = -1; - if (this.strict || this._uFlag) { - this.raise("Invalid escape"); + if (this.eat(LeftCurlyBracket) && this.eatUnicodePropertyValueExpression() && this.eat(RightCurlyBracket)) { + this.onUnicodePropertyCharacterSet(start - 1, this.index, "property", this._lastKeyValue, this._lastValValue || null, negate); + return true; + } + + this.raise("Invalid property name"); } return false; } - eatBackreference() { + consumeCharacterEscape() { const start = this.index; - if (this.eatDecimalEscape()) { - const n = this._lastIntValue; - - if (n <= this._numCapturingParens) { - this.onBackreference(start - 1, this.index, n); - return true; - } - - if (this.strict) { - this.raise("Invalid escape"); - } - - this.rewind(start); + if (this.eatControlEscape() || this.eatCControlLetter() || this.eatZero() || this.eatHexEscapeSequence() || this.eatRegExpUnicodeEscapeSequence() || !this.strict && !this._uFlag && this.eatLegacyOctalEscapeSequence() || this.eatIdentityEscape()) { + this.onCharacter(start - 1, this.index, this._lastIntValue); + return true; } return false; } - eatKGroupName() { + consumeKGroupName() { const start = this.index; if (this.eat(LatinSmallLetterK)) { @@ -71122,14 +71169,198 @@ class RegExpValidator { return false; } - eatCharacterEscape() { + consumeCharacterClass() { const start = this.index; - if (this.eatControlEscape() || this.eatCControlLetter() || this.eatZero() || this.eatHexEscapeSequence() || this.eatRegExpUnicodeEscapeSequence() || !this.strict && this.eatLegacyOctalEscapeSequence() || this.eatIdentityEscape()) { + if (this.eat(LeftSquareBracket)) { + const negate = this.eat(CircumflexAccent); + this.onCharacterClassEnter(start, negate); + this.consumeClassRanges(); + + if (!this.eat(RightSquareBracket)) { + this.raise("Unterminated character class"); + } + + this.onCharacterClassLeave(start, this.index, negate); + return true; + } + + return false; + } + + consumeClassRanges() { + const strict = this.strict || this._uFlag; + + for (;;) { + const rangeStart = this.index; + + if (!this.consumeClassAtom()) { + break; + } + + const min = this._lastIntValue; + + if (!this.eat(HyphenMinus)) { + continue; + } + + this.onCharacter(this.index - 1, this.index, HyphenMinus); + + if (!this.consumeClassAtom()) { + break; + } + + const max = this._lastIntValue; + + if (min === -1 || max === -1) { + if (strict) { + this.raise("Invalid character class"); + } + + continue; + } + + if (min > max) { + this.raise("Range out of order in character class"); + } + + this.onCharacterClassRange(rangeStart, this.index, min, max); + } + } + + consumeClassAtom() { + const start = this.index; + const cp = this.currentCodePoint; + + if (cp !== -1 && cp !== ReverseSolidus && cp !== RightSquareBracket) { + this.advance(); + this._lastIntValue = cp; + this.onCharacter(start, this.index, this._lastIntValue); + return true; + } + + if (this.eat(ReverseSolidus)) { + if (this.consumeClassEscape()) { + return true; + } + + if (!this.strict && this.currentCodePoint === LatinSmallLetterC) { + this._lastIntValue = ReverseSolidus; + this.onCharacter(start, this.index, this._lastIntValue); + return true; + } + + if (this.strict || this._uFlag) { + this.raise("Invalid escape"); + } + + this.rewind(start); + } + + return false; + } + + consumeClassEscape() { + const start = this.index; + + if (this.eat(LatinSmallLetterB)) { + this._lastIntValue = Backspace; + this.onCharacter(start - 1, this.index, this._lastIntValue); + return true; + } + + if (this._uFlag && this.eat(HyphenMinus)) { + this._lastIntValue = HyphenMinus; this.onCharacter(start - 1, this.index, this._lastIntValue); return true; } + let cp = 0; + + if (!this.strict && !this._uFlag && this.currentCodePoint === LatinSmallLetterC && (isDecimalDigit(cp = this.nextCodePoint) || cp === LowLine)) { + this.advance(); + this.advance(); + this._lastIntValue = cp % 0x20; + this.onCharacter(start - 1, this.index, this._lastIntValue); + return true; + } + + return this.consumeCharacterClassEscape() || this.consumeCharacterEscape(); + } + + eatGroupName() { + if (this.eat(LessThanSign)) { + if (this.eatRegExpIdentifierName() && this.eat(GreaterThanSign)) { + return true; + } + + this.raise("Invalid capture group name"); + } + + return false; + } + + eatRegExpIdentifierName() { + if (this.eatRegExpIdentifierStart()) { + this._lastStrValue = String.fromCodePoint(this._lastIntValue); + + while (this.eatRegExpIdentifierPart()) { + this._lastStrValue += String.fromCodePoint(this._lastIntValue); + } + + return true; + } + + return false; + } + + eatRegExpIdentifierStart() { + const start = this.index; + const forceUFlag = !this._uFlag && this.ecmaVersion >= 2020; + let cp = this.currentCodePoint; + this.advance(); + + if (cp === ReverseSolidus && this.eatRegExpUnicodeEscapeSequence(forceUFlag)) { + cp = this._lastIntValue; + } else if (forceUFlag && isLeadSurrogate(cp) && isTrailSurrogate(this.currentCodePoint)) { + cp = combineSurrogatePair(cp, this.currentCodePoint); + this.advance(); + } + + if (isRegExpIdentifierStart(cp)) { + this._lastIntValue = cp; + return true; + } + + if (this.index !== start) { + this.rewind(start); + } + + return false; + } + + eatRegExpIdentifierPart() { + const start = this.index; + const forceUFlag = !this._uFlag && this.ecmaVersion >= 2020; + let cp = this.currentCodePoint; + this.advance(); + + if (cp === ReverseSolidus && this.eatRegExpUnicodeEscapeSequence(forceUFlag)) { + cp = this._lastIntValue; + } else if (forceUFlag && isLeadSurrogate(cp) && isTrailSurrogate(this.currentCodePoint)) { + cp = combineSurrogatePair(cp, this.currentCodePoint); + this.advance(); + } + + if (isRegExpIdentifierPart(cp)) { + this._lastIntValue = cp; + return true; + } + + if (this.index !== start) { + this.rewind(start); + } + return false; } @@ -71158,8 +71389,8 @@ class RegExpValidator { } eatControlEscape() { - if (this.eat(LatinSmallLetterT)) { - this._lastIntValue = CharacterTabulation; + if (this.eat(LatinSmallLetterF)) { + this._lastIntValue = FormFeed; return true; } @@ -71168,18 +71399,18 @@ class RegExpValidator { return true; } - if (this.eat(LatinSmallLetterV)) { - this._lastIntValue = LineTabulation; + if (this.eat(LatinSmallLetterR)) { + this._lastIntValue = CarriageReturn; return true; } - if (this.eat(LatinSmallLetterF)) { - this._lastIntValue = FormFeed; + if (this.eat(LatinSmallLetterT)) { + this._lastIntValue = CharacterTabulation; return true; } - if (this.eat(LatinSmallLetterR)) { - this._lastIntValue = CarriageReturn; + if (this.eat(LatinSmallLetterV)) { + this._lastIntValue = LineTabulation; return true; } @@ -71198,38 +71429,38 @@ class RegExpValidator { return false; } - eatRegExpUnicodeEscapeSequence() { + eatRegExpUnicodeEscapeSequence(forceUFlag = false) { const start = this.index; + const uFlag = forceUFlag || this._uFlag; if (this.eat(LatinSmallLetterU)) { - if (this.eatFixedHexDigits(4)) { - const lead = this._lastIntValue; + if (uFlag && this.eatRegExpUnicodeSurrogatePairEscape() || this.eatFixedHexDigits(4) || uFlag && this.eatRegExpUnicodeCodePointEscape()) { + return true; + } - if (this._uFlag && lead >= 0xd800 && lead <= 0xdbff) { - const leadSurrogateEnd = this.index; + if (this.strict || uFlag) { + this.raise("Invalid unicode escape"); + } - if (this.eat(ReverseSolidus) && this.eat(LatinSmallLetterU) && this.eatFixedHexDigits(4)) { - const trail = this._lastIntValue; + this.rewind(start); + } - if (trail >= 0xdc00 && trail <= 0xdfff) { - this._lastIntValue = (lead - 0xd800) * 0x400 + (trail - 0xdc00) + 0x10000; - return true; - } - } + return false; + } - this.rewind(leadSurrogateEnd); - this._lastIntValue = lead; - } + eatRegExpUnicodeSurrogatePairEscape() { + const start = this.index; - return true; - } + if (this.eatFixedHexDigits(4)) { + const lead = this._lastIntValue; - if (this._uFlag && this.eat(LeftCurlyBracket) && this.eatHexDigits() && this.eat(RightCurlyBracket) && isValidUnicode(this._lastIntValue)) { - return true; - } + if (isLeadSurrogate(lead) && this.eat(ReverseSolidus) && this.eat(LatinSmallLetterU) && this.eatFixedHexDigits(4)) { + const trail = this._lastIntValue; - if (this.strict || this._uFlag) { - this.raise("Invalid unicode escape"); + if (isTrailSurrogate(trail)) { + this._lastIntValue = combineSurrogatePair(lead, trail); + return true; + } } this.rewind(start); @@ -71238,22 +71469,22 @@ class RegExpValidator { return false; } - eatIdentityEscape() { - if (this._uFlag) { - if (this.eatSyntaxCharacter()) { - return true; - } - - if (this.eat(Solidus)) { - this._lastIntValue = Solidus; - return true; - } + eatRegExpUnicodeCodePointEscape() { + const start = this.index; - return false; + if (this.eat(LeftCurlyBracket) && this.eatHexDigits() && this.eat(RightCurlyBracket) && isValidUnicode(this._lastIntValue)) { + return true; } - if (this.isValidIdentityEscape(this.currentCodePoint)) { - this._lastIntValue = this.currentCodePoint; + this.rewind(start); + return false; + } + + eatIdentityEscape() { + const cp = this.currentCodePoint; + + if (this.isValidIdentityEscape(cp)) { + this._lastIntValue = cp; this.advance(); return true; } @@ -71266,11 +71497,19 @@ class RegExpValidator { return false; } + if (this._uFlag) { + return isSyntaxCharacter(cp) || cp === Solidus; + } + if (this.strict) { return !isIdContinue(cp); } - return cp !== LatinSmallLetterC && (!this._nFlag || cp !== LatinSmallLetterK); + if (this._nFlag) { + return !(cp === LatinSmallLetterC || cp === LatinSmallLetterK); + } + + return cp !== LatinSmallLetterC; } eatDecimalEscape() { @@ -71289,61 +71528,6 @@ class RegExpValidator { return false; } - eatCharacterClassEscape() { - const start = this.index; - - if (this.eat(LatinSmallLetterD)) { - this._lastIntValue = -1; - this.onEscapeCharacterSet(start - 1, this.index, "digit", false); - return true; - } - - if (this.eat(LatinCapitalLetterD)) { - this._lastIntValue = -1; - this.onEscapeCharacterSet(start - 1, this.index, "digit", true); - return true; - } - - if (this.eat(LatinSmallLetterS)) { - this._lastIntValue = -1; - this.onEscapeCharacterSet(start - 1, this.index, "space", false); - return true; - } - - if (this.eat(LatinCapitalLetterS)) { - this._lastIntValue = -1; - this.onEscapeCharacterSet(start - 1, this.index, "space", true); - return true; - } - - if (this.eat(LatinSmallLetterW)) { - this._lastIntValue = -1; - this.onEscapeCharacterSet(start - 1, this.index, "word", false); - return true; - } - - if (this.eat(LatinCapitalLetterW)) { - this._lastIntValue = -1; - this.onEscapeCharacterSet(start - 1, this.index, "word", true); - return true; - } - - let negate = false; - - if (this._uFlag && this.ecmaVersion >= 2018 && (this.eat(LatinSmallLetterP) || (negate = this.eat(LatinCapitalLetterP)))) { - this._lastIntValue = -1; - - if (this.eat(LeftCurlyBracket) && this.eatUnicodePropertyValueExpression() && this.eat(RightCurlyBracket)) { - this.onUnicodePropertyCharacterSet(start - 1, this.index, "property", this._lastKeyValue, this._lastValValue || null, negate); - return true; - } - - this.raise("Invalid property name"); - } - - return false; - } - eatUnicodePropertyValueExpression() { const start = this.index; @@ -71410,120 +71594,6 @@ class RegExpValidator { return this.eatUnicodePropertyValue(); } - eatCharacterClass() { - const start = this.index; - - if (this.eat(LeftSquareBracket)) { - const negate = this.eat(CircumflexAccent); - this.onCharacterClassEnter(start, negate); - this.classRanges(); - - if (!this.eat(RightSquareBracket)) { - this.raise("Unterminated character class"); - } - - this.onCharacterClassLeave(start, this.index, negate); - return true; - } - - return false; - } - - classRanges() { - let start = this.index; - - while (this.eatClassAtom()) { - const left = this._lastIntValue; - const hyphenStart = this.index; - - if (this.eat(HyphenMinus)) { - this.onCharacter(hyphenStart, this.index, HyphenMinus); - - if (this.eatClassAtom()) { - const right = this._lastIntValue; - - if (left === -1 || right === -1) { - if (this.strict) { - this.raise("Invalid character class"); - } - } else if (left > right) { - this.raise("Range out of order in character class"); - } else { - this.onCharacterClassRange(start, this.index, left, right); - } - } - } - - start = this.index; - } - } - - eatClassAtom() { - const start = this.index; - - if (this.eat(ReverseSolidus)) { - if (this.eatClassEscape()) { - return true; - } - - if (this._uFlag) { - this.raise("Invalid escape"); - } - - this.rewind(start); - } - - const cp = this.currentCodePoint; - - if (cp !== -1 && cp !== RightSquareBracket) { - this.advance(); - this._lastIntValue = cp; - this.onCharacter(start, this.index, cp); - return true; - } - - return false; - } - - eatClassEscape() { - const start = this.index; - - if (this.eat(LatinSmallLetterB)) { - this._lastIntValue = Backspace; - this.onCharacter(start - 1, this.index, Backspace); - return true; - } - - if (this._uFlag && this.eat(HyphenMinus)) { - this._lastIntValue = HyphenMinus; - this.onCharacter(start - 1, this.index, HyphenMinus); - return true; - } - - if (!this._uFlag && this.eat(LatinSmallLetterC)) { - if (this.eatClassControlLetter()) { - this.onCharacter(start - 1, this.index, this._lastIntValue); - return true; - } - - this.rewind(start); - } - - return this.eatCharacterClassEscape() || this.eatCharacterEscape(); - } - - eatClassControlLetter() { - const cp = this.currentCodePoint; - - if (isDecimalDigit(cp) || cp === LowLine) { - this.advance(); - this._lastIntValue = cp % 0x20; - return true; - } - - return false; - } - eatHexEscapeSequence() { const start = this.index; @@ -71532,7 +71602,7 @@ class RegExpValidator { return true; } - if (this._uFlag) { + if (this._uFlag || this.strict) { this.raise("Invalid escape"); } @@ -73949,7 +74019,7 @@ module.exports = { if (allowed.indexOf(kind) === -1 && node.body.type === "BlockStatement" && node.body.body.length === 0 && innerComments.length === 0) { context.report({ node, - loc: node.body.loc.start, + loc: node.body.loc, messageId: "unexpected", data: { name @@ -77570,7 +77640,9 @@ module.exports = { tokensToIgnore.add(secondToken); } - if (hasExcessParens(node)) { + const hasExtraParens = node.parent.type === "ExportDefaultDeclaration" ? hasExcessParensWithPrecedence(node, PRECEDENCE_OF_ASSIGNMENT_EXPR) : hasExcessParens(node); + + if (hasExtraParens) { report(node); } } @@ -79424,9 +79496,16 @@ module.exports = { * @author Brandon Mills */ //------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = __webpack_require__(426); //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ + +const validParent = new Set(["Program", "ExportNamedDeclaration", "ExportDefaultDeclaration"]); +const validBlockStatementParent = new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]); module.exports = { meta: { type: "problem", @@ -79445,48 +79524,31 @@ module.exports = { }, create(context) { - /** - * Find the nearest Program or Function ancestor node. - * @returns {Object} Ancestor's type and distance from node. - */ - function nearestBody() { - const ancestors = context.getAncestors(); - let ancestor = ancestors.pop(), - generation = 1; - - while (ancestor && ["Program", "FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].indexOf(ancestor.type) < 0) { - generation += 1; - ancestor = ancestors.pop(); - } - - return { - // Type of containing ancestor - type: ancestor.type, - // Separation between ancestor and node - distance: generation - }; - } /** * Ensure that a given node is at a program or function body's root. * @param {ASTNode} node Declaration node to check. * @returns {void} */ - - function check(node) { - const body = nearestBody(), - valid = body.type === "Program" && body.distance === 1 || body.distance === 2; + const parent = node.parent; - if (!valid) { - context.report({ - node, - messageId: "moveDeclToRoot", - data: { - type: node.type === "FunctionDeclaration" ? "function" : "variable", - body: body.type === "Program" ? "program" : "function body" - } - }); + if (parent.type === "BlockStatement" && validBlockStatementParent.has(parent.parent.type)) { + return; + } + + if (validParent.has(parent.type)) { + return; } + + const upperFunction = astUtils.getUpperFunction(parent); + context.report({ + node, + messageId: "moveDeclToRoot", + data: { + type: node.type === "FunctionDeclaration" ? "function" : "variable", + body: upperFunction === null ? "program" : "function body" + } + }); } return { @@ -80353,7 +80415,7 @@ module.exports = { }); } /** - * Checks for any ocurrence of a BlockStatement in a place where lists of statements can appear + * Checks for any occurrence of a BlockStatement in a place where lists of statements can appear * @param {ASTNode} node The node to check * @returns {boolean} True if the node is a lone block. */ @@ -81433,6 +81495,8 @@ module.exports = { module.exports = { meta: { + deprecated: true, + replacedBy: ["node/no-mixed-requires"], type: "suggestion", docs: { description: "disallow `require` calls to be mixed with regular variable declarations", @@ -82510,9 +82574,14 @@ module.exports = { * @author Matt DuVall */ //------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = __webpack_require__(426); //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ + module.exports = { meta: { type: "suggestion", @@ -82531,6 +82600,12 @@ module.exports = { create(context) { return { NewExpression(node) { + const variable = astUtils.getVariableByName(context.getScope(), node.callee.name); + + if (variable && variable.identifiers.length > 0) { + return; + } + if (node.callee.name === "Object") { context.report({ node, @@ -82559,6 +82634,8 @@ module.exports = { module.exports = { meta: { + deprecated: true, + replacedBy: ["node/no-new-require"], type: "suggestion", docs: { description: "disallow `new` operators with calls to `require`", @@ -83125,6 +83202,8 @@ module.exports = { module.exports = { meta: { + deprecated: true, + replacedBy: ["node/no-path-concat"], type: "suggestion", docs: { description: "disallow string concatenation with `__dirname` and `__filename`", @@ -83277,6 +83356,8 @@ module.exports = { module.exports = { meta: { + deprecated: true, + replacedBy: ["node/no-process-env"], type: "suggestion", docs: { description: "disallow the use of `process.env`", @@ -83324,6 +83405,8 @@ module.exports = { module.exports = { meta: { + deprecated: true, + replacedBy: ["node/no-process-exit"], type: "suggestion", docs: { description: "disallow the use of `process.exit()`", @@ -84636,6 +84719,8 @@ const arrayOfStringsOrObjects = { }; module.exports = { meta: { + deprecated: true, + replacedBy: ["node/no-restricted-require"], type: "suggestion", docs: { description: "disallow specified modules when loaded by `require`", @@ -86251,6 +86336,8 @@ module.exports = { module.exports = { meta: { + deprecated: true, + replacedBy: ["node/no-sync"], type: "suggestion", docs: { description: "disallow synchronous methods", @@ -87490,11 +87577,8 @@ module.exports = { if (openParen.loc.start.line !== nodeExpressionEnd.loc.end.line) { context.report({ node, - loc: openParen.loc.start, - messageId, - data: { - char: openParen.value - } + loc: openParen.loc, + messageId }); } } //-------------------------------------------------------------------------- @@ -87512,22 +87596,25 @@ module.exports = { }, TaggedTemplateExpression(node) { - if (node.tag.loc.end.line === node.quasi.loc.start.line) { - return; - } // handle generics type parameters on template tags - + const { + quasi + } = node; // handles common tags, parenthesized tags, and typescript's generic type arguments - const tokenBefore = sourceCode.getTokenBefore(node.quasi); + const tokenBefore = sourceCode.getTokenBefore(quasi); - if (tokenBefore.loc.end.line === node.quasi.loc.start.line) { - return; + if (tokenBefore.loc.end.line !== quasi.loc.start.line) { + context.report({ + node, + loc: { + start: quasi.loc.start, + end: { + line: quasi.loc.start.line, + column: quasi.loc.start.column + 1 + } + }, + messageId: "taggedTemplate" + }); } - - context.report({ - node, - loc: node.loc.start, - messageId: "taggedTemplate" - }); }, CallExpression(node) { @@ -90380,7 +90467,7 @@ module.exports = { const operatorToken = sourceCode.getFirstTokenBetween(left, right, isConcatOperatorToken); context.report({ node, - loc: operatorToken.loc.start, + loc: operatorToken.loc, messageId: "unexpectedConcat" }); } @@ -101092,7 +101179,10 @@ module.exports = { if (hasSpacing && functionConfig === "never") { context.report({ node, - loc: leftToken.loc.end, + loc: { + start: leftToken.loc.end, + end: rightToken.loc.start + }, messageId: "unexpectedSpace", fix(fixer) { @@ -101109,7 +101199,7 @@ module.exports = { } else if (!hasSpacing && functionConfig === "always") { context.report({ node, - loc: leftToken.loc.end, + loc: rightToken.loc, messageId: "missingSpace", fix: fixer => fixer.insertTextAfter(leftToken, " ") }); @@ -106319,7 +106409,7 @@ module.exports = { /* 719 */ /***/ (function(module) { -module.exports = JSON.parse("{\"_from\":\"doctrine@^3.0.0\",\"_id\":\"doctrine@3.0.0\",\"_inBundle\":false,\"_integrity\":\"sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==\",\"_location\":\"/doctrine\",\"_phantomChildren\":{},\"_requested\":{\"type\":\"range\",\"registry\":true,\"raw\":\"doctrine@^3.0.0\",\"name\":\"doctrine\",\"escapedName\":\"doctrine\",\"rawSpec\":\"^3.0.0\",\"saveSpec\":null,\"fetchSpec\":\"^3.0.0\"},\"_requiredBy\":[\"/\",\"/eslint\"],\"_resolved\":\"https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz\",\"_shasum\":\"addebead72a6574db783639dc87a121773973961\",\"_spec\":\"doctrine@^3.0.0\",\"_where\":\"/home/dcsapak/git/pve-eslint/eslint-v7.0.0-alpha.3\",\"bugs\":{\"url\":\"https://github.com/eslint/doctrine/issues\"},\"bundleDependencies\":false,\"dependencies\":{\"esutils\":\"^2.0.2\"},\"deprecated\":false,\"description\":\"JSDoc parser\",\"devDependencies\":{\"coveralls\":\"^3.0.1\",\"dateformat\":\"^1.0.11\",\"eslint\":\"^1.10.3\",\"eslint-release\":\"^1.0.0\",\"linefix\":\"^0.1.1\",\"mocha\":\"^3.4.2\",\"npm-license\":\"^0.3.1\",\"nyc\":\"^10.3.2\",\"semver\":\"^5.0.3\",\"shelljs\":\"^0.5.3\",\"shelljs-nodecli\":\"^0.1.1\",\"should\":\"^5.0.1\"},\"directories\":{\"lib\":\"./lib\"},\"engines\":{\"node\":\">=6.0.0\"},\"files\":[\"lib\"],\"homepage\":\"https://github.com/eslint/doctrine\",\"license\":\"Apache-2.0\",\"main\":\"lib/doctrine.js\",\"maintainers\":[{\"name\":\"Nicholas C. Zakas\",\"email\":\"nicholas+npm@nczconsulting.com\",\"url\":\"https://www.nczonline.net\"},{\"name\":\"Yusuke Suzuki\",\"email\":\"utatane.tea@gmail.com\",\"url\":\"https://github.com/Constellation\"}],\"name\":\"doctrine\",\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/eslint/doctrine.git\"},\"scripts\":{\"coveralls\":\"nyc report --reporter=text-lcov | coveralls\",\"generate-alpharelease\":\"eslint-generate-prerelease alpha\",\"generate-betarelease\":\"eslint-generate-prerelease beta\",\"generate-rcrelease\":\"eslint-generate-prerelease rc\",\"generate-release\":\"eslint-generate-release\",\"lint\":\"eslint lib/\",\"pretest\":\"npm run lint\",\"publish-release\":\"eslint-publish-release\",\"test\":\"nyc mocha\"},\"version\":\"3.0.0\"}"); +module.exports = JSON.parse("{\"_from\":\"doctrine@^3.0.0\",\"_id\":\"doctrine@3.0.0\",\"_inBundle\":false,\"_integrity\":\"sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==\",\"_location\":\"/doctrine\",\"_phantomChildren\":{},\"_requested\":{\"type\":\"range\",\"registry\":true,\"raw\":\"doctrine@^3.0.0\",\"name\":\"doctrine\",\"escapedName\":\"doctrine\",\"rawSpec\":\"^3.0.0\",\"saveSpec\":null,\"fetchSpec\":\"^3.0.0\"},\"_requiredBy\":[\"/\",\"/eslint\"],\"_resolved\":\"https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz\",\"_shasum\":\"addebead72a6574db783639dc87a121773973961\",\"_spec\":\"doctrine@^3.0.0\",\"_where\":\"/home/dcsapak/git/pve-eslint/eslint-v7.0.0\",\"bugs\":{\"url\":\"https://github.com/eslint/doctrine/issues\"},\"bundleDependencies\":false,\"dependencies\":{\"esutils\":\"^2.0.2\"},\"deprecated\":false,\"description\":\"JSDoc parser\",\"devDependencies\":{\"coveralls\":\"^3.0.1\",\"dateformat\":\"^1.0.11\",\"eslint\":\"^1.10.3\",\"eslint-release\":\"^1.0.0\",\"linefix\":\"^0.1.1\",\"mocha\":\"^3.4.2\",\"npm-license\":\"^0.3.1\",\"nyc\":\"^10.3.2\",\"semver\":\"^5.0.3\",\"shelljs\":\"^0.5.3\",\"shelljs-nodecli\":\"^0.1.1\",\"should\":\"^5.0.1\"},\"directories\":{\"lib\":\"./lib\"},\"engines\":{\"node\":\">=6.0.0\"},\"files\":[\"lib\"],\"homepage\":\"https://github.com/eslint/doctrine\",\"license\":\"Apache-2.0\",\"main\":\"lib/doctrine.js\",\"maintainers\":[{\"name\":\"Nicholas C. Zakas\",\"email\":\"nicholas+npm@nczconsulting.com\",\"url\":\"https://www.nczonline.net\"},{\"name\":\"Yusuke Suzuki\",\"email\":\"utatane.tea@gmail.com\",\"url\":\"https://github.com/Constellation\"}],\"name\":\"doctrine\",\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/eslint/doctrine.git\"},\"scripts\":{\"coveralls\":\"nyc report --reporter=text-lcov | coveralls\",\"generate-alpharelease\":\"eslint-generate-prerelease alpha\",\"generate-betarelease\":\"eslint-generate-prerelease beta\",\"generate-rcrelease\":\"eslint-generate-prerelease rc\",\"generate-release\":\"eslint-generate-release\",\"lint\":\"eslint lib/\",\"pretest\":\"npm run lint\",\"publish-release\":\"eslint-publish-release\",\"test\":\"nyc mocha\"},\"version\":\"3.0.0\"}"); /***/ }), /* 720 */ @@ -107019,20 +107109,17 @@ function looksLikeLiteral(node) { /** * Attempts to derive a Literal node from nodes that are treated like literals. * @param {ASTNode} node Node to normalize. - * @param {number} [defaultValue] The default value to be returned if the node - * is not a Literal. * @returns {ASTNode} One of the following options. * 1. The original node if the node is already a Literal * 2. A normalized Literal node with the negative number as the value if the * node represents a negative number literal. * 3. A normalized Literal node with the string as the value if the node is * a Template Literal without expression. - * 4. The Literal node which has the `defaultValue` argument if it exists. - * 5. Otherwise `null`. + * 4. Otherwise `null`. */ -function getNormalizedLiteral(node, defaultValue) { +function getNormalizedLiteral(node) { if (node.type === "Literal") { return node; } @@ -107053,14 +107140,6 @@ function getNormalizedLiteral(node, defaultValue) { }; } - if (defaultValue) { - return { - type: "Literal", - value: defaultValue, - raw: String(defaultValue) - }; - } - return null; } /** @@ -107119,7 +107198,7 @@ module.exports = { meta: { type: "suggestion", docs: { - description: "require or disallow \"Yoda\" conditions", + description: 'require or disallow "Yoda" conditions', category: "Best Practices", recommended: false, url: "https://eslint.org/docs/rules/yoda" @@ -107172,8 +107251,24 @@ module.exports = { */ function isBetweenTest() { - let leftLiteral, rightLiteral; - return node.operator === "&&" && (leftLiteral = getNormalizedLiteral(left.left)) && (rightLiteral = getNormalizedLiteral(right.right, Number.POSITIVE_INFINITY)) && leftLiteral.value <= rightLiteral.value && same(left.right, right.left); + if (node.operator === "&&" && same(left.right, right.left)) { + const leftLiteral = getNormalizedLiteral(left.left); + const rightLiteral = getNormalizedLiteral(right.right); + + if (leftLiteral === null && rightLiteral === null) { + return false; + } + + if (rightLiteral === null || leftLiteral === null) { + return true; + } + + if (leftLiteral.value <= rightLiteral.value) { + return true; + } + } + + return false; } /** * Determines whether node is of the form `x < 0 || 1 <= x`. @@ -107182,8 +107277,24 @@ module.exports = { function isOutsideTest() { - let leftLiteral, rightLiteral; - return node.operator === "||" && (leftLiteral = getNormalizedLiteral(left.right, Number.NEGATIVE_INFINITY)) && (rightLiteral = getNormalizedLiteral(right.left)) && leftLiteral.value <= rightLiteral.value && same(left.left, right.right); + if (node.operator === "||" && same(left.left, right.right)) { + const leftLiteral = getNormalizedLiteral(left.right); + const rightLiteral = getNormalizedLiteral(right.left); + + if (leftLiteral === null && rightLiteral === null) { + return false; + } + + if (rightLiteral === null || leftLiteral === null) { + return true; + } + + if (leftLiteral.value <= rightLiteral.value) { + return true; + } + } + + return false; } /** * Determines whether node is wrapped in parentheses. @@ -120712,13 +120823,13 @@ module.exports = class ConfigCommentParser { /* 799 */ /***/ (function(module, exports, __webpack_require__) { -// Generated by LiveScript 1.4.0 +// Generated by LiveScript 1.6.0 (function () { var parseString, cast, parseType, VERSION, parsedTypeParse, parse; parseString = __webpack_require__(800); cast = __webpack_require__(807); parseType = __webpack_require__(808).parseType; - VERSION = '0.3.0'; + VERSION = '0.4.1'; parsedTypeParse = function parsedTypeParse(parsedType, string, options) { options == null && (options = {}); @@ -120742,7 +120853,7 @@ module.exports = class ConfigCommentParser { /* 800 */ /***/ (function(module, exports, __webpack_require__) { -// Generated by LiveScript 1.4.0 +// Generated by LiveScript 1.6.0 (function () { var reject, special, tokenRegex; reject = __webpack_require__(801).reject; @@ -120867,7 +120978,7 @@ module.exports = class ConfigCommentParser { options == null && (options = {}); if (!options.explicit && types.length === 1 && types[0].type === 'String') { - return "'" + string.replace(/\\'/g, "\\\\'") + "'"; + return string; } tokens = reject(not$, string.split(tokenRegex)); @@ -120889,7 +121000,7 @@ module.exports = class ConfigCommentParser { /* 801 */ /***/ (function(module, exports, __webpack_require__) { -// Generated by LiveScript 1.4.0 +// Generated by LiveScript 1.6.0 var Func, List, Obj, @@ -121066,7 +121177,7 @@ prelude.even = Num.even; prelude.odd = Num.odd; prelude.gcd = Num.gcd; prelude.lcm = Num.lcm; -prelude.VERSION = '1.1.2'; +prelude.VERSION = '1.2.1'; module.exports = prelude; function curry$(f, bound) { @@ -121086,14 +121197,13 @@ function curry$(f, bound) { /* 802 */ /***/ (function(module, exports) { -// Generated by LiveScript 1.4.0 +// Generated by LiveScript 1.6.0 var apply, curry, flip, fix, over, memoize, - slice$ = [].slice, toString$ = {}.toString; apply = curry$(function (f, list) { return f.apply(null, list); @@ -121127,8 +121237,14 @@ memoize = function memoize(f) { var memo; memo = {}; return function () { - var args, key, arg; - args = slice$.call(arguments); + var args, res$, i$, to$, key, arg; + res$ = []; + + for (i$ = 0, to$ = arguments.length; i$ < to$; ++i$) { + res$.push(arguments[i$]); + } + + args = res$; key = function () { var i$, @@ -121174,12 +121290,13 @@ function curry$(f, bound) { /* 803 */ /***/ (function(module, exports) { -// Generated by LiveScript 1.4.0 +// Generated by LiveScript 1.6.0 var each, map, compact, filter, reject, + remove, partition, find, head, @@ -121244,8 +121361,7 @@ var each, elemIndices, findIndex, findIndices, - toString$ = {}.toString, - slice$ = [].slice; + toString$ = {}.toString; each = curry$(function (f, xs) { var i$, len$, x; @@ -121320,6 +121436,17 @@ reject = curry$(function (f, xs) { return results$; }); +remove = curry$(function (el, xs) { + var i, x$; + i = elemIndex(el, xs); + x$ = xs.slice(); + + if (i != null) { + x$.splice(i, 1); + } + + return x$; +}); partition = curry$(function (f, xs) { var passed, failed, i$, len$, x; passed = []; @@ -121497,8 +121624,14 @@ _flatten = function flatten(xs) { }; difference = function difference(xs) { - var yss, results, i$, len$, x, j$, len1$, ys; - yss = slice$.call(arguments, 1); + var yss, res$, i$, to$, results, len$, x, j$, len1$, ys; + res$ = []; + + for (i$ = 1, to$ = arguments.length; i$ < to$; ++i$) { + res$.push(arguments[i$]); + } + + yss = res$; results = []; outer: for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { @@ -121519,8 +121652,14 @@ difference = function difference(xs) { }; intersection = function intersection(xs) { - var yss, results, i$, len$, x, j$, len1$, ys; - yss = slice$.call(arguments, 1); + var yss, res$, i$, to$, results, len$, x, j$, len1$, ys; + res$ = []; + + for (i$ = 1, to$ = arguments.length; i$ < to$; ++i$) { + res$.push(arguments[i$]); + } + + yss = res$; results = []; outer: for (i$ = 0, len$ = xs.length; i$ < len$; ++i$) { @@ -121541,8 +121680,14 @@ intersection = function intersection(xs) { }; union = function union() { - var xss, results, i$, len$, xs, j$, len1$, x; - xss = slice$.call(arguments); + var xss, res$, i$, to$, results, len$, xs, j$, len1$, x; + res$ = []; + + for (i$ = 0, to$ = arguments.length; i$ < to$; ++i$) { + res$.push(arguments[i$]); + } + + xss = res$; results = []; for (i$ = 0, len$ = xss.length; i$ < len$; ++i$) { @@ -121904,8 +122049,10 @@ zipWith = curry$(function (f, xs, ys) { zipAll = function zipAll() { var xss, - minLength, + res$, i$, + to$, + minLength, len$, xs, ref$, @@ -121913,7 +122060,13 @@ zipAll = function zipAll() { lresult$, j$, results$ = []; - xss = slice$.call(arguments); + res$ = []; + + for (i$ = 0, to$ = arguments.length; i$ < to$; ++i$) { + res$.push(arguments[i$]); + } + + xss = res$; minLength = undefined; for (i$ = 0, len$ = xss.length; i$ < len$; ++i$) { @@ -121938,14 +122091,22 @@ zipAll = function zipAll() { zipAllWith = function zipAllWith(f) { var xss, - minLength, + res$, i$, + to$, + minLength, len$, xs, ref$, i, results$ = []; - xss = slice$.call(arguments, 1); + res$ = []; + + for (i$ = 1, to$ = arguments.length; i$ < to$; ++i$) { + res$.push(arguments[i$]); + } + + xss = res$; minLength = undefined; for (i$ = 0, len$ = xss.length; i$ < len$; ++i$) { @@ -122048,6 +122209,7 @@ module.exports = { filter: filter, compact: compact, reject: reject, + remove: remove, partition: partition, find: find, head: head, @@ -122158,7 +122320,7 @@ function not$(x) { /* 804 */ /***/ (function(module, exports) { -// Generated by LiveScript 1.4.0 +// Generated by LiveScript 1.6.0 var values, keys, pairsToObj, objToPairs, listsToObj, objToLists, empty, each, map, compact, filter, reject, partition, find; values = function values(object) { @@ -122378,7 +122540,7 @@ function curry$(f, bound) { /* 805 */ /***/ (function(module, exports) { -// Generated by LiveScript 1.4.0 +// Generated by LiveScript 1.6.0 var split, join, lines, unlines, words, unwords, chars, unchars, reverse, repeat, capitalize, camelize, dasherize; split = curry$(function (sep, str) { return str.split(sep); @@ -122489,7 +122651,7 @@ function curry$(f, bound) { /* 806 */ /***/ (function(module, exports) { -// Generated by LiveScript 1.4.0 +// Generated by LiveScript 1.6.0 var max, min, negate, abs, signum, quot, rem, div, mod, recip, pi, tau, exp, sqrt, ln, pow, sin, tan, cos, asin, acos, atan, atan2, truncate, round, ceiling, floor, isItNaN, even, odd, gcd, lcm; max = curry$(function (x$, y$) { return x$ > y$ ? x$ : y$; @@ -122638,7 +122800,7 @@ function curry$(f, bound) { /* 807 */ /***/ (function(module, exports, __webpack_require__) { -// Generated by LiveScript 1.4.0 +// Generated by LiveScript 1.6.0 (function () { var parsedTypeCheck, types, @@ -122836,7 +122998,7 @@ function curry$(f, bound) { }, options); }, String: function String(it) { - var that; + var replace, that; if (toString$.call(it).slice(8, -1) !== 'String') { return { @@ -122844,15 +123006,48 @@ function curry$(f, bound) { }; } + replace = function replace(value, quote) { + return value.replace(/\\([^u]|u[0-9a-fA-F]{4})/g, function (all, escaped) { + switch (escaped[0]) { + case quote: + return quote; + + case '\\': + return '\\'; + + case 'b': + return '\b'; + + case 'f': + return '\f'; + + case 'n': + return '\n'; + + case 'r': + return '\r'; + + case 't': + return '\t'; + + case 'u': + return JSON.parse("\"" + all + "\""); + + default: + return escaped; + } + }); + }; + if (that = it.match(/^'([\s\S]*)'$/)) { return { type: 'Just', - value: that[1].replace(/\\'/g, "'") + value: replace(that[1], "'") }; } else if (that = it.match(/^"([\s\S]*)"$/)) { return { type: 'Just', - value: that[1].replace(/\\"/g, '"') + value: replace(that[1], '"') }; } else { return { @@ -123003,17 +123198,23 @@ function curry$(f, bound) { throw new Error("Value " + JSON.stringify(node) + " does not type check against " + JSON.stringify(types) + "."); } - module.exports = typesCast; + module.exports = function (node, types, options) { + if (!options.explicit && types.length === 1 && types[0].type === 'String') { + return node; + } + + return typesCast(node, types, options); + }; }).call(this); /***/ }), /* 808 */ /***/ (function(module, exports, __webpack_require__) { -// Generated by LiveScript 1.4.0 +// Generated by LiveScript 1.6.0 (function () { var VERSION, parseType, parsedTypeCheck, typeCheck; - VERSION = '0.3.2'; + VERSION = '0.4.0'; parseType = __webpack_require__(809); parsedTypeCheck = __webpack_require__(810); @@ -123033,7 +123234,7 @@ function curry$(f, bound) { /* 809 */ /***/ (function(module, exports) { -// Generated by LiveScript 1.4.0 +// Generated by LiveScript 1.6.0 (function () { var identifierRegex, tokenRegex; identifierRegex = /[\$\w]+/; @@ -123201,7 +123402,7 @@ function curry$(f, bound) { } function consumeTypes(tokens) { - var lookahead, types, typesSoFar, typeObj, type; + var lookahead, types, typesSoFar, typeObj, type, structure; if ('::' === peek(tokens)) { throw new Error("No comment before comment separator '::' found."); @@ -123231,13 +123432,15 @@ function curry$(f, bound) { } for (;;) { - typeObj = consumeType(tokens), type = typeObj.type; + typeObj = consumeType(tokens), type = typeObj.type, structure = typeObj.structure; if (!typesSoFar[type]) { types.push(typeObj); } - typesSoFar[type] = true; + if (structure == null) { + typesSoFar[type] = true; + } if (!maybeConsumeOp(tokens, '|')) { break; @@ -123284,7 +123487,7 @@ function curry$(f, bound) { /* 810 */ /***/ (function(module, exports, __webpack_require__) { -// Generated by LiveScript 1.4.0 +// Generated by LiveScript 1.6.0 (function () { var ref$, any, @@ -123292,7 +123495,6 @@ function curry$(f, bound) { isItNaN, types, defaultType, - customTypes, toString$ = {}.toString; ref$ = __webpack_require__(801), any = ref$.any, all = ref$.all, isItNaN = ref$.isItNaN; types = { @@ -123330,20 +123532,20 @@ function curry$(f, bound) { tuple: 'Array' }; - function checkArray(input, type) { + function checkArray(input, type, options) { return all(function (it) { - return checkMultiple(it, type.of); + return checkMultiple(it, type.of, options); }, input); } - function checkTuple(input, type) { + function checkTuple(input, type, options) { var i, i$, ref$, len$, types; i = 0; for (i$ = 0, len$ = (ref$ = type.of).length; i$ < len$; ++i$) { types = ref$[i$]; - if (!checkMultiple(input[i], types)) { + if (!checkMultiple(input[i], types, options)) { return false; } @@ -123353,7 +123555,7 @@ function curry$(f, bound) { return input.length <= i; } - function checkFields(input, type) { + function checkFields(input, type, options) { var inputKeys, numInputKeys, k, numOfKeys, key, ref$, types; inputKeys = {}; numInputKeys = 0; @@ -123368,7 +123570,7 @@ function curry$(f, bound) { for (key in ref$ = type.of) { types = ref$[key]; - if (!checkMultiple(input[key], types)) { + if (!checkMultiple(input[key], types, options)) { return false; } @@ -123380,24 +123582,24 @@ function curry$(f, bound) { return type.subset || numInputKeys === numOfKeys; } - function checkStructure(input, type) { + function checkStructure(input, type, options) { if (!(input instanceof Object)) { return false; } switch (type.structure) { case 'fields': - return checkFields(input, type); + return checkFields(input, type, options); case 'array': - return checkArray(input, type); + return checkArray(input, type, options); case 'tuple': - return checkTuple(input, type); + return checkTuple(input, type, options); } } - function check(input, typeObj) { + function check(input, typeObj, options) { var type, structure, setting, that; type = typeObj.type, structure = typeObj.structure; @@ -123406,12 +123608,12 @@ function curry$(f, bound) { return true; } - setting = customTypes[type] || types[type]; + setting = options.customTypes[type] || types[type]; if (setting) { - return setting.typeOf === toString$.call(input).slice(8, -1) && setting.validate(input); + return (setting.typeOf === void 8 || setting.typeOf === toString$.call(input).slice(8, -1)) && setting.validate(input); } else { - return type === toString$.call(input).slice(8, -1) && (!structure || checkStructure(input, typeObj)); + return type === toString$.call(input).slice(8, -1) && (!structure || checkStructure(input, typeObj, options)); } } else if (structure) { if (that = defaultType[structure]) { @@ -123420,26 +123622,30 @@ function curry$(f, bound) { } } - return checkStructure(input, typeObj); + return checkStructure(input, typeObj, options); } else { throw new Error("No type defined. Input: " + input + "."); } } - function checkMultiple(input, types) { + function checkMultiple(input, types, options) { if (toString$.call(types).slice(8, -1) !== 'Array') { throw new Error("Types must be in an array. Input: " + input + "."); } return any(function (it) { - return check(input, it); + return check(input, it, options); }, types); } module.exports = function (parsedType, input, options) { options == null && (options = {}); - customTypes = options.customTypes || {}; - return checkMultiple(input, parsedType); + + if (options.customTypes == null) { + options.customTypes = {}; + } + + return checkMultiple(input, parsedType, options); }; }).call(this); @@ -123770,17 +123976,79 @@ module.exports = NodeEventGenerator; }(this, function () { "use strict"; + function e(t) { + return (e = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (e) { + return typeof e; + } : function (e) { + return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e; + })(t); + } + + function t(e, t) { + return function (e) { + if (Array.isArray(e)) return e; + }(e) || function (e, t) { + if ("undefined" == typeof Symbol || !(Symbol.iterator in Object(e))) return; + var r = [], + n = !0, + o = !1, + a = void 0; + + try { + for (var s, i = e[Symbol.iterator](); !(n = (s = i.next()).done) && (r.push(s.value), !t || r.length !== t); n = !0); + } catch (e) { + o = !0, a = e; + } finally { + try { + n || null == i.return || i.return(); + } finally { + if (o) throw a; + } + } + + return r; + }(e, t) || n(e, t) || function () { + throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + }(); + } + + function r(e) { + return function (e) { + if (Array.isArray(e)) return o(e); + }(e) || function (e) { + if ("undefined" != typeof Symbol && Symbol.iterator in Object(e)) return Array.from(e); + }(e) || n(e) || function () { + throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + }(); + } + + function n(e, t) { + if (e) { + if ("string" == typeof e) return o(e, t); + var r = Object.prototype.toString.call(e).slice(8, -1); + return "Object" === r && e.constructor && (r = e.constructor.name), "Map" === r || "Set" === r ? Array.from(r) : "Arguments" === r || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r) ? o(e, t) : void 0; + } + } + + function o(e, t) { + (null == t || t > e.length) && (t = e.length); + + for (var r = 0, n = new Array(t); r < t; r++) n[r] = e[r]; + + return n; + } + "undefined" != typeof globalThis ? globalThis : "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self && self; - function e(e, t) { + function a(e, t) { return e(t = { exports: {} }, t.exports), t.exports; } - var t = e(function (e, t) { + var s = a(function (e, t) { !function e(t) { - var r, n, s, o, a, i; + var r, n, o, a, s, i; function l(e) { var t, @@ -123800,9 +124068,9 @@ module.exports = NodeEventGenerator; this.node = e, this.path = t, this.wrap = r, this.ref = n; } - function p() {} + function f() {} - function f(e) { + function p(e) { return null != e && "object" == typeof e && "string" == typeof e.type; } @@ -123811,17 +124079,23 @@ module.exports = NodeEventGenerator; } function d(e, t) { - return new p().traverse(e, t); + for (var r = e.length - 1; r >= 0; --r) if (e[r].node === t) return !0; + + return !1; } - function x(e, t) { + function y(e, t) { + return new f().traverse(e, t); + } + + function m(e, t) { var r; return r = function (e, t) { - var r, n, s, o; + var r, n, o, a; - for (n = e.length, s = 0; n;) t(e[o = s + (r = n >>> 1)]) ? n = r : (s = o + 1, n -= r + 1); + for (n = e.length, o = 0; n;) t(e[a = o + (r = n >>> 1)]) ? n = r : (o = a + 1, n -= r + 1); - return s; + return o; }(t, function (t) { return t.range[0] > e.range[0]; }), e.extendedRange = [e.range[0], e.range[1]], r !== t.length && (e.extendedRange[1] = t[r].range[0]), (r -= 1) >= 0 && (e.extendedRange[0] = t[r].range[1]), e; @@ -123900,7 +124174,7 @@ module.exports = NodeEventGenerator; WhileStatement: "WhileStatement", WithStatement: "WithStatement", YieldExpression: "YieldExpression" - }, s = { + }, o = { AssignmentExpression: ["left", "right"], AssignmentPattern: ["left", "right"], ArrayExpression: ["elements"], @@ -123974,120 +124248,123 @@ module.exports = NodeEventGenerator; WithStatement: ["object", "body"], YieldExpression: ["argument"] }, n = { - Break: o = {}, - Skip: a = {}, + Break: a = {}, + Skip: s = {}, Remove: i = {} }, u.prototype.replace = function (e) { this.parent[this.key] = e; }, u.prototype.remove = function () { return Array.isArray(this.parent) ? (this.parent.splice(this.key, 1), !0) : (this.replace(null), !1); - }, p.prototype.path = function () { - var e, t, r, n, s; + }, f.prototype.path = function () { + var e, t, r, n, o; - function o(e, t) { + function a(e, t) { if (Array.isArray(t)) for (r = 0, n = t.length; r < n; ++r) e.push(t[r]);else e.push(t); } if (!this.__current.path) return null; - for (s = [], e = 2, t = this.__leavelist.length; e < t; ++e) o(s, this.__leavelist[e].path); + for (o = [], e = 2, t = this.__leavelist.length; e < t; ++e) a(o, this.__leavelist[e].path); - return o(s, this.__current.path), s; - }, p.prototype.type = function () { + return a(o, this.__current.path), o; + }, f.prototype.type = function () { return this.current().type || this.__current.wrap; - }, p.prototype.parents = function () { + }, f.prototype.parents = function () { var e, t, r; for (r = [], e = 1, t = this.__leavelist.length; e < t; ++e) r.push(this.__leavelist[e].node); return r; - }, p.prototype.current = function () { + }, f.prototype.current = function () { return this.__current.node; - }, p.prototype.__execute = function (e, t) { + }, f.prototype.__execute = function (e, t) { var r, n; return n = void 0, r = this.__current, this.__current = t, this.__state = null, e && (n = e.call(this, t.node, this.__leavelist[this.__leavelist.length - 1].node)), this.__current = r, n; - }, p.prototype.notify = function (e) { + }, f.prototype.notify = function (e) { this.__state = e; - }, p.prototype.skip = function () { + }, f.prototype.skip = function () { + this.notify(s); + }, f.prototype.break = function () { this.notify(a); - }, p.prototype.break = function () { - this.notify(o); - }, p.prototype.remove = function () { + }, f.prototype.remove = function () { this.notify(i); - }, p.prototype.__initialize = function (e, t) { - this.visitor = t, this.root = e, this.__worklist = [], this.__leavelist = [], this.__current = null, this.__state = null, this.__fallback = null, "iteration" === t.fallback ? this.__fallback = Object.keys : "function" == typeof t.fallback && (this.__fallback = t.fallback), this.__keys = s, t.keys && (this.__keys = Object.assign(Object.create(this.__keys), t.keys)); - }, p.prototype.traverse = function (e, t) { - var r, n, s, i, l, u, p, d, x, m, y, g; + }, f.prototype.__initialize = function (e, t) { + this.visitor = t, this.root = e, this.__worklist = [], this.__leavelist = [], this.__current = null, this.__state = null, this.__fallback = null, "iteration" === t.fallback ? this.__fallback = Object.keys : "function" == typeof t.fallback && (this.__fallback = t.fallback), this.__keys = o, t.keys && (this.__keys = Object.assign(Object.create(this.__keys), t.keys)); + }, f.prototype.traverse = function (e, t) { + var r, n, o, i, l, u, f, y, m, x, g, v; - for (this.__initialize(e, t), g = {}, r = this.__worklist, n = this.__leavelist, r.push(new c(e, null, null, null)), n.push(new c(null, null, null, null)); r.length;) if ((s = r.pop()) !== g) { - if (s.node) { - if (u = this.__execute(t.enter, s), this.__state === o || u === o) return; - if (r.push(g), n.push(s), this.__state === a || u === a) continue; + for (this.__initialize(e, t), v = {}, r = this.__worklist, n = this.__leavelist, r.push(new c(e, null, null, null)), n.push(new c(null, null, null, null)); r.length;) if ((o = r.pop()) !== v) { + if (o.node) { + if (u = this.__execute(t.enter, o), this.__state === a || u === a) return; + if (r.push(v), n.push(o), this.__state === s || u === s) continue; - if (l = (i = s.node).type || s.wrap, !(m = this.__keys[l])) { + if (l = (i = o.node).type || o.wrap, !(x = this.__keys[l])) { if (!this.__fallback) throw new Error("Unknown node type " + l + "."); - m = this.__fallback(i); + x = this.__fallback(i); } - for (d = m.length; (d -= 1) >= 0;) if (y = i[p = m[d]]) if (Array.isArray(y)) { - for (x = y.length; (x -= 1) >= 0;) if (y[x]) { - if (h(l, m[d])) s = new c(y[x], [p, x], "Property", null);else { - if (!f(y[x])) continue; - s = new c(y[x], [p, x], null, null); + for (y = x.length; (y -= 1) >= 0;) if (g = i[f = x[y]]) if (Array.isArray(g)) { + for (m = g.length; (m -= 1) >= 0;) if (g[m] && !d(n, g[m])) { + if (h(l, x[y])) o = new c(g[m], [f, m], "Property", null);else { + if (!p(g[m])) continue; + o = new c(g[m], [f, m], null, null); } - r.push(s); + r.push(o); } - } else f(y) && r.push(new c(y, p, null, null)); + } else if (p(g)) { + if (d(n, g)) continue; + r.push(new c(g, f, null, null)); + } } - } else if (s = n.pop(), u = this.__execute(t.leave, s), this.__state === o || u === o) return; - }, p.prototype.replace = function (e, t) { - var r, n, s, l, p, d, x, m, y, g, v, A, E; + } else if (o = n.pop(), u = this.__execute(t.leave, o), this.__state === a || u === a) return; + }, f.prototype.replace = function (e, t) { + var r, n, o, l, f, d, y, m, x, g, v, A, b; - function _(e) { - var t, n, s, o; - if (e.ref.remove()) for (n = e.ref.key, o = e.ref.parent, t = r.length; t--;) if ((s = r[t]).ref && s.ref.parent === o) { - if (s.ref.key < n) break; - --s.ref.key; + function E(e) { + var t, n, o, a; + if (e.ref.remove()) for (n = e.ref.key, a = e.ref.parent, t = r.length; t--;) if ((o = r[t]).ref && o.ref.parent === a) { + if (o.ref.key < n) break; + --o.ref.key; } } for (this.__initialize(e, t), v = {}, r = this.__worklist, n = this.__leavelist, d = new c(e, null, null, new u(A = { root: e }, "root")), r.push(d), n.push(d); r.length;) if ((d = r.pop()) !== v) { - if (void 0 !== (p = this.__execute(t.enter, d)) && p !== o && p !== a && p !== i && (d.ref.replace(p), d.node = p), this.__state !== i && p !== i || (_(d), d.node = null), this.__state === o || p === o) return A.root; + if (void 0 !== (f = this.__execute(t.enter, d)) && f !== a && f !== s && f !== i && (d.ref.replace(f), d.node = f), this.__state !== i && f !== i || (E(d), d.node = null), this.__state === a || f === a) return A.root; - if ((s = d.node) && (r.push(v), n.push(d), this.__state !== a && p !== a)) { - if (l = s.type || d.wrap, !(y = this.__keys[l])) { + if ((o = d.node) && (r.push(v), n.push(d), this.__state !== s && f !== s)) { + if (l = o.type || d.wrap, !(x = this.__keys[l])) { if (!this.__fallback) throw new Error("Unknown node type " + l + "."); - y = this.__fallback(s); + x = this.__fallback(o); } - for (x = y.length; (x -= 1) >= 0;) if (g = s[E = y[x]]) if (Array.isArray(g)) { + for (y = x.length; (y -= 1) >= 0;) if (g = o[b = x[y]]) if (Array.isArray(g)) { for (m = g.length; (m -= 1) >= 0;) if (g[m]) { - if (h(l, y[x])) d = new c(g[m], [E, m], "Property", new u(g, m));else { - if (!f(g[m])) continue; - d = new c(g[m], [E, m], null, new u(g, m)); + if (h(l, x[y])) d = new c(g[m], [b, m], "Property", new u(g, m));else { + if (!p(g[m])) continue; + d = new c(g[m], [b, m], null, new u(g, m)); } r.push(d); } - } else f(g) && r.push(new c(g, E, null, new u(s, E))); + } else p(g) && r.push(new c(g, b, null, new u(o, b))); } - } else if (d = n.pop(), void 0 !== (p = this.__execute(t.leave, d)) && p !== o && p !== a && p !== i && d.ref.replace(p), this.__state !== i && p !== i || _(d), this.__state === o || p === o) return A.root; + } else if (d = n.pop(), void 0 !== (f = this.__execute(t.leave, d)) && f !== a && f !== s && f !== i && d.ref.replace(f), this.__state !== i && f !== i || E(d), this.__state === a || f === a) return A.root; return A.root; - }, t.Syntax = r, t.traverse = d, t.replace = function (e, t) { - return new p().replace(e, t); + }, t.Syntax = r, t.traverse = y, t.replace = function (e, t) { + return new f().replace(e, t); }, t.attachComments = function (e, t, r) { - var s, - o, + var o, a, + s, i, u = []; if (!e.range) throw new Error("attachComments needs range information"); if (!r.length) { if (t.length) { - for (a = 0, o = t.length; a < o; a += 1) (s = l(t[a])).extendedRange = [0, e.range[0]], u.push(s); + for (s = 0, a = t.length; s < a; s += 1) (o = l(t[s])).extendedRange = [0, e.range[0]], u.push(o); e.leadingComments = u; } @@ -124095,30 +124372,30 @@ module.exports = NodeEventGenerator; return e; } - for (a = 0, o = t.length; a < o; a += 1) u.push(x(l(t[a]), r)); + for (s = 0, a = t.length; s < a; s += 1) u.push(m(l(t[s]), r)); - return i = 0, d(e, { + return i = 0, y(e, { enter: function enter(e) { for (var t; i < u.length && !((t = u[i]).extendedRange[1] > e.range[0]);) t.extendedRange[1] === e.range[0] ? (e.leadingComments || (e.leadingComments = []), e.leadingComments.push(t), u.splice(i, 1)) : i += 1; return i === u.length ? n.Break : u[i].extendedRange[0] > e.range[1] ? n.Skip : void 0; } - }), i = 0, d(e, { + }), i = 0, y(e, { leave: function leave(e) { for (var t; i < u.length && (t = u[i], !(e.range[1] < t.extendedRange[0]));) e.range[1] === t.extendedRange[0] ? (e.trailingComments || (e.trailingComments = []), e.trailingComments.push(t), u.splice(i, 1)) : i += 1; return i === u.length ? n.Break : u[i].extendedRange[0] > e.range[1] ? n.Skip : void 0; } }), e; - }, t.VisitorKeys = s, t.VisitorOption = n, t.Controller = p, t.cloneEnvironment = function () { + }, t.VisitorKeys = o, t.VisitorOption = n, t.Controller = f, t.cloneEnvironment = function () { return e({}); }, t; }(t); }), - r = e(function (e) { + i = a(function (e) { e.exports && (e.exports = function () { - function e(t, r, n, s) { - this.message = t, this.expected = r, this.found = n, this.location = s, this.name = "SyntaxError", "function" == typeof Error.captureStackTrace && Error.captureStackTrace(this, e); + function e(t, r, n, o) { + this.message = t, this.expected = r, this.found = n, this.location = o, this.name = "SyntaxError", "function" == typeof Error.captureStackTrace && Error.captureStackTrace(this, e); } return function (e, t) { @@ -124130,13 +124407,13 @@ module.exports = NodeEventGenerator; }(e, Error), e.buildMessage = function (e, t) { var r = { literal: function literal(e) { - return '"' + s(e.text) + '"'; + return '"' + o(e.text) + '"'; }, class: function _class(e) { var t, r = ""; - for (t = 0; t < e.parts.length; t++) r += e.parts[t] instanceof Array ? o(e.parts[t][0]) + "-" + o(e.parts[t][1]) : o(e.parts[t]); + for (t = 0; t < e.parts.length; t++) r += e.parts[t] instanceof Array ? a(e.parts[t][0]) + "-" + a(e.parts[t][1]) : a(e.parts[t]); return "[" + (e.inverted ? "^" : "") + r + "]"; }, @@ -124155,7 +124432,7 @@ module.exports = NodeEventGenerator; return e.charCodeAt(0).toString(16).toUpperCase(); } - function s(e) { + function o(e) { return e.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\0/g, "\\0").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/[\x00-\x0F]/g, function (e) { return "\\x0" + n(e); }).replace(/[\x10-\x1F\x7F-\x9F]/g, function (e) { @@ -124163,7 +124440,7 @@ module.exports = NodeEventGenerator; }); } - function o(e) { + function a(e) { return e.replace(/\\/g, "\\\\").replace(/\]/g, "\\]").replace(/\^/g, "\\^").replace(/-/g, "\\-").replace(/\0/g, "\\0").replace(/\t/g, "\\t").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/[\x00-\x0F]/g, function (e) { return "\\x0" + n(e); }).replace(/[\x10-\x1F\x7F-\x9F]/g, function (e) { @@ -124174,29 +124451,29 @@ module.exports = NodeEventGenerator; return "Expected " + function (e) { var t, n, - s, - o = new Array(e.length); + o, + a = new Array(e.length); - for (t = 0; t < e.length; t++) o[t] = (s = e[t], r[s.type](s)); + for (t = 0; t < e.length; t++) a[t] = (o = e[t], r[o.type](o)); - if (o.sort(), o.length > 0) { - for (t = 1, n = 1; t < o.length; t++) o[t - 1] !== o[t] && (o[n] = o[t], n++); + if (a.sort(), a.length > 0) { + for (t = 1, n = 1; t < a.length; t++) a[t - 1] !== a[t] && (a[n] = a[t], n++); - o.length = n; + a.length = n; } - switch (o.length) { + switch (a.length) { case 1: - return o[0]; + return a[0]; case 2: - return o[0] + " or " + o[1]; + return a[0] + " or " + a[1]; default: - return o.slice(0, -1).join(", ") + ", or " + o[o.length - 1]; + return a.slice(0, -1).join(", ") + ", or " + a[a.length - 1]; } }(e) + " but " + function (e) { - return e ? '"' + s(e) + '"' : "end of input"; + return e ? '"' + o(e) + '"' : "end of input"; }(t) + " found."; }, { SyntaxError: e, @@ -124204,37 +124481,37 @@ module.exports = NodeEventGenerator; r = void 0 !== r ? r : {}; var n, - s, o, a, + s, i = {}, l = { - start: Ee + start: be }, - u = Ee, + u = be, c = me(" ", !1), - p = /^[^ [\],():#!=><~+.]/, - f = ye([" ", "[", "]", ",", "(", ")", ":", "#", "!", "=", ">", "<", "~", "+", "."], !0, !1), + f = /^[^ [\],():#!=><~+.]/, + p = xe([" ", "[", "]", ",", "(", ")", ":", "#", "!", "=", ">", "<", "~", "+", "."], !0, !1), h = function h(e) { return e.join(""); }, d = me(">", !1), - x = me("~", !1), + y = me("~", !1), m = me("+", !1), - y = me(",", !1), + x = me(",", !1), g = me("!", !1), v = me("*", !1), A = me("#", !1), - E = me("[", !1), - _ = me("]", !1), - b = /^[>", "<", "!"], !1, !1), - C = me("=", !1), - w = function w(e) { + b = me("[", !1), + E = me("]", !1), + S = /^[>", "<", "!"], !1, !1), + w = me("=", !1), + C = function C(e) { return (e || "") + "="; }, P = /^[><]/, - k = ye([">", "<"], !1, !1), + k = xe([">", "<"], !1, !1), D = me(".", !1), I = function I(e, t, r) { return { @@ -124246,7 +124523,7 @@ module.exports = NodeEventGenerator; }, j = me('"', !1), F = /^[^\\"]/, - T = ye(["\\", '"'], !0, !1), + T = xe(["\\", '"'], !0, !1), L = me("\\", !1), R = { type: "any" @@ -124286,39 +124563,39 @@ module.exports = NodeEventGenerator; }, M = me("'", !1), U = /^[^\\']/, - V = ye(["\\", "'"], !0, !1), + V = xe(["\\", "'"], !0, !1), q = /^[0-9]/, - N = ye([["0", "9"]], !1, !1), + N = xe([["0", "9"]], !1, !1), W = me("type(", !1), - $ = /^[^ )]/, - G = ye([" ", ")"], !0, !1), - z = me(")", !1), - K = /^[imsu]/, - H = ye(["i", "m", "s", "u"], !1, !1), - Y = me("/", !1), + G = /^[^ )]/, + z = xe([" ", ")"], !0, !1), + K = me(")", !1), + H = /^[imsu]/, + Y = xe(["i", "m", "s", "u"], !1, !1), + $ = me("/", !1), J = /^[^\/]/, - Q = ye(["/"], !0, !1), + Q = xe(["/"], !0, !1), X = me(":not(", !1), Z = me(":matches(", !1), ee = me(":has(", !1), te = me(":first-child", !1), re = me(":last-child", !1), ne = me(":nth-child(", !1), - se = me(":nth-last-child(", !1), - oe = me(":", !1), - ae = me("statement", !0), + oe = me(":nth-last-child(", !1), + ae = me(":", !1), + se = me("statement", !0), ie = me("expression", !0), le = me("declaration", !0), ue = me("function", !0), ce = me("pattern", !0), - pe = 0, - fe = [{ + fe = 0, + pe = [{ line: 1, column: 1 }], he = 0, de = [], - xe = {}; + ye = {}; if ("startRule" in r) { if (!(r.startRule in l)) throw new Error("Can't start parsing from rule \"" + r.startRule + '".'); @@ -124333,7 +124610,7 @@ module.exports = NodeEventGenerator; }; } - function ye(e, t, r) { + function xe(e, t, r) { return { type: "class", parts: e, @@ -124344,17 +124621,17 @@ module.exports = NodeEventGenerator; function ge(e) { var r, - n = fe[e]; + n = pe[e]; if (n) return n; - for (r = e - 1; !fe[r];) r--; + for (r = e - 1; !pe[r];) r--; for (n = { - line: (n = fe[r]).line, + line: (n = pe[r]).line, column: n.column }; r < e;) 10 === t.charCodeAt(r) ? (n.line++, n.column = 1) : n.column++, r++; - return fe[e] = n, n; + return pe[e] = n, n; } function ve(e, t) { @@ -124375,119 +124652,119 @@ module.exports = NodeEventGenerator; } function Ae(e) { - pe < he || (pe > he && (he = pe, de = []), de.push(e)); + fe < he || (fe > he && (he = fe, de = []), de.push(e)); } - function Ee() { + function be() { var e, t, r, n, - s = 30 * pe + 0, - o = xe[s]; - return o ? (pe = o.nextPos, o.result) : (e = pe, (t = _e()) !== i && (r = Ce()) !== i && _e() !== i ? e = t = 1 === (n = r).length ? n[0] : { + o = 30 * fe + 0, + a = ye[o]; + return a ? (fe = a.nextPos, a.result) : (e = fe, (t = Ee()) !== i && (r = we()) !== i && Ee() !== i ? e = t = 1 === (n = r).length ? n[0] : { type: "matches", selectors: n - } : (pe = e, e = i), e === i && (e = pe, (t = _e()) !== i && (t = void 0), e = t), xe[s] = { - nextPos: pe, + } : (fe = e, e = i), e === i && (e = fe, (t = Ee()) !== i && (t = void 0), e = t), ye[o] = { + nextPos: fe, result: e }, e); } - function _e() { + function Ee() { var e, r, - n = 30 * pe + 1, - s = xe[n]; - if (s) return pe = s.nextPos, s.result; + n = 30 * fe + 1, + o = ye[n]; + if (o) return fe = o.nextPos, o.result; - for (e = [], 32 === t.charCodeAt(pe) ? (r = " ", pe++) : (r = i, Ae(c)); r !== i;) e.push(r), 32 === t.charCodeAt(pe) ? (r = " ", pe++) : (r = i, Ae(c)); + for (e = [], 32 === t.charCodeAt(fe) ? (r = " ", fe++) : (r = i, Ae(c)); r !== i;) e.push(r), 32 === t.charCodeAt(fe) ? (r = " ", fe++) : (r = i, Ae(c)); - return xe[n] = { - nextPos: pe, + return ye[n] = { + nextPos: fe, result: e }, e; } - function be() { + function Se() { var e, r, n, - s = 30 * pe + 2, - o = xe[s]; - if (o) return pe = o.nextPos, o.result; - if (r = [], p.test(t.charAt(pe)) ? (n = t.charAt(pe), pe++) : (n = i, Ae(f)), n !== i) for (; n !== i;) r.push(n), p.test(t.charAt(pe)) ? (n = t.charAt(pe), pe++) : (n = i, Ae(f));else r = i; - return r !== i && (r = h(r)), e = r, xe[s] = { - nextPos: pe, + o = 30 * fe + 2, + a = ye[o]; + if (a) return fe = a.nextPos, a.result; + if (r = [], f.test(t.charAt(fe)) ? (n = t.charAt(fe), fe++) : (n = i, Ae(p)), n !== i) for (; n !== i;) r.push(n), f.test(t.charAt(fe)) ? (n = t.charAt(fe), fe++) : (n = i, Ae(p));else r = i; + return r !== i && (r = h(r)), e = r, ye[o] = { + nextPos: fe, result: e }, e; } - function Se() { + function _e() { var e, r, n, - s = 30 * pe + 3, - o = xe[s]; - return o ? (pe = o.nextPos, o.result) : (e = pe, (r = _e()) !== i ? (62 === t.charCodeAt(pe) ? (n = ">", pe++) : (n = i, Ae(d)), n !== i && _e() !== i ? e = r = "child" : (pe = e, e = i)) : (pe = e, e = i), e === i && (e = pe, (r = _e()) !== i ? (126 === t.charCodeAt(pe) ? (n = "~", pe++) : (n = i, Ae(x)), n !== i && _e() !== i ? e = r = "sibling" : (pe = e, e = i)) : (pe = e, e = i), e === i && (e = pe, (r = _e()) !== i ? (43 === t.charCodeAt(pe) ? (n = "+", pe++) : (n = i, Ae(m)), n !== i && _e() !== i ? e = r = "adjacent" : (pe = e, e = i)) : (pe = e, e = i), e === i && (e = pe, 32 === t.charCodeAt(pe) ? (r = " ", pe++) : (r = i, Ae(c)), r !== i && (n = _e()) !== i ? e = r = "descendant" : (pe = e, e = i)))), xe[s] = { - nextPos: pe, + o = 30 * fe + 3, + a = ye[o]; + return a ? (fe = a.nextPos, a.result) : (e = fe, (r = Ee()) !== i ? (62 === t.charCodeAt(fe) ? (n = ">", fe++) : (n = i, Ae(d)), n !== i && Ee() !== i ? e = r = "child" : (fe = e, e = i)) : (fe = e, e = i), e === i && (e = fe, (r = Ee()) !== i ? (126 === t.charCodeAt(fe) ? (n = "~", fe++) : (n = i, Ae(y)), n !== i && Ee() !== i ? e = r = "sibling" : (fe = e, e = i)) : (fe = e, e = i), e === i && (e = fe, (r = Ee()) !== i ? (43 === t.charCodeAt(fe) ? (n = "+", fe++) : (n = i, Ae(m)), n !== i && Ee() !== i ? e = r = "adjacent" : (fe = e, e = i)) : (fe = e, e = i), e === i && (e = fe, 32 === t.charCodeAt(fe) ? (r = " ", fe++) : (r = i, Ae(c)), r !== i && (n = Ee()) !== i ? e = r = "descendant" : (fe = e, e = i)))), ye[o] = { + nextPos: fe, result: e }, e); } - function Ce() { + function we() { var e, r, n, - s, o, a, + s, l, u, - c = 30 * pe + 4, - p = xe[c]; - if (p) return pe = p.nextPos, p.result; + c = 30 * fe + 4, + f = ye[c]; + if (f) return fe = f.nextPos, f.result; - if (e = pe, (r = we()) !== i) { - for (n = [], s = pe, (o = _e()) !== i ? (44 === t.charCodeAt(pe) ? (a = ",", pe++) : (a = i, Ae(y)), a !== i && (l = _e()) !== i && (u = we()) !== i ? s = o = [o, a, l, u] : (pe = s, s = i)) : (pe = s, s = i); s !== i;) n.push(s), s = pe, (o = _e()) !== i ? (44 === t.charCodeAt(pe) ? (a = ",", pe++) : (a = i, Ae(y)), a !== i && (l = _e()) !== i && (u = we()) !== i ? s = o = [o, a, l, u] : (pe = s, s = i)) : (pe = s, s = i); + if (e = fe, (r = Ce()) !== i) { + for (n = [], o = fe, (a = Ee()) !== i ? (44 === t.charCodeAt(fe) ? (s = ",", fe++) : (s = i, Ae(x)), s !== i && (l = Ee()) !== i && (u = Ce()) !== i ? o = a = [a, s, l, u] : (fe = o, o = i)) : (fe = o, o = i); o !== i;) n.push(o), o = fe, (a = Ee()) !== i ? (44 === t.charCodeAt(fe) ? (s = ",", fe++) : (s = i, Ae(x)), s !== i && (l = Ee()) !== i && (u = Ce()) !== i ? o = a = [a, s, l, u] : (fe = o, o = i)) : (fe = o, o = i); n !== i ? e = r = [r].concat(n.map(function (e) { return e[3]; - })) : (pe = e, e = i); - } else pe = e, e = i; + })) : (fe = e, e = i); + } else fe = e, e = i; - return xe[c] = { - nextPos: pe, + return ye[c] = { + nextPos: fe, result: e }, e; } - function we() { + function Ce() { var e, t, r, n, - s, o, a, - l = 30 * pe + 5, - u = xe[l]; - if (u) return pe = u.nextPos, u.result; + s, + l = 30 * fe + 5, + u = ye[l]; + if (u) return fe = u.nextPos, u.result; - if (e = pe, (t = Pe()) !== i) { - for (r = [], n = pe, (s = Se()) !== i && (o = Pe()) !== i ? n = s = [s, o] : (pe = n, n = i); n !== i;) r.push(n), n = pe, (s = Se()) !== i && (o = Pe()) !== i ? n = s = [s, o] : (pe = n, n = i); + if (e = fe, (t = Pe()) !== i) { + for (r = [], n = fe, (o = _e()) !== i && (a = Pe()) !== i ? n = o = [o, a] : (fe = n, n = i); n !== i;) r.push(n), n = fe, (o = _e()) !== i && (a = Pe()) !== i ? n = o = [o, a] : (fe = n, n = i); - r !== i ? (a = t, e = t = r.reduce(function (e, t) { + r !== i ? (s = t, e = t = r.reduce(function (e, t) { return { type: t[0], left: e, right: t[1] }; - }, a)) : (pe = e, e = i); - } else pe = e, e = i; + }, s)) : (fe = e, e = i); + } else fe = e, e = i; - return xe[l] = { - nextPos: pe, + return ye[l] = { + nextPos: fe, result: e }, e; } @@ -124496,363 +124773,363 @@ module.exports = NodeEventGenerator; var e, r, n, + o, + a, s, - o = 30 * pe + 6, - a = xe[o]; - if (a) return pe = a.nextPos, a.result; - - if (e = pe, 33 === t.charCodeAt(pe) ? (r = "!", pe++) : (r = i, Ae(g)), r === i && (r = null), r !== i) { - if (n = [], (s = ke()) !== i) for (; s !== i;) n.push(s), s = ke();else n = i; - n !== i ? e = r = function (e, t) { - const r = 1 === t.length ? t[0] : { - type: "compound", - selectors: t - }; - return e && (r.subject = !0), r; - }(r, n) : (pe = e, e = i); - } else pe = e, e = i; - - return xe[o] = { - nextPos: pe, + l, + u = 30 * fe + 6, + c = ye[u]; + if (c) return fe = c.nextPos, c.result; + + if (e = fe, 33 === t.charCodeAt(fe) ? (r = "!", fe++) : (r = i, Ae(g)), r === i && (r = null), r !== i) { + if (n = [], (o = ke()) !== i) for (; o !== i;) n.push(o), o = ke();else n = i; + n !== i ? (a = r, l = 1 === (s = n).length ? s[0] : { + type: "compound", + selectors: s + }, a && (l.subject = !0), e = r = l) : (fe = e, e = i); + } else fe = e, e = i; + + return ye[u] = { + nextPos: fe, result: e }, e; } function ke() { var e, - r = 30 * pe + 7, - n = xe[r]; - return n ? (pe = n.nextPos, n.result) : ((e = function () { + r = 30 * fe + 7, + n = ye[r]; + return n ? (fe = n.nextPos, n.result) : ((e = function () { var e, r, - n = 30 * pe + 8, - s = xe[n]; - return s ? (pe = s.nextPos, s.result) : (42 === t.charCodeAt(pe) ? (r = "*", pe++) : (r = i, Ae(v)), r !== i && (r = { + n = 30 * fe + 8, + o = ye[n]; + return o ? (fe = o.nextPos, o.result) : (42 === t.charCodeAt(fe) ? (r = "*", fe++) : (r = i, Ae(v)), r !== i && (r = { type: "wildcard", value: r - }), e = r, xe[n] = { - nextPos: pe, + }), e = r, ye[n] = { + nextPos: fe, result: e }, e); }()) === i && (e = function () { var e, r, n, - s = 30 * pe + 9, - o = xe[s]; - return o ? (pe = o.nextPos, o.result) : (e = pe, 35 === t.charCodeAt(pe) ? (r = "#", pe++) : (r = i, Ae(A)), r === i && (r = null), r !== i && (n = be()) !== i ? e = r = { + o = 30 * fe + 9, + a = ye[o]; + return a ? (fe = a.nextPos, a.result) : (e = fe, 35 === t.charCodeAt(fe) ? (r = "#", fe++) : (r = i, Ae(A)), r === i && (r = null), r !== i && (n = Se()) !== i ? e = r = { type: "identifier", value: n - } : (pe = e, e = i), xe[s] = { - nextPos: pe, + } : (fe = e, e = i), ye[o] = { + nextPos: fe, result: e }, e); }()) === i && (e = function () { var e, r, n, - s, - o = 30 * pe + 10, - a = xe[o]; - return a ? (pe = a.nextPos, a.result) : (e = pe, 91 === t.charCodeAt(pe) ? (r = "[", pe++) : (r = i, Ae(E)), r !== i && _e() !== i && (n = function () { + o, + a = 30 * fe + 10, + s = ye[a]; + return s ? (fe = s.nextPos, s.result) : (e = fe, 91 === t.charCodeAt(fe) ? (r = "[", fe++) : (r = i, Ae(b)), r !== i && Ee() !== i && (n = function () { var e, r, n, - s, - o = 30 * pe + 14, - a = xe[o]; - return a ? (pe = a.nextPos, a.result) : (e = pe, (r = De()) !== i && _e() !== i && (n = function () { + o, + a = 30 * fe + 14, + s = ye[a]; + return s ? (fe = s.nextPos, s.result) : (e = fe, (r = De()) !== i && Ee() !== i && (n = function () { var e, r, n, - s = 30 * pe + 12, - o = xe[s]; - return o ? (pe = o.nextPos, o.result) : (e = pe, 33 === t.charCodeAt(pe) ? (r = "!", pe++) : (r = i, Ae(g)), r === i && (r = null), r !== i ? (61 === t.charCodeAt(pe) ? (n = "=", pe++) : (n = i, Ae(C)), n !== i ? (r = w(r), e = r) : (pe = e, e = i)) : (pe = e, e = i), xe[s] = { - nextPos: pe, + o = 30 * fe + 12, + a = ye[o]; + return a ? (fe = a.nextPos, a.result) : (e = fe, 33 === t.charCodeAt(fe) ? (r = "!", fe++) : (r = i, Ae(g)), r === i && (r = null), r !== i ? (61 === t.charCodeAt(fe) ? (n = "=", fe++) : (n = i, Ae(w)), n !== i ? (r = C(r), e = r) : (fe = e, e = i)) : (fe = e, e = i), ye[o] = { + nextPos: fe, result: e }, e); - }()) !== i && _e() !== i ? ((s = function () { + }()) !== i && Ee() !== i ? ((o = function () { var e, r, n, - s, o, - a = 30 * pe + 18, - l = xe[a]; - if (l) return pe = l.nextPos, l.result; - if (e = pe, "type(" === t.substr(pe, 5) ? (r = "type(", pe += 5) : (r = i, Ae(W)), r !== i) { - if (_e() !== i) { - if (n = [], $.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(G)), s !== i) for (; s !== i;) n.push(s), $.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(G));else n = i; - n !== i && (s = _e()) !== i ? (41 === t.charCodeAt(pe) ? (o = ")", pe++) : (o = i, Ae(z)), o !== i ? (r = { + a, + s = 30 * fe + 18, + l = ye[s]; + if (l) return fe = l.nextPos, l.result; + if (e = fe, "type(" === t.substr(fe, 5) ? (r = "type(", fe += 5) : (r = i, Ae(W)), r !== i) { + if (Ee() !== i) { + if (n = [], G.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(z)), o !== i) for (; o !== i;) n.push(o), G.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(z));else n = i; + n !== i && (o = Ee()) !== i ? (41 === t.charCodeAt(fe) ? (a = ")", fe++) : (a = i, Ae(K)), a !== i ? (r = { type: "type", value: n.join("") - }, e = r) : (pe = e, e = i)) : (pe = e, e = i); - } else pe = e, e = i; - } else pe = e, e = i; - return xe[a] = { - nextPos: pe, + }, e = r) : (fe = e, e = i)) : (fe = e, e = i); + } else fe = e, e = i; + } else fe = e, e = i; + return ye[s] = { + nextPos: fe, result: e }, e; - }()) === i && (s = function () { + }()) === i && (o = function () { var e, r, n, - s, o, a, - l = 30 * pe + 20, - u = xe[l]; - if (u) return pe = u.nextPos, u.result; + s, + l = 30 * fe + 20, + u = ye[l]; + if (u) return fe = u.nextPos, u.result; - if (e = pe, 47 === t.charCodeAt(pe) ? (r = "/", pe++) : (r = i, Ae(Y)), r !== i) { - if (n = [], J.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(Q)), s !== i) for (; s !== i;) n.push(s), J.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(Q));else n = i; - n !== i ? (47 === t.charCodeAt(pe) ? (s = "/", pe++) : (s = i, Ae(Y)), s !== i ? ((o = function () { + if (e = fe, 47 === t.charCodeAt(fe) ? (r = "/", fe++) : (r = i, Ae($)), r !== i) { + if (n = [], J.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(Q)), o !== i) for (; o !== i;) n.push(o), J.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(Q));else n = i; + n !== i ? (47 === t.charCodeAt(fe) ? (o = "/", fe++) : (o = i, Ae($)), o !== i ? ((a = function () { var e, r, - n = 30 * pe + 19, - s = xe[n]; - if (s) return pe = s.nextPos, s.result; - if (e = [], K.test(t.charAt(pe)) ? (r = t.charAt(pe), pe++) : (r = i, Ae(H)), r !== i) for (; r !== i;) e.push(r), K.test(t.charAt(pe)) ? (r = t.charAt(pe), pe++) : (r = i, Ae(H));else e = i; - return xe[n] = { - nextPos: pe, + n = 30 * fe + 19, + o = ye[n]; + if (o) return fe = o.nextPos, o.result; + if (e = [], H.test(t.charAt(fe)) ? (r = t.charAt(fe), fe++) : (r = i, Ae(Y)), r !== i) for (; r !== i;) e.push(r), H.test(t.charAt(fe)) ? (r = t.charAt(fe), fe++) : (r = i, Ae(Y));else e = i; + return ye[n] = { + nextPos: fe, result: e }, e; - }()) === i && (o = null), o !== i ? (a = o, r = { + }()) === i && (a = null), a !== i ? (s = a, r = { type: "regexp", - value: new RegExp(n.join(""), a ? a.join("") : "") - }, e = r) : (pe = e, e = i)) : (pe = e, e = i)) : (pe = e, e = i); - } else pe = e, e = i; + value: new RegExp(n.join(""), s ? s.join("") : "") + }, e = r) : (fe = e, e = i)) : (fe = e, e = i)) : (fe = e, e = i); + } else fe = e, e = i; - return xe[l] = { - nextPos: pe, + return ye[l] = { + nextPos: fe, result: e }, e; - }()), s !== i ? (r = I(r, n, s), e = r) : (pe = e, e = i)) : (pe = e, e = i), e === i && (e = pe, (r = De()) !== i && _e() !== i && (n = function () { + }()), o !== i ? (r = I(r, n, o), e = r) : (fe = e, e = i)) : (fe = e, e = i), e === i && (e = fe, (r = De()) !== i && Ee() !== i && (n = function () { var e, r, n, - s = 30 * pe + 11, - o = xe[s]; - return o ? (pe = o.nextPos, o.result) : (e = pe, b.test(t.charAt(pe)) ? (r = t.charAt(pe), pe++) : (r = i, Ae(S)), r === i && (r = null), r !== i ? (61 === t.charCodeAt(pe) ? (n = "=", pe++) : (n = i, Ae(C)), n !== i ? (r = w(r), e = r) : (pe = e, e = i)) : (pe = e, e = i), e === i && (P.test(t.charAt(pe)) ? (e = t.charAt(pe), pe++) : (e = i, Ae(k))), xe[s] = { - nextPos: pe, + o = 30 * fe + 11, + a = ye[o]; + return a ? (fe = a.nextPos, a.result) : (e = fe, S.test(t.charAt(fe)) ? (r = t.charAt(fe), fe++) : (r = i, Ae(_)), r === i && (r = null), r !== i ? (61 === t.charCodeAt(fe) ? (n = "=", fe++) : (n = i, Ae(w)), n !== i ? (r = C(r), e = r) : (fe = e, e = i)) : (fe = e, e = i), e === i && (P.test(t.charAt(fe)) ? (e = t.charAt(fe), fe++) : (e = i, Ae(k))), ye[o] = { + nextPos: fe, result: e }, e); - }()) !== i && _e() !== i ? ((s = function () { + }()) !== i && Ee() !== i ? ((o = function () { var e, r, n, - s, o, a, - l = 30 * pe + 15, - u = xe[l]; - if (u) return pe = u.nextPos, u.result; + s, + l = 30 * fe + 15, + u = ye[l]; + if (u) return fe = u.nextPos, u.result; - if (e = pe, 34 === t.charCodeAt(pe) ? (r = '"', pe++) : (r = i, Ae(j)), r !== i) { - for (n = [], F.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(T)), s === i && (s = pe, 92 === t.charCodeAt(pe) ? (o = "\\", pe++) : (o = i, Ae(L)), o !== i ? (t.length > pe ? (a = t.charAt(pe), pe++) : (a = i, Ae(R)), a !== i ? (o = O(o, a), s = o) : (pe = s, s = i)) : (pe = s, s = i)); s !== i;) n.push(s), F.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(T)), s === i && (s = pe, 92 === t.charCodeAt(pe) ? (o = "\\", pe++) : (o = i, Ae(L)), o !== i ? (t.length > pe ? (a = t.charAt(pe), pe++) : (a = i, Ae(R)), a !== i ? (o = O(o, a), s = o) : (pe = s, s = i)) : (pe = s, s = i)); + if (e = fe, 34 === t.charCodeAt(fe) ? (r = '"', fe++) : (r = i, Ae(j)), r !== i) { + for (n = [], F.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(T)), o === i && (o = fe, 92 === t.charCodeAt(fe) ? (a = "\\", fe++) : (a = i, Ae(L)), a !== i ? (t.length > fe ? (s = t.charAt(fe), fe++) : (s = i, Ae(R)), s !== i ? (a = O(a, s), o = a) : (fe = o, o = i)) : (fe = o, o = i)); o !== i;) n.push(o), F.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(T)), o === i && (o = fe, 92 === t.charCodeAt(fe) ? (a = "\\", fe++) : (a = i, Ae(L)), a !== i ? (t.length > fe ? (s = t.charAt(fe), fe++) : (s = i, Ae(R)), s !== i ? (a = O(a, s), o = a) : (fe = o, o = i)) : (fe = o, o = i)); - n !== i ? (34 === t.charCodeAt(pe) ? (s = '"', pe++) : (s = i, Ae(j)), s !== i ? (r = B(n), e = r) : (pe = e, e = i)) : (pe = e, e = i); - } else pe = e, e = i; + n !== i ? (34 === t.charCodeAt(fe) ? (o = '"', fe++) : (o = i, Ae(j)), o !== i ? (r = B(n), e = r) : (fe = e, e = i)) : (fe = e, e = i); + } else fe = e, e = i; - if (e === i) if (e = pe, 39 === t.charCodeAt(pe) ? (r = "'", pe++) : (r = i, Ae(M)), r !== i) { - for (n = [], U.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(V)), s === i && (s = pe, 92 === t.charCodeAt(pe) ? (o = "\\", pe++) : (o = i, Ae(L)), o !== i ? (t.length > pe ? (a = t.charAt(pe), pe++) : (a = i, Ae(R)), a !== i ? (o = O(o, a), s = o) : (pe = s, s = i)) : (pe = s, s = i)); s !== i;) n.push(s), U.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(V)), s === i && (s = pe, 92 === t.charCodeAt(pe) ? (o = "\\", pe++) : (o = i, Ae(L)), o !== i ? (t.length > pe ? (a = t.charAt(pe), pe++) : (a = i, Ae(R)), a !== i ? (o = O(o, a), s = o) : (pe = s, s = i)) : (pe = s, s = i)); + if (e === i) if (e = fe, 39 === t.charCodeAt(fe) ? (r = "'", fe++) : (r = i, Ae(M)), r !== i) { + for (n = [], U.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(V)), o === i && (o = fe, 92 === t.charCodeAt(fe) ? (a = "\\", fe++) : (a = i, Ae(L)), a !== i ? (t.length > fe ? (s = t.charAt(fe), fe++) : (s = i, Ae(R)), s !== i ? (a = O(a, s), o = a) : (fe = o, o = i)) : (fe = o, o = i)); o !== i;) n.push(o), U.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(V)), o === i && (o = fe, 92 === t.charCodeAt(fe) ? (a = "\\", fe++) : (a = i, Ae(L)), a !== i ? (t.length > fe ? (s = t.charAt(fe), fe++) : (s = i, Ae(R)), s !== i ? (a = O(a, s), o = a) : (fe = o, o = i)) : (fe = o, o = i)); - n !== i ? (39 === t.charCodeAt(pe) ? (s = "'", pe++) : (s = i, Ae(M)), s !== i ? (r = B(n), e = r) : (pe = e, e = i)) : (pe = e, e = i); - } else pe = e, e = i; - return xe[l] = { - nextPos: pe, + n !== i ? (39 === t.charCodeAt(fe) ? (o = "'", fe++) : (o = i, Ae(M)), o !== i ? (r = B(n), e = r) : (fe = e, e = i)) : (fe = e, e = i); + } else fe = e, e = i; + return ye[l] = { + nextPos: fe, result: e }, e; - }()) === i && (s = function () { + }()) === i && (o = function () { var e, r, n, + o, + a, s, - o = 30 * pe + 16, - a = xe[o]; - if (a) return pe = a.nextPos, a.result; - - for (e = pe, r = pe, n = [], q.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(N)); s !== i;) n.push(s), q.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(N)); - - if (n !== i ? (46 === t.charCodeAt(pe) ? (s = ".", pe++) : (s = i, Ae(D)), s !== i ? r = n = [n, s] : (pe = r, r = i)) : (pe = r, r = i), r === i && (r = null), r !== i) { - if (n = [], q.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(N)), s !== i) for (; s !== i;) n.push(s), q.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(N));else n = i; - n !== i ? (r = function (e, t) { - const r = e ? [].concat.apply([], e).join("") : ""; - return { - type: "literal", - value: parseFloat(r + t.join("")) - }; - }(r, n), e = r) : (pe = e, e = i); - } else pe = e, e = i; - - return xe[o] = { - nextPos: pe, + l, + u = 30 * fe + 16, + c = ye[u]; + if (c) return fe = c.nextPos, c.result; + + for (e = fe, r = fe, n = [], q.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(N)); o !== i;) n.push(o), q.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(N)); + + if (n !== i ? (46 === t.charCodeAt(fe) ? (o = ".", fe++) : (o = i, Ae(D)), o !== i ? r = n = [n, o] : (fe = r, r = i)) : (fe = r, r = i), r === i && (r = null), r !== i) { + if (n = [], q.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(N)), o !== i) for (; o !== i;) n.push(o), q.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(N));else n = i; + n !== i ? (s = n, l = (a = r) ? [].concat.apply([], a).join("") : "", r = { + type: "literal", + value: parseFloat(l + s.join("")) + }, e = r) : (fe = e, e = i); + } else fe = e, e = i; + + return ye[u] = { + nextPos: fe, result: e }, e; - }()) === i && (s = function () { + }()) === i && (o = function () { var e, t, - r = 30 * pe + 17, - n = xe[r]; - return n ? (pe = n.nextPos, n.result) : ((t = be()) !== i && (t = { + r = 30 * fe + 17, + n = ye[r]; + return n ? (fe = n.nextPos, n.result) : ((t = Se()) !== i && (t = { type: "literal", value: t - }), e = t, xe[r] = { - nextPos: pe, + }), e = t, ye[r] = { + nextPos: fe, result: e }, e); - }()), s !== i ? (r = I(r, n, s), e = r) : (pe = e, e = i)) : (pe = e, e = i), e === i && (e = pe, (r = De()) !== i && (r = { + }()), o !== i ? (r = I(r, n, o), e = r) : (fe = e, e = i)) : (fe = e, e = i), e === i && (e = fe, (r = De()) !== i && (r = { type: "attribute", name: r - }), e = r)), xe[o] = { - nextPos: pe, + }), e = r)), ye[a] = { + nextPos: fe, result: e }, e); - }()) !== i && _e() !== i ? (93 === t.charCodeAt(pe) ? (s = "]", pe++) : (s = i, Ae(_)), s !== i ? e = r = n : (pe = e, e = i)) : (pe = e, e = i), xe[o] = { - nextPos: pe, + }()) !== i && Ee() !== i ? (93 === t.charCodeAt(fe) ? (o = "]", fe++) : (o = i, Ae(E)), o !== i ? e = r = n : (fe = e, e = i)) : (fe = e, e = i), ye[a] = { + nextPos: fe, result: e }, e); }()) === i && (e = function () { var e, r, n, - s, o, a, + s, l, u, - c = 30 * pe + 21, - p = xe[c]; - if (p) return pe = p.nextPos, p.result; - if (e = pe, 46 === t.charCodeAt(pe) ? (r = ".", pe++) : (r = i, Ae(D)), r !== i) { - if ((n = be()) !== i) { - for (s = [], o = pe, 46 === t.charCodeAt(pe) ? (a = ".", pe++) : (a = i, Ae(D)), a !== i && (l = be()) !== i ? o = a = [a, l] : (pe = o, o = i); o !== i;) s.push(o), o = pe, 46 === t.charCodeAt(pe) ? (a = ".", pe++) : (a = i, Ae(D)), a !== i && (l = be()) !== i ? o = a = [a, l] : (pe = o, o = i); - - s !== i ? (u = n, r = { + c = 30 * fe + 21, + f = ye[c]; + if (f) return fe = f.nextPos, f.result; + if (e = fe, 46 === t.charCodeAt(fe) ? (r = ".", fe++) : (r = i, Ae(D)), r !== i) { + if ((n = Se()) !== i) { + for (o = [], a = fe, 46 === t.charCodeAt(fe) ? (s = ".", fe++) : (s = i, Ae(D)), s !== i && (l = Se()) !== i ? a = s = [s, l] : (fe = a, a = i); a !== i;) o.push(a), a = fe, 46 === t.charCodeAt(fe) ? (s = ".", fe++) : (s = i, Ae(D)), s !== i && (l = Se()) !== i ? a = s = [s, l] : (fe = a, a = i); + + o !== i ? (u = n, r = { type: "field", - name: s.reduce(function (e, t) { + name: o.reduce(function (e, t) { return e + t[0] + t[1]; }, u) - }, e = r) : (pe = e, e = i); - } else pe = e, e = i; - } else pe = e, e = i; - return xe[c] = { - nextPos: pe, + }, e = r) : (fe = e, e = i); + } else fe = e, e = i; + } else fe = e, e = i; + return ye[c] = { + nextPos: fe, result: e }, e; }()) === i && (e = function () { var e, r, n, - s, - o = 30 * pe + 22, - a = xe[o]; - return a ? (pe = a.nextPos, a.result) : (e = pe, ":not(" === t.substr(pe, 5) ? (r = ":not(", pe += 5) : (r = i, Ae(X)), r !== i && _e() !== i && (n = Ce()) !== i && _e() !== i ? (41 === t.charCodeAt(pe) ? (s = ")", pe++) : (s = i, Ae(z)), s !== i ? e = r = { + o, + a = 30 * fe + 22, + s = ye[a]; + return s ? (fe = s.nextPos, s.result) : (e = fe, ":not(" === t.substr(fe, 5) ? (r = ":not(", fe += 5) : (r = i, Ae(X)), r !== i && Ee() !== i && (n = we()) !== i && Ee() !== i ? (41 === t.charCodeAt(fe) ? (o = ")", fe++) : (o = i, Ae(K)), o !== i ? e = r = { type: "not", selectors: n - } : (pe = e, e = i)) : (pe = e, e = i), xe[o] = { - nextPos: pe, + } : (fe = e, e = i)) : (fe = e, e = i), ye[a] = { + nextPos: fe, result: e }, e); }()) === i && (e = function () { var e, r, n, - s, - o = 30 * pe + 23, - a = xe[o]; - return a ? (pe = a.nextPos, a.result) : (e = pe, ":matches(" === t.substr(pe, 9) ? (r = ":matches(", pe += 9) : (r = i, Ae(Z)), r !== i && _e() !== i && (n = Ce()) !== i && _e() !== i ? (41 === t.charCodeAt(pe) ? (s = ")", pe++) : (s = i, Ae(z)), s !== i ? e = r = { + o, + a = 30 * fe + 23, + s = ye[a]; + return s ? (fe = s.nextPos, s.result) : (e = fe, ":matches(" === t.substr(fe, 9) ? (r = ":matches(", fe += 9) : (r = i, Ae(Z)), r !== i && Ee() !== i && (n = we()) !== i && Ee() !== i ? (41 === t.charCodeAt(fe) ? (o = ")", fe++) : (o = i, Ae(K)), o !== i ? e = r = { type: "matches", selectors: n - } : (pe = e, e = i)) : (pe = e, e = i), xe[o] = { - nextPos: pe, + } : (fe = e, e = i)) : (fe = e, e = i), ye[a] = { + nextPos: fe, result: e }, e); }()) === i && (e = function () { var e, r, n, - s, - o = 30 * pe + 24, - a = xe[o]; - return a ? (pe = a.nextPos, a.result) : (e = pe, ":has(" === t.substr(pe, 5) ? (r = ":has(", pe += 5) : (r = i, Ae(ee)), r !== i && _e() !== i && (n = Ce()) !== i && _e() !== i ? (41 === t.charCodeAt(pe) ? (s = ")", pe++) : (s = i, Ae(z)), s !== i ? e = r = { + o, + a = 30 * fe + 24, + s = ye[a]; + return s ? (fe = s.nextPos, s.result) : (e = fe, ":has(" === t.substr(fe, 5) ? (r = ":has(", fe += 5) : (r = i, Ae(ee)), r !== i && Ee() !== i && (n = we()) !== i && Ee() !== i ? (41 === t.charCodeAt(fe) ? (o = ")", fe++) : (o = i, Ae(K)), o !== i ? e = r = { type: "has", selectors: n - } : (pe = e, e = i)) : (pe = e, e = i), xe[o] = { - nextPos: pe, + } : (fe = e, e = i)) : (fe = e, e = i), ye[a] = { + nextPos: fe, result: e }, e); }()) === i && (e = function () { var e, r, - n = 30 * pe + 25, - s = xe[n]; - return s ? (pe = s.nextPos, s.result) : (":first-child" === t.substr(pe, 12) ? (r = ":first-child", pe += 12) : (r = i, Ae(te)), r !== i && (r = Ie(1)), e = r, xe[n] = { - nextPos: pe, + n = 30 * fe + 25, + o = ye[n]; + return o ? (fe = o.nextPos, o.result) : (":first-child" === t.substr(fe, 12) ? (r = ":first-child", fe += 12) : (r = i, Ae(te)), r !== i && (r = Ie(1)), e = r, ye[n] = { + nextPos: fe, result: e }, e); }()) === i && (e = function () { var e, r, - n = 30 * pe + 26, - s = xe[n]; - return s ? (pe = s.nextPos, s.result) : (":last-child" === t.substr(pe, 11) ? (r = ":last-child", pe += 11) : (r = i, Ae(re)), r !== i && (r = je(1)), e = r, xe[n] = { - nextPos: pe, + n = 30 * fe + 26, + o = ye[n]; + return o ? (fe = o.nextPos, o.result) : (":last-child" === t.substr(fe, 11) ? (r = ":last-child", fe += 11) : (r = i, Ae(re)), r !== i && (r = je(1)), e = r, ye[n] = { + nextPos: fe, result: e }, e); }()) === i && (e = function () { var e, r, n, - s, o, - a = 30 * pe + 27, - l = xe[a]; - if (l) return pe = l.nextPos, l.result; - if (e = pe, ":nth-child(" === t.substr(pe, 11) ? (r = ":nth-child(", pe += 11) : (r = i, Ae(ne)), r !== i) { - if (_e() !== i) { - if (n = [], q.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(N)), s !== i) for (; s !== i;) n.push(s), q.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(N));else n = i; - n !== i && (s = _e()) !== i ? (41 === t.charCodeAt(pe) ? (o = ")", pe++) : (o = i, Ae(z)), o !== i ? (r = Ie(parseInt(n.join(""), 10)), e = r) : (pe = e, e = i)) : (pe = e, e = i); - } else pe = e, e = i; - } else pe = e, e = i; - return xe[a] = { - nextPos: pe, + a, + s = 30 * fe + 27, + l = ye[s]; + if (l) return fe = l.nextPos, l.result; + if (e = fe, ":nth-child(" === t.substr(fe, 11) ? (r = ":nth-child(", fe += 11) : (r = i, Ae(ne)), r !== i) { + if (Ee() !== i) { + if (n = [], q.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(N)), o !== i) for (; o !== i;) n.push(o), q.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(N));else n = i; + n !== i && (o = Ee()) !== i ? (41 === t.charCodeAt(fe) ? (a = ")", fe++) : (a = i, Ae(K)), a !== i ? (r = Ie(parseInt(n.join(""), 10)), e = r) : (fe = e, e = i)) : (fe = e, e = i); + } else fe = e, e = i; + } else fe = e, e = i; + return ye[s] = { + nextPos: fe, result: e }, e; }()) === i && (e = function () { var e, r, n, - s, o, - a = 30 * pe + 28, - l = xe[a]; - if (l) return pe = l.nextPos, l.result; - if (e = pe, ":nth-last-child(" === t.substr(pe, 16) ? (r = ":nth-last-child(", pe += 16) : (r = i, Ae(se)), r !== i) { - if (_e() !== i) { - if (n = [], q.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(N)), s !== i) for (; s !== i;) n.push(s), q.test(t.charAt(pe)) ? (s = t.charAt(pe), pe++) : (s = i, Ae(N));else n = i; - n !== i && (s = _e()) !== i ? (41 === t.charCodeAt(pe) ? (o = ")", pe++) : (o = i, Ae(z)), o !== i ? (r = je(parseInt(n.join(""), 10)), e = r) : (pe = e, e = i)) : (pe = e, e = i); - } else pe = e, e = i; - } else pe = e, e = i; - return xe[a] = { - nextPos: pe, + a, + s = 30 * fe + 28, + l = ye[s]; + if (l) return fe = l.nextPos, l.result; + if (e = fe, ":nth-last-child(" === t.substr(fe, 16) ? (r = ":nth-last-child(", fe += 16) : (r = i, Ae(oe)), r !== i) { + if (Ee() !== i) { + if (n = [], q.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(N)), o !== i) for (; o !== i;) n.push(o), q.test(t.charAt(fe)) ? (o = t.charAt(fe), fe++) : (o = i, Ae(N));else n = i; + n !== i && (o = Ee()) !== i ? (41 === t.charCodeAt(fe) ? (a = ")", fe++) : (a = i, Ae(K)), a !== i ? (r = je(parseInt(n.join(""), 10)), e = r) : (fe = e, e = i)) : (fe = e, e = i); + } else fe = e, e = i; + } else fe = e, e = i; + return ye[s] = { + nextPos: fe, result: e }, e; }()) === i && (e = function () { var e, r, n, - s = 30 * pe + 29, - o = xe[s]; - return o ? (pe = o.nextPos, o.result) : (e = pe, 58 === t.charCodeAt(pe) ? (r = ":", pe++) : (r = i, Ae(oe)), r !== i ? ("statement" === t.substr(pe, 9).toLowerCase() ? (n = t.substr(pe, 9), pe += 9) : (n = i, Ae(ae)), n === i && ("expression" === t.substr(pe, 10).toLowerCase() ? (n = t.substr(pe, 10), pe += 10) : (n = i, Ae(ie)), n === i && ("declaration" === t.substr(pe, 11).toLowerCase() ? (n = t.substr(pe, 11), pe += 11) : (n = i, Ae(le)), n === i && ("function" === t.substr(pe, 8).toLowerCase() ? (n = t.substr(pe, 8), pe += 8) : (n = i, Ae(ue)), n === i && ("pattern" === t.substr(pe, 7).toLowerCase() ? (n = t.substr(pe, 7), pe += 7) : (n = i, Ae(ce)))))), n !== i ? e = r = { + o = 30 * fe + 29, + a = ye[o]; + return a ? (fe = a.nextPos, a.result) : (e = fe, 58 === t.charCodeAt(fe) ? (r = ":", fe++) : (r = i, Ae(ae)), r !== i ? ("statement" === t.substr(fe, 9).toLowerCase() ? (n = t.substr(fe, 9), fe += 9) : (n = i, Ae(se)), n === i && ("expression" === t.substr(fe, 10).toLowerCase() ? (n = t.substr(fe, 10), fe += 10) : (n = i, Ae(ie)), n === i && ("declaration" === t.substr(fe, 11).toLowerCase() ? (n = t.substr(fe, 11), fe += 11) : (n = i, Ae(le)), n === i && ("function" === t.substr(fe, 8).toLowerCase() ? (n = t.substr(fe, 8), fe += 8) : (n = i, Ae(ue)), n === i && ("pattern" === t.substr(fe, 7).toLowerCase() ? (n = t.substr(fe, 7), fe += 7) : (n = i, Ae(ce)))))), n !== i ? e = r = { type: "class", name: n - } : (pe = e, e = i)) : (pe = e, e = i), xe[s] = { - nextPos: pe, + } : (fe = e, e = i)) : (fe = e, e = i), ye[o] = { + nextPos: fe, result: e }, e); - }()), xe[r] = { - nextPos: pe, + }()), ye[r] = { + nextPos: fe, result: e }, e); } @@ -124861,12 +125138,12 @@ module.exports = NodeEventGenerator; var e, r, n, - s = 30 * pe + 13, - o = xe[s]; - if (o) return pe = o.nextPos, o.result; - if (r = [], (n = be()) === i && (46 === t.charCodeAt(pe) ? (n = ".", pe++) : (n = i, Ae(D))), n !== i) for (; n !== i;) r.push(n), (n = be()) === i && (46 === t.charCodeAt(pe) ? (n = ".", pe++) : (n = i, Ae(D)));else r = i; - return r !== i && (r = h(r)), e = r, xe[s] = { - nextPos: pe, + o = 30 * fe + 13, + a = ye[o]; + if (a) return fe = a.nextPos, a.result; + if (r = [], (n = Se()) === i && (46 === t.charCodeAt(fe) ? (n = ".", fe++) : (n = i, Ae(D))), n !== i) for (; n !== i;) r.push(n), (n = Se()) === i && (46 === t.charCodeAt(fe) ? (n = ".", fe++) : (n = i, Ae(D)));else r = i; + return r !== i && (r = h(r)), e = r, ye[o] = { + nextPos: fe, result: e }, e; } @@ -124891,182 +125168,177 @@ module.exports = NodeEventGenerator; }; } - if ((n = u()) !== i && pe === t.length) return n; - throw n !== i && pe < t.length && Ae({ + if ((n = u()) !== i && fe === t.length) return n; + throw n !== i && fe < t.length && Ae({ type: "end" - }), s = de, o = he < t.length ? t.charAt(he) : null, a = he < t.length ? ve(he, he + 1) : ve(he, he), new e(e.buildMessage(s, o), s, o, a); + }), o = de, a = he < t.length ? t.charAt(he) : null, s = he < t.length ? ve(he, he + 1) : ve(he, he), new e(e.buildMessage(o, a), o, a, s); } }; }()); }); - function n(e, r, i) { + function l(t, r, n) { if (!r) return !0; - if (!e) return !1; + if (!t) return !1; - switch (i || (i = []), r.type) { + switch (n || (n = []), r.type) { case "wildcard": return !0; case "identifier": - return r.value.toLowerCase() === e.type.toLowerCase(); + return r.value.toLowerCase() === t.type.toLowerCase(); case "field": - { - const t = r.name.split("."); - return function e(t, r, n) { - if (0 === n.length) return t === r; - if (null == r) return !1; - const s = r[n[0]], - o = n.slice(1); + var o = r.name.split("."), + a = n[o.length - 1]; + return function e(t, r, n) { + if (0 === n.length) return t === r; + if (null == r) return !1; + var o = r[n[0]], + a = n.slice(1); - if (Array.isArray(s)) { - for (let r = 0, n = s.length; r < n; ++r) if (e(t, s[r], o)) return !0; + if (Array.isArray(o)) { + for (var s = 0, i = o.length; s < i; ++s) if (e(t, o[s], a)) return !0; - return !1; - } + return !1; + } - return e(t, s, o); - }(e, i[t.length - 1], t); - } + return e(t, o, a); + }(t, a, o); case "matches": - for (let t = 0, s = r.selectors.length; t < s; ++t) if (n(e, r.selectors[t], i)) return !0; + for (var i = 0, p = r.selectors.length; i < p; ++i) if (l(t, r.selectors[i], n)) return !0; return !1; case "compound": - for (let t = 0, s = r.selectors.length; t < s; ++t) if (!n(e, r.selectors[t], i)) return !1; + for (var h = 0, d = r.selectors.length; h < d; ++h) if (!l(t, r.selectors[h], n)) return !1; return !0; case "not": - for (let t = 0, s = r.selectors.length; t < s; ++t) if (n(e, r.selectors[t], i)) return !1; + for (var y = 0, m = r.selectors.length; y < m; ++y) if (l(t, r.selectors[y], n)) return !1; return !0; case "has": - { - const s = []; - - for (let o = 0, a = r.selectors.length; o < a; ++o) { - const a = []; - t.traverse(e, { - enter(e, t) { - null != t && a.unshift(t), n(e, r.selectors[o], a) && s.push(e); + var x = function () { + for (var e = [], n = function n(_n, o) { + var a = []; + s.traverse(t, { + enter: function enter(t, o) { + null != o && a.unshift(o), l(t, r.selectors[_n], a) && e.push(t); }, - - leave() { + leave: function leave() { a.shift(); }, - fallback: "iteration" }); - } + }, o = 0, a = r.selectors.length; o < a; ++o) n(o); - return 0 !== s.length; - } + return { + v: 0 !== e.length + }; + }(); + + if ("object" === e(x)) return x.v; case "child": - return !!n(e, r.right, i) && n(i[0], r.left, i.slice(1)); + return !!l(t, r.right, n) && l(n[0], r.left, n.slice(1)); case "descendant": - if (n(e, r.right, i)) for (let e = 0, t = i.length; e < t; ++e) if (n(i[e], r.left, i.slice(e + 1))) return !0; + if (l(t, r.right, n)) for (var g = 0, v = n.length; g < v; ++g) if (l(n[g], r.left, n.slice(g + 1))) return !0; return !1; case "attribute": - { - const t = function (e, t) { - const r = t.split("."); - - for (let t = 0; t < r.length; t++) { - if (null == e) return e; - e = e[r[t]]; - } - - return e; - }(e, r.name); + var A = function (e, t) { + for (var r = t.split("."), n = 0; n < r.length; n++) { + if (null == e) return e; + e = e[r[n]]; + } - switch (r.operator) { - case void 0: - return null != t; + return e; + }(t, r.name); - case "=": - switch (r.value.type) { - case "regexp": - return "string" == typeof t && r.value.value.test(t); + switch (r.operator) { + case void 0: + return null != A; - case "literal": - return "".concat(r.value.value) === "".concat(t); + case "=": + switch (r.value.type) { + case "regexp": + return "string" == typeof A && r.value.value.test(A); - case "type": - return r.value.value === typeof t; - } + case "literal": + return "".concat(r.value.value) === "".concat(A); - throw new Error("Unknown selector value type: ".concat(r.value.type)); + case "type": + return r.value.value === e(A); + } - case "!=": - switch (r.value.type) { - case "regexp": - return !r.value.value.test(t); + throw new Error("Unknown selector value type: ".concat(r.value.type)); - case "literal": - return "".concat(r.value.value) !== "".concat(t); + case "!=": + switch (r.value.type) { + case "regexp": + return !r.value.value.test(A); - case "type": - return r.value.value !== typeof t; - } + case "literal": + return "".concat(r.value.value) !== "".concat(A); - throw new Error("Unknown selector value type: ".concat(r.value.type)); + case "type": + return r.value.value !== e(A); + } - case "<=": - return t <= r.value.value; + throw new Error("Unknown selector value type: ".concat(r.value.type)); - case "<": - return t < r.value.value; + case "<=": + return A <= r.value.value; - case ">": - return t > r.value.value; + case "<": + return A < r.value.value; - case ">=": - return t >= r.value.value; - } + case ">": + return A > r.value.value; - throw new Error("Unknown operator: ".concat(r.operator)); + case ">=": + return A >= r.value.value; } + throw new Error("Unknown operator: ".concat(r.operator)); + case "sibling": - return n(e, r.right, i) && s(e, r.left, i, "LEFT_SIDE") || r.left.subject && n(e, r.left, i) && s(e, r.right, i, "RIGHT_SIDE"); + return l(t, r.right, n) && u(t, r.left, n, "LEFT_SIDE") || r.left.subject && l(t, r.left, n) && u(t, r.right, n, "RIGHT_SIDE"); case "adjacent": - return n(e, r.right, i) && o(e, r.left, i, "LEFT_SIDE") || r.right.subject && n(e, r.left, i) && o(e, r.right, i, "RIGHT_SIDE"); + return l(t, r.right, n) && c(t, r.left, n, "LEFT_SIDE") || r.right.subject && l(t, r.left, n) && c(t, r.right, n, "RIGHT_SIDE"); case "nth-child": - return n(e, r.right, i) && a(e, i, function () { + return l(t, r.right, n) && f(t, n, function () { return r.index.value - 1; }); case "nth-last-child": - return n(e, r.right, i) && a(e, i, function (e) { + return l(t, r.right, n) && f(t, n, function (e) { return e - r.index.value; }); case "class": switch (r.name.toLowerCase()) { case "statement": - if ("Statement" === e.type.slice(-9)) return !0; + if ("Statement" === t.type.slice(-9)) return !0; case "declaration": - return "Declaration" === e.type.slice(-11); + return "Declaration" === t.type.slice(-11); case "pattern": - if ("Pattern" === e.type.slice(-7)) return !0; + if ("Pattern" === t.type.slice(-7)) return !0; case "expression": - return "Expression" === e.type.slice(-10) || "Literal" === e.type.slice(-7) || "Identifier" === e.type && (0 === i.length || "MetaProperty" !== i[0].type) || "MetaProperty" === e.type; + return "Expression" === t.type.slice(-10) || "Literal" === t.type.slice(-7) || "Identifier" === t.type && (0 === n.length || "MetaProperty" !== n[0].type) || "MetaProperty" === t.type; case "function": - return "FunctionDeclaration" === e.type || "FunctionExpression" === e.type || "ArrowFunctionExpression" === e.type; + return "FunctionDeclaration" === t.type || "FunctionExpression" === t.type || "ArrowFunctionExpression" === t.type; } throw new Error("Unknown class name: ".concat(r.name)); @@ -125075,104 +125347,118 @@ module.exports = NodeEventGenerator; throw new Error("Unknown selector type: ".concat(r.type)); } - function s(e, r, s, o) { - const [a] = s; + function u(e, r, n, o) { + var a = t(n, 1)[0]; if (!a) return !1; - const i = t.VisitorKeys[a.type]; - for (let t = 0, l = i.length; t < l; ++t) { - const l = a[i[t]]; + for (var i = s.VisitorKeys[a.type], u = 0, c = i.length; u < c; ++u) { + var f = a[i[u]]; - if (Array.isArray(l)) { - const t = l.indexOf(e); - if (t < 0) continue; - let a, i; - "LEFT_SIDE" === o ? (a = 0, i = t) : (a = t + 1, i = l.length); + if (Array.isArray(f)) { + var p = f.indexOf(e); + if (p < 0) continue; + var h = void 0, + d = void 0; + "LEFT_SIDE" === o ? (h = 0, d = p) : (h = p + 1, d = f.length); - for (let e = a; e < i; ++e) if (n(l[e], r, s)) return !0; + for (var y = h; y < d; ++y) if (l(f[y], r, n)) return !0; } } return !1; } - function o(e, r, s, o) { - const [a] = s; + function c(e, r, n, o) { + var a = t(n, 1)[0]; if (!a) return !1; - const i = t.VisitorKeys[a.type]; - for (let t = 0, l = i.length; t < l; ++t) { - const l = a[i[t]]; + for (var i = s.VisitorKeys[a.type], u = 0, c = i.length; u < c; ++u) { + var f = a[i[u]]; - if (Array.isArray(l)) { - const t = l.indexOf(e); - if (t < 0) continue; - if ("LEFT_SIDE" === o && t > 0 && n(l[t - 1], r, s)) return !0; - if ("RIGHT_SIDE" === o && t < l.length - 1 && n(l[t + 1], r, s)) return !0; + if (Array.isArray(f)) { + var p = f.indexOf(e); + if (p < 0) continue; + if ("LEFT_SIDE" === o && p > 0 && l(f[p - 1], r, n)) return !0; + if ("RIGHT_SIDE" === o && p < f.length - 1 && l(f[p + 1], r, n)) return !0; } } return !1; } - function a(e, r, n) { - const [s] = r; - if (!s) return !1; - const o = t.VisitorKeys[s.type]; + function f(e, r, n) { + var o = t(r, 1)[0]; + if (!o) return !1; - for (let t = 0, r = o.length; t < r; ++t) { - const r = s[o[t]]; + for (var a = s.VisitorKeys[o.type], i = 0, l = a.length; i < l; ++i) { + var u = o[a[i]]; - if (Array.isArray(r)) { - const t = r.indexOf(e); - if (t >= 0 && t === n(r.length)) return !0; + if (Array.isArray(u)) { + var c = u.indexOf(e); + if (c >= 0 && c === n(u.length)) return !0; } } return !1; } - function i(e, r) { - const s = [], - o = []; - if (!r) return o; + function p(n, o) { + if (null == n || "object" != e(n)) return []; + null == o && (o = n); - const a = function e(t, r) { - if (null == t || "object" != typeof t) return []; - null == r && (r = t); - const n = t.subject ? [r] : []; + for (var a = n.subject ? [o] : [], s = 0, i = function (e) { + for (var t = [], r = Object.keys(e), n = 0; n < r.length; n++) t.push([r[n], e[r[n]]]); - for (const [s, o] of Object.entries(t)) n.push(...e(o, "left" === s ? o : r)); - - return n; - }(r); + return t; + }(n); s < i.length; s++) { + var l = t(i[s], 2), + u = l[0], + c = l[1]; + a.push.apply(a, r(p(c, "left" === u ? c : o))); + } - return t.traverse(e, { - enter(e, t) { - if (null != t && s.unshift(t), n(e, r, s)) if (a.length) for (let t = 0, r = a.length; t < r; ++t) { - n(e, a[t], s) && o.push(e); + return a; + } - for (let e = 0, r = s.length; e < r; ++e) n(s[e], a[t], s.slice(e + 1)) && o.push(s[e]); - } else o.push(e); - }, + function h(e, t, r) { + if (t) { + var n = [], + o = p(t); + s.traverse(e, { + enter: function enter(e, a) { + if (null != a && n.unshift(a), l(e, t, n)) if (o.length) for (var s = 0, i = o.length; s < i; ++s) { + l(e, o[s], n) && r(e, a, n); - leave() { - s.shift(); - }, + for (var u = 0, c = n.length; u < c; ++u) { + var f = n.slice(u + 1); + l(n[u], o[s], f) && r(n[u], a, f); + } + } else r(e, a, n); + }, + leave: function leave() { + n.shift(); + }, + fallback: "iteration" + }); + } + } - fallback: "iteration" - }), o; + function d(e, t) { + var r = []; + return h(e, t, function (e) { + r.push(e); + }), r; } - function l(e) { - return r.parse(e); + function y(e) { + return i.parse(e); } - function u(e, t) { - return i(e, l(t)); + function m(e, t) { + return d(e, y(t)); } - return u.parse = l, u.match = i, u.matches = n, u.query = u, u; + return m.parse = y, m.match = d, m.traverse = h, m.matches = l, m.query = m, m; }); /***/ }), @@ -125396,7 +125682,7 @@ function mapSuggestions(descriptor, sourceCode, messages) { return descriptor.suggest.map(suggestInfo => { const computedDesc = suggestInfo.desc || messages[suggestInfo.messageId]; - return _objectSpread({}, suggestInfo, { + return _objectSpread(_objectSpread({}, suggestInfo), {}, { desc: interpolate(computedDesc, suggestInfo.data), fix: normalizeFixes(suggestInfo, sourceCode) }); @@ -126247,6 +126533,8 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]); //---------- /** @typedef {import("../shared/types").ConfigData} ConfigData */ +/** @typedef {import("../shared/types").DeprecatedRuleInfo} DeprecatedRuleInfo */ + /** @typedef {import("../shared/types").LintMessage} LintMessage */ /** @typedef {import("../shared/types").ParserOptions} ParserOptions */ @@ -126264,29 +126552,29 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]); //---------- /** * The options to configure a CLI engine with. * @typedef {Object} CLIEngineOptions - * @property {boolean} allowInlineConfig Enable or disable inline configuration comments. - * @property {ConfigData} baseConfig Base config object, extended by all configs used with this CLIEngine instance - * @property {boolean} cache Enable result caching. - * @property {string} cacheLocation The cache file to use instead of .eslintcache. - * @property {string} configFile The configuration file to use. - * @property {string} cwd The value to use for the current working directory. - * @property {string[]} envs An array of environments to load. - * @property {string[]|null} extensions An array of file extensions to check. - * @property {boolean|Function} fix Execute in autofix mode. If a function, should return a boolean. - * @property {string[]} fixTypes Array of rule types to apply fixes for. - * @property {string[]} globals An array of global variables to declare. - * @property {boolean} ignore False disables use of .eslintignore. - * @property {string} ignorePath The ignore file to use instead of .eslintignore. - * @property {string|string[]} ignorePattern One or more glob patterns to ignore. - * @property {boolean} useEslintrc False disables looking for .eslintrc - * @property {string} parser The name of the parser to use. - * @property {ParserOptions} parserOptions An object of parserOption settings to use. - * @property {string[]} plugins An array of plugins to load. - * @property {Record} rules An object of rules to use. - * @property {string[]} rulePaths An array of directories to load custom rules from. - * @property {boolean} reportUnusedDisableDirectives `true` adds reports for unused eslint-disable directives - * @property {boolean} globInputPaths Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. - * @property {string} resolvePluginsRelativeTo The folder where plugins should be resolved from, defaulting to the CWD + * @property {boolean} [allowInlineConfig] Enable or disable inline configuration comments. + * @property {ConfigData} [baseConfig] Base config object, extended by all configs used with this CLIEngine instance + * @property {boolean} [cache] Enable result caching. + * @property {string} [cacheLocation] The cache file to use instead of .eslintcache. + * @property {string} [configFile] The configuration file to use. + * @property {string} [cwd] The value to use for the current working directory. + * @property {string[]} [envs] An array of environments to load. + * @property {string[]|null} [extensions] An array of file extensions to check. + * @property {boolean|Function} [fix] Execute in autofix mode. If a function, should return a boolean. + * @property {string[]} [fixTypes] Array of rule types to apply fixes for. + * @property {string[]} [globals] An array of global variables to declare. + * @property {boolean} [ignore] False disables use of .eslintignore. + * @property {string} [ignorePath] The ignore file to use instead of .eslintignore. + * @property {string|string[]} [ignorePattern] One or more glob patterns to ignore. + * @property {boolean} [useEslintrc] False disables looking for .eslintrc + * @property {string} [parser] The name of the parser to use. + * @property {ParserOptions} [parserOptions] An object of parserOption settings to use. + * @property {string[]} [plugins] An array of plugins to load. + * @property {Record} [rules] An object of rules to use. + * @property {string[]} [rulePaths] An array of directories to load custom rules from. + * @property {boolean} [reportUnusedDisableDirectives] `true` adds reports for unused eslint-disable directives + * @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. + * @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD */ /** @@ -126302,13 +126590,6 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]); //---------- * @property {string} [output] The source code of the file that was linted, with as many fixes applied as possible. */ -/** - * Information of deprecated rules. - * @typedef {Object} DeprecatedRuleInfo - * @property {string} ruleId The rule ID. - * @property {string[]} replacedBy The rule IDs that replace this deprecated rule. - */ - /** * Linting results. * @typedef {Object} LintReport @@ -126837,7 +127118,7 @@ class CLIEngine { const filteredMessages = result.messages.filter(isErrorMessage); if (filteredMessages.length > 0) { - filtered.push(_objectSpread({}, result, { + filtered.push(_objectSpread(_objectSpread({}, result), {}, { messages: filteredMessages, errorCount: filteredMessages.length, warningCount: 0, @@ -127010,15 +127291,22 @@ class CLIEngine { if (lintResultCache) { lintResultCache.reconcile(); - } // Collect used deprecated rules. - + } - const usedDeprecatedRules = Array.from(iterateRuleDeprecationWarnings(lastConfigArrays)); debug("Linting complete in: ".concat(Date.now() - startTime, "ms")); - return _objectSpread({ + let usedDeprecatedRules; + return _objectSpread(_objectSpread({ results - }, calculateStatsPerRun(results), { - usedDeprecatedRules + }, calculateStatsPerRun(results)), {}, { + // Initialize it lazily because CLI and `ESLint` API don't use it. + get usedDeprecatedRules() { + if (!usedDeprecatedRules) { + usedDeprecatedRules = Array.from(iterateRuleDeprecationWarnings(lastConfigArrays)); + } + + return usedDeprecatedRules; + } + }); } /** @@ -127075,15 +127363,22 @@ class CLIEngine { fileEnumerator, linter })); - } // Collect used deprecated rules. - + } - const usedDeprecatedRules = Array.from(iterateRuleDeprecationWarnings(lastConfigArrays)); debug("Linting complete in: ".concat(Date.now() - startTime, "ms")); - return _objectSpread({ + let usedDeprecatedRules; + return _objectSpread(_objectSpread({ results - }, calculateStatsPerRun(results), { - usedDeprecatedRules + }, calculateStatsPerRun(results)), {}, { + // Initialize it lazily because CLI and `ESLint` API don't use it. + get usedDeprecatedRules() { + if (!usedDeprecatedRules) { + usedDeprecatedRules = Array.from(iterateRuleDeprecationWarnings(lastConfigArrays)); + } + + return usedDeprecatedRules; + } + }); } /** @@ -127137,11 +127432,10 @@ class CLIEngine { return defaultIgnores(absolutePath); } /** - * Returns the formatter representing the given format or null if no formatter - * with the given name can be found. + * Returns the formatter representing the given format or null if the `format` is not a string. * @param {string} [format] The name of the format to load or the path to a * custom formatter. - * @returns {Function} The formatter function or null if not found. + * @returns {(Function|null)} The formatter function or null if the `format` is not a string. */ @@ -127358,6 +127652,7 @@ const Module = __webpack_require__(828); * `Module.createRequire` is added in v12.2.0. It supports URL as well. * We only support the case where the argument is a filepath, not a URL. */ +// eslint-disable-next-line node/no-unsupported-features/node-builtins, node/no-deprecated-api const createRequire = Module.createRequire || Module.createRequireFromPath; @@ -127704,6 +127999,18 @@ class CascadingConfigArrayFactory { debug("Load config files for ".concat(directoryPath, ".")); return this._finalizeConfigArray(this._loadConfigInAncestors(directoryPath), directoryPath, ignoreNotFoundError); } + /** + * Set the config data to override all configs. + * Require to call `clearCache()` method after this method is called. + * @param {ConfigData} configData The config data to override all configs. + * @returns {void} + */ + + + setOverrideConfig(configData) { + const slots = internalSlotsMap.get(this); + slots.cliConfigData = configData; + } /** * Clear config cache. * @returns {void} @@ -128626,7 +128933,7 @@ class ConfigArrayFactory { }; // Flatten `overries`. for (let i = 0; i < overrideList.length; ++i) { - yield* this._normalizeObjectConfigData(overrideList[i], _objectSpread({}, ctx, { + yield* this._normalizeObjectConfigData(overrideList[i], _objectSpread(_objectSpread({}, ctx), {}, { name: "".concat(ctx.name, "#overrides[").concat(i, "]") })); } @@ -128669,14 +128976,14 @@ class ConfigArrayFactory { _loadExtendedBuiltInConfig(extendName, ctx) { if (extendName === "eslint:recommended") { - return this._loadConfigData(_objectSpread({}, ctx, { + return this._loadConfigData(_objectSpread(_objectSpread({}, ctx), {}, { filePath: eslintRecommendedPath, name: "".concat(ctx.name, " \xBB ").concat(extendName) })); } if (extendName === "eslint:all") { - return this._loadConfigData(_objectSpread({}, ctx, { + return this._loadConfigData(_objectSpread(_objectSpread({}, ctx), {}, { filePath: eslintAllPath, name: "".concat(ctx.name, " \xBB ").concat(extendName) })); @@ -128707,8 +129014,8 @@ class ConfigArrayFactory { const configData = plugin.definition && plugin.definition.configs[configName]; if (configData) { - return this._normalizeConfigData(configData, _objectSpread({}, ctx, { - filePath: plugin.filePath, + return this._normalizeConfigData(configData, _objectSpread(_objectSpread({}, ctx), {}, { + filePath: plugin.filePath || ctx.filePath, name: "".concat(ctx.name, " \xBB plugin:").concat(plugin.id, "/").concat(configName) })); } @@ -128753,7 +129060,7 @@ class ConfigArrayFactory { } writeDebugLogForLoading(request, relativeTo, filePath); - return this._loadConfigData(_objectSpread({}, ctx, { + return this._loadConfigData(_objectSpread(_objectSpread({}, ctx), {}, { filePath, name: "".concat(ctx.name, " \xBB ").concat(request) })); @@ -128866,7 +129173,8 @@ class ConfigArrayFactory { if (plugin) { return new ConfigDependency({ definition: normalizePlugin(plugin), - filePath: ctx.filePath, + filePath: "", + // It's unknown where the plugin came from. id, importerName: ctx.name, importerPath: ctx.filePath @@ -128943,7 +129251,7 @@ class ConfigArrayFactory { yield* this._normalizeObjectConfigData({ files: ["*".concat(processorId)], processor: "".concat(pluginId, "/").concat(processorId) - }, _objectSpread({}, ctx, { + }, _objectSpread(_objectSpread({}, ctx), {}, { type: "implicit-processor", name: "".concat(ctx.name, "#processors[\"").concat(pluginId, "/").concat(processorId, "\"]") })); @@ -129162,6 +129470,10 @@ const isEscaped = (jsonString, quotePosition) => { }; module.exports = (jsonString, options = {}) => { + if (typeof jsonString !== 'string') { + throw new TypeError("Expected argument `jsonString` to be a `string`, got `".concat(typeof jsonString, "`")); + } + const strip = options.whitespace === false ? stripWithoutWhitespace : stripWithWhitespace; let insideString = false; let insideComment = false; @@ -129384,7 +129696,7 @@ function getMatchedIndices(elements, filePath) { for (let i = elements.length - 1; i >= 0; --i) { const element = elements[i]; - if (!element.criteria || element.criteria.test(filePath)) { + if (!element.criteria || filePath && element.criteria.test(filePath)) { indices.push(i); } } @@ -130067,7 +130379,13 @@ function getCommonAncestorPath(sourcePaths) { } } - return result || path.sep; + let resolvedResult = result || path.sep; // if Windows common ancestor is root of drive must have trailing slash to be absolute. + + if (resolvedResult && resolvedResult.endsWith(":") && process.platform === "win32") { + resolvedResult += path.sep; + } + + return resolvedResult; } /** * Make relative path. @@ -130342,7 +130660,7 @@ class ConfigDependency { const obj = this[util.inspect.custom](); // Display `error.message` (`Error#message` is unenumerable). if (obj.error instanceof Error) { - obj.error = _objectSpread({}, obj.error, { + obj.error = _objectSpread(_objectSpread({}, obj.error), {}, { message: obj.error.message }); } @@ -130459,7 +130777,7 @@ function toMatcher(patterns) { return patterns.map(pattern => { if (/^\.[/\\]/u.test(pattern)) { return new Minimatch(pattern.slice(2), // `./*.js` should not match with `subdir/foo.js` - _objectSpread({}, minimatchOpts, { + _objectSpread(_objectSpread({}, minimatchOpts), {}, { matchBase: false })); } @@ -130596,7 +130914,7 @@ class OverrideTester { toJSON() { if (this.patterns.length === 1) { - return _objectSpread({}, patternToJson(this.patterns[0]), { + return _objectSpread(_objectSpread({}, patternToJson(this.patterns[0])), {}, { basePath: this.basePath }); } @@ -144700,7 +145018,7 @@ class FileEnumerator { : this.isTargetPath(filePath, config); if (matched) { - const ignored = this._isIgnoredFile(filePath, _objectSpread({}, options, { + const ignored = this._isIgnoredFile(filePath, _objectSpread(_objectSpread({}, options), {}, { config })); @@ -144722,7 +145040,7 @@ class FileEnumerator { }); } - const ignored = this._isIgnoredFile(filePath + path.sep, _objectSpread({}, options, { + const ignored = this._isIgnoredFile(filePath + path.sep, _objectSpread(_objectSpread({}, options), {}, { config })); @@ -145980,7 +146298,7 @@ function mkdirP(p, opts, f, made) { var xfs = opts.fs || fs; if (mode === undefined) { - mode = _0777 & ~process.umask(); + mode = _0777; } if (!made) made = null; @@ -146027,7 +146345,7 @@ mkdirP.sync = function sync(p, opts, made) { var xfs = opts.fs || fs; if (mode === undefined) { - mode = _0777 & ~process.umask(); + mode = _0777; } if (!made) made = null; -- 2.39.2