From 05e030a8ae728cc5222adb7e51df2ed1511eaac6 Mon Sep 17 00:00:00 2001 From: Teddy Katz Date: Sun, 24 Dec 2017 21:48:38 -0500 Subject: [PATCH 1/6] Prevent parseForESLint() behavior from changing after parse() is called (fixes https://github.com/babel/babel-eslint/issues/558, fixes https://github.com/eslint/eslint/issues/9767) --- lib/index.js | 6 +----- test/babel-eslint.js | 12 ++++++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/index.js b/lib/index.js index 130b10b8..d950300c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,18 +1,14 @@ "use strict"; -let patched = false; - exports.parse = function(code, options) { - patched = true; return require("./parse-with-patch")(code, options); }; exports.parseForESLint = function(code, options) { - if (!patched && options.eslintVisitorKeys && options.eslintScopeManager) { + if (options.eslintVisitorKeys && options.eslintScopeManager) { return require("./parse-with-scope")(code, options); } - patched = true; return { ast: require("./parse-with-patch")(code, options) }; }; diff --git a/test/babel-eslint.js b/test/babel-eslint.js index 3bebc0ac..e9d88731 100644 --- a/test/babel-eslint.js +++ b/test/babel-eslint.js @@ -539,4 +539,16 @@ describe("Public API", () => { babelEslint.parseNoPatch("foo", {}) ); }); + + it("still provides a custom scope with parseForESLint after calling parse()", () => { + assertImplementsAST( + espree.parse("foo", { sourceType: "module" }), + babelEslint.parse("foo", {}) + ); + const parseForESLintResult = babelEslint.parseForESLint("foo", { + eslintVisitorKeys: true, + eslintScopeManager: true, + }); + assert(parseForESLintResult.visitorKeys); + }); }); From f78855ba20f393d4bb398d64dcc5f259a387a40b Mon Sep 17 00:00:00 2001 From: Teddy Katz Date: Sun, 24 Dec 2017 22:09:59 -0500 Subject: [PATCH 2/6] Avoid using the enhanced referencer after monkeypatching --- lib/analyze-scope.js | 4 ++++ lib/parse-with-patch.js | 2 ++ test/babel-eslint.js | 27 +++++++++++++++++++++------ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/analyze-scope.js b/lib/analyze-scope.js index b538dea0..c4087739 100644 --- a/lib/analyze-scope.js +++ b/lib/analyze-scope.js @@ -308,6 +308,10 @@ class Referencer extends OriginalReferencer { } module.exports = function(ast, parserOptions) { + if (OriginalReferencer._babelEslintPatched) { + return escope.analyze(ast, parserOptions); + } + const options = { optimistic: false, directive: false, diff --git a/lib/parse-with-patch.js b/lib/parse-with-patch.js index d8157cfe..f415bcbf 100644 --- a/lib/parse-with-patch.js +++ b/lib/parse-with-patch.js @@ -352,6 +352,8 @@ function monkeypatch(modules) { this.close(node); } }; + + referencer._babelEslintPatched = true; } module.exports = function(code, options) { diff --git a/test/babel-eslint.js b/test/babel-eslint.js index e9d88731..655ca04e 100644 --- a/test/babel-eslint.js +++ b/test/babel-eslint.js @@ -540,15 +540,30 @@ describe("Public API", () => { ); }); - it("still provides a custom scope with parseForESLint after calling parse()", () => { + /* + * This test ensures that the enhanced referencer does not get used if eslint-scope has already been + * monkeypatched, because this causes some correctness issues. For example, if the enhanced referencer + * is used after the original referencer is monkeypatched, type annotation references are counted twice. + */ + it("does not visit type annotations multiple times after monkeypatching and calling parseForESLint()", () => { assertImplementsAST( espree.parse("foo", { sourceType: "module" }), babelEslint.parse("foo", {}) ); - const parseForESLintResult = babelEslint.parseForESLint("foo", { - eslintVisitorKeys: true, - eslintScopeManager: true, - }); - assert(parseForESLintResult.visitorKeys); + const parseResult = babelEslint.parseForESLint( + "type Foo = {}; function x(): Foo {}", + { + eslintVisitorKeys: true, + eslintScopeManager: true, + } + ); + assert(parseResult.visitorKeys); + assert(parseResult.scopeManager); + + const fooVariable = parseResult.scopeManager.getDeclaredVariables( + parseResult.ast.body[0] + )[0]; + + assert.strictEqual(fooVariable.references.length, 1); }); }); From 00978ecc26479052f0073e7b7dc15865f01de430 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Mon, 25 Dec 2017 12:06:51 +0900 Subject: [PATCH 3/6] Chore: add test for #558 --- package.json | 3 +- .../eslint-plugin-import/.eslintrc.yml | 11 + test/fixtures/eslint-plugin-import/a.js | 1 + test/fixtures/eslint-plugin-import/b.js | 1 + test/fixtures/eslint-plugin-import/c.js | 4 + test/z_eslint-plugin-import.js | 14 ++ yarn.lock | 214 +++++++++++++++++- 7 files changed, 237 insertions(+), 11 deletions(-) create mode 100644 test/fixtures/eslint-plugin-import/.eslintrc.yml create mode 100644 test/fixtures/eslint-plugin-import/a.js create mode 100644 test/fixtures/eslint-plugin-import/b.js create mode 100644 test/fixtures/eslint-plugin-import/c.js create mode 100644 test/z_eslint-plugin-import.js diff --git a/package.json b/package.json index 0f8892c2..a5a56e33 100644 --- a/package.json +++ b/package.json @@ -39,9 +39,10 @@ "devDependencies": { "babel-eslint": "^8.0.0", "dedent": "^0.7.0", - "eslint": "^4.12.1", + "eslint": "^4.14.0", "eslint-config-babel": "^7.0.1", "eslint-plugin-flowtype": "^2.30.3", + "eslint-plugin-import": "^2.8.0", "eslint-plugin-prettier": "^2.1.2", "espree": "^3.4.0", "husky": "^0.14.0", diff --git a/test/fixtures/eslint-plugin-import/.eslintrc.yml b/test/fixtures/eslint-plugin-import/.eslintrc.yml new file mode 100644 index 00000000..418b3d0c --- /dev/null +++ b/test/fixtures/eslint-plugin-import/.eslintrc.yml @@ -0,0 +1,11 @@ +root: true + +# babel-eslint +parser: ../../../lib/index.js + +# use eslint-plugin-import +plugins: + - import +rules: + import/no-named-as-default: error + no-unused-vars: error diff --git a/test/fixtures/eslint-plugin-import/a.js b/test/fixtures/eslint-plugin-import/a.js new file mode 100644 index 00000000..e8d96fc4 --- /dev/null +++ b/test/fixtures/eslint-plugin-import/a.js @@ -0,0 +1 @@ +export default function foo() { } diff --git a/test/fixtures/eslint-plugin-import/b.js b/test/fixtures/eslint-plugin-import/b.js new file mode 100644 index 00000000..b3a52f87 --- /dev/null +++ b/test/fixtures/eslint-plugin-import/b.js @@ -0,0 +1 @@ +import foo from './a.js'; diff --git a/test/fixtures/eslint-plugin-import/c.js b/test/fixtures/eslint-plugin-import/c.js new file mode 100644 index 00000000..2beac98f --- /dev/null +++ b/test/fixtures/eslint-plugin-import/c.js @@ -0,0 +1,4 @@ +// @flow +type Foo = {}; + +const FlowTypeButton = ({ }: Foo) => { }; diff --git a/test/z_eslint-plugin-import.js b/test/z_eslint-plugin-import.js new file mode 100644 index 00000000..01a80ec2 --- /dev/null +++ b/test/z_eslint-plugin-import.js @@ -0,0 +1,14 @@ +"use strict"; + +const eslint = require("eslint"); + +describe("https://github.com/babel/babel-eslint/issues/558", () => { + it("don't crash with eslint-plugin-import", () => { + const engine = new eslint.CLIEngine({ ignore: false }); + engine.executeOnFiles([ + "test/fixtures/eslint-plugin-import/a.js", + "test/fixtures/eslint-plugin-import/b.js", + "test/fixtures/eslint-plugin-import/c.js", + ]); + }); +}); diff --git a/yarn.lock b/yarn.lock index e3f86975..824dfa8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -241,6 +241,10 @@ browser-stdout@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" +builtin-modules@^1.0.0, builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + caller-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" @@ -352,6 +356,10 @@ concat-stream@^1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -381,12 +389,18 @@ date-fns@^1.27.2: version "1.28.5" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.28.5.tgz#257cfc45d322df45ef5658665967ee841cd73faf" -debug@3.1.0: +debug@3.1.0, debug@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: ms "2.0.0" +debug@^2.6.8: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + debug@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/debug/-/debug-3.0.1.tgz#0564c612b521dc92d9f2988f0549e34f9c98db64" @@ -417,6 +431,13 @@ diff@3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.1.tgz#aa8567a6eed03c531fc89d3f711cd0e5259dec75" +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + doctrine@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.2.tgz#68f96ce8efc56cc42651f1faadb4f175273b0075" @@ -441,12 +462,41 @@ eslint-config-babel@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/eslint-config-babel/-/eslint-config-babel-7.0.1.tgz#aac7b79f2f06f52358a5a764fdc01fde23718572" +eslint-import-resolver-node@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz#4422574cde66a9a7b099938ee4d508a199e0e3cc" + dependencies: + debug "^2.6.8" + resolve "^1.2.0" + +eslint-module-utils@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz#abaec824177613b8a95b299639e1b6facf473449" + dependencies: + debug "^2.6.8" + pkg-dir "^1.0.0" + eslint-plugin-flowtype@^2.30.3: version "2.34.0" resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.34.0.tgz#b9875f314652e5081623c9d2b18a346bbb759c09" dependencies: lodash "^4.15.0" +eslint-plugin-import@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.8.0.tgz#fa1b6ef31fcb3c501c09859c1b86f1fc5b986894" + dependencies: + builtin-modules "^1.1.1" + contains-path "^0.1.0" + debug "^2.6.8" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.1" + eslint-module-utils "^2.1.1" + has "^1.0.1" + lodash.cond "^4.3.0" + minimatch "^3.0.3" + read-pkg-up "^2.0.0" + eslint-plugin-prettier@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.1.2.tgz#4b90f4ee7f92bfbe2e926017e1ca40eb628965ea" @@ -465,21 +515,21 @@ eslint-visitor-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" -eslint@^4.12.1: - version "4.12.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.12.1.tgz#5ec1973822b4a066b353770c3c6d69a2a188e880" +eslint@^4.14.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.14.0.tgz#96609768d1dd23304faba2d94b7fefe5a5447a82" dependencies: ajv "^5.3.0" babel-code-frame "^6.22.0" chalk "^2.1.0" concat-stream "^1.6.0" cross-spawn "^5.1.0" - debug "^3.0.1" + debug "^3.1.0" doctrine "^2.0.2" eslint-scope "^3.7.1" + eslint-visitor-keys "^1.0.0" espree "^3.5.2" esquery "^1.0.0" - estraverse "^4.2.0" esutils "^2.0.2" file-entry-cache "^2.0.0" functional-red-black-tree "^1.0.1" @@ -542,7 +592,7 @@ esrecurse@^4.1.0: estraverse "^4.1.0" object-assign "^4.0.1" -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" @@ -610,6 +660,19 @@ file-entry-cache@^2.0.0: flat-cache "^1.2.1" object-assign "^4.0.1" +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + dependencies: + locate-path "^2.0.0" + flat-cache@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" @@ -623,6 +686,10 @@ fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" +function-bind@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" @@ -683,10 +750,20 @@ has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" +has@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" + dependencies: + function-bind "^1.0.2" + he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" +hosted-git-info@^2.1.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" + husky@^0.14.0: version "0.14.1" resolved "https://registry.yarnpkg.com/husky/-/husky-0.14.1.tgz#8edba33e728ceed75343e88bb8002e4cbd8d1b40" @@ -757,6 +834,12 @@ is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + is-ci@^1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e" @@ -809,7 +892,7 @@ is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" -isarray@~1.0.0: +isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -929,10 +1012,30 @@ listr@^0.12.0: stream-to-observable "^0.1.0" strip-ansi "^3.0.1" +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + lodash.chunk@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.chunk/-/lodash.chunk-4.2.0.tgz#66e5ce1f76ed27b4303d8c6512e8d1216e8106bc" +lodash.cond@^4.3.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" + lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -967,7 +1070,7 @@ mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: +minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -1014,6 +1117,15 @@ natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" +normalize-package-data@^2.3.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + normalize-path@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379" @@ -1094,6 +1206,16 @@ p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" +p-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + p-map@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.1.1.tgz#05f5e4ae97a068371bc2a5cc86bfbdbc19c4ae7a" @@ -1104,6 +1226,16 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -1116,6 +1248,16 @@ path-key@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + dependencies: + pify "^2.0.0" + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -1130,6 +1272,12 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" +pkg-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" + dependencies: + find-up "^1.0.0" + pluralize@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" @@ -1154,6 +1302,21 @@ pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + readable-stream@^2.2.2: version "2.3.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.2.tgz#5a04df05e4f57fe3f0dc68fdd11dc5c97c7e6f4d" @@ -1187,6 +1350,12 @@ resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" +resolve@^1.2.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" + dependencies: + path-parse "^1.0.5" + restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" @@ -1233,7 +1402,7 @@ safe-buffer@~5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" -semver@^5.3.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" @@ -1255,6 +1424,20 @@ slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + dependencies: + spdx-license-ids "^1.0.2" + +spdx-expression-parse@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + +spdx-license-ids@^1.0.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -1300,6 +1483,10 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -1379,6 +1566,13 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" +validate-npm-package-license@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + which@^1.2.10, which@^1.2.9: version "1.2.14" resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" From 1dff11203514fd2ed16a6e76f48eee06185b6c8b Mon Sep 17 00:00:00 2001 From: Teddy Katz Date: Sun, 24 Dec 2017 23:50:39 -0500 Subject: [PATCH 4/6] Pass correct scope analyzer options --- lib/analyze-scope.js | 13 +++++++----- lib/parse-with-patch.js | 15 +++++++------- package.json | 1 + test/non-regression.js | 27 ++++++++++++++++++++++--- yarn.lock | 44 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 84 insertions(+), 16 deletions(-) diff --git a/lib/analyze-scope.js b/lib/analyze-scope.js index c4087739..6aa95e47 100644 --- a/lib/analyze-scope.js +++ b/lib/analyze-scope.js @@ -308,10 +308,6 @@ class Referencer extends OriginalReferencer { } module.exports = function(ast, parserOptions) { - if (OriginalReferencer._babelEslintPatched) { - return escope.analyze(ast, parserOptions); - } - const options = { optimistic: false, directive: false, @@ -322,9 +318,16 @@ module.exports = function(ast, parserOptions) { impliedStrict: false, sourceType: ast.sourceType, ecmaVersion: parserOptions.ecmaVersion || 6, - childVisitorKeys, fallback, }; + + if (OriginalReferencer._babelEslintPatched) { + options._skipBabelEslintGlobals = true; + return escope.analyze(ast, options); + } + + options.childVisitorKeys = childVisitorKeys; + const scopeManager = new escope.ScopeManager(options); const referencer = new Referencer(options, scopeManager); diff --git a/lib/parse-with-patch.js b/lib/parse-with-patch.js index f415bcbf..8847a9f2 100644 --- a/lib/parse-with-patch.js +++ b/lib/parse-with-patch.js @@ -60,15 +60,16 @@ function monkeypatch(modules) { var analyze = escope.analyze; escope.analyze = function(ast, opts) { - opts = opts || {}; - opts.ecmaVersion = eslintOptions.ecmaVersion; - opts.sourceType = eslintOptions.sourceType; - if (eslintOptions.globalReturn !== undefined) { - opts.nodejsScope = eslintOptions.globalReturn; + if (!opts || !opts._skipBabelEslintGlobals) { + opts = opts || {}; + opts.ecmaVersion = eslintOptions.ecmaVersion; + opts.sourceType = eslintOptions.sourceType; + if (eslintOptions.globalReturn !== undefined) { + opts.nodejsScope = eslintOptions.globalReturn; + } } - var results = analyze.call(this, ast, opts); - return results; + return analyze.call(this, ast, opts); }; // if there are decorators, then visit each diff --git a/package.json b/package.json index a5a56e33..42f0ffdd 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "dedent": "^0.7.0", "eslint": "^4.14.0", "eslint-config-babel": "^7.0.1", + "eslint-old": "npm:eslint@4.13.1", "eslint-plugin-flowtype": "^2.30.3", "eslint-plugin-import": "^2.8.0", "eslint-plugin-prettier": "^2.1.2", diff --git a/test/non-regression.js b/test/non-regression.js index 4161af03..e21a95a0 100644 --- a/test/non-regression.js +++ b/test/non-regression.js @@ -1,14 +1,16 @@ /*eslint-env mocha*/ "use strict"; var eslint = require("eslint"); +var oldEslint = require("eslint-old"); var unpad = require("dedent"); -function verifyAndAssertMessages( +function verifyAndAssertMessagesWithSpecificESLint( code, rules, expectedMessages, sourceType, - overrideConfig + overrideConfig, + linter ) { var config = { parser: require.resolve(".."), @@ -34,7 +36,7 @@ function verifyAndAssertMessages( } } - var messages = eslint.linter.verify(code, config); + var messages = linter.verify(code, config); if (messages.length !== expectedMessages.length) { throw new Error( @@ -62,6 +64,25 @@ function verifyAndAssertMessages( }); } +function verifyAndAssertMessages( + code, + rules, + expectedMessages, + sourceType, + overrideConfig +) { + [new eslint.Linter(), new oldEslint.Linter()].forEach(linter => { + verifyAndAssertMessagesWithSpecificESLint( + code, + rules, + expectedMessages, + sourceType, + overrideConfig, + linter + ); + }); +} + describe("verify", () => { it("arrow function support (issue #1)", () => { verifyAndAssertMessages("describe('stuff', () => {});", {}, []); diff --git a/yarn.lock b/yarn.lock index 824dfa8a..5cb0ef21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -476,6 +476,48 @@ eslint-module-utils@^2.1.1: debug "^2.6.8" pkg-dir "^1.0.0" +"eslint-old@npm:eslint@4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.13.1.tgz#0055e0014464c7eb7878caf549ef2941992b444f" + dependencies: + ajv "^5.3.0" + babel-code-frame "^6.22.0" + chalk "^2.1.0" + concat-stream "^1.6.0" + cross-spawn "^5.1.0" + debug "^3.0.1" + doctrine "^2.0.2" + eslint-scope "^3.7.1" + espree "^3.5.2" + esquery "^1.0.0" + estraverse "^4.2.0" + esutils "^2.0.2" + file-entry-cache "^2.0.0" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.0.1" + ignore "^3.3.3" + imurmurhash "^0.1.4" + inquirer "^3.0.6" + is-resolvable "^1.0.0" + js-yaml "^3.9.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.4" + minimatch "^3.0.2" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + pluralize "^7.0.0" + progress "^2.0.0" + require-uncached "^1.0.3" + semver "^5.3.0" + strip-ansi "^4.0.0" + strip-json-comments "~2.0.1" + table "^4.0.1" + text-table "~0.2.0" + eslint-plugin-flowtype@^2.30.3: version "2.34.0" resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.34.0.tgz#b9875f314652e5081623c9d2b18a346bbb759c09" @@ -592,7 +634,7 @@ esrecurse@^4.1.0: estraverse "^4.1.0" object-assign "^4.0.1" -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" From db14da01f2751053e1f911ad344c2966ea58e048 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Mon, 25 Dec 2017 15:08:45 +0900 Subject: [PATCH 5/6] fix escope patch and improve tests --- lib/analyze-scope.js | 2 +- lib/index.js | 9 +- lib/parse-with-patch.js | 379 +--------------------- lib/parse-with-scope.js | 6 - lib/patch-eslint-scope.js | 376 +++++++++++++++++++++ package.json | 2 +- test/babel-eslint.js | 70 +--- test/fixtures/assert-implements-ast.js | 41 +++ test/fixtures/preprocess-to-patch.js | 5 + test/fixtures/use-eslint-old.js | 12 + test/non-regression.js | 19 +- test/z_eslint-plugin-import.js | 14 - test/z_parser-for-eslint-after-patched.js | 45 +++ 13 files changed, 499 insertions(+), 481 deletions(-) create mode 100644 lib/patch-eslint-scope.js create mode 100644 test/fixtures/assert-implements-ast.js create mode 100644 test/fixtures/preprocess-to-patch.js create mode 100644 test/fixtures/use-eslint-old.js delete mode 100644 test/z_eslint-plugin-import.js create mode 100644 test/z_parser-for-eslint-after-patched.js diff --git a/lib/analyze-scope.js b/lib/analyze-scope.js index 6aa95e47..6001c136 100644 --- a/lib/analyze-scope.js +++ b/lib/analyze-scope.js @@ -322,7 +322,7 @@ module.exports = function(ast, parserOptions) { }; if (OriginalReferencer._babelEslintPatched) { - options._skipBabelEslintGlobals = true; + require("./patch-eslint-scope")(parserOptions); return escope.analyze(ast, options); } diff --git a/lib/index.js b/lib/index.js index d950300c..973f742e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,14 +1,19 @@ "use strict"; exports.parse = function(code, options) { - return require("./parse-with-patch")(code, options); + return exports.parseForESLint(code, options).ast; }; exports.parseForESLint = function(code, options) { + options = options || {}; + options.ecmaVersion = options.ecmaVersion || 6; + options.sourceType = options.sourceType || "module"; + options.allowImportExportEverywhere = + options.allowImportExportEverywhere || false; + if (options.eslintVisitorKeys && options.eslintScopeManager) { return require("./parse-with-scope")(code, options); } - return { ast: require("./parse-with-patch")(code, options) }; }; diff --git a/lib/parse-with-patch.js b/lib/parse-with-patch.js index 8847a9f2..ba1b95b5 100644 --- a/lib/parse-with-patch.js +++ b/lib/parse-with-patch.js @@ -1,384 +1,9 @@ "use strict"; var parse = require("./parse"); -var Module = require("module"); -var path = require("path"); -var t = require("@babel/types"); -var hasPatched = false; -var eslintOptions = {}; - -function getModules() { - try { - // avoid importing a local copy of eslint, try to find a peer dependency - var eslintLoc = Module._resolveFilename("eslint", module.parent); - } catch (err) { - try { - // avoids breaking in jest where module.parent is undefined - eslintLoc = require.resolve("eslint"); - } catch (err) { - throw new ReferenceError("couldn't resolve eslint"); - } - } - - // get modules relative to what eslint will load - var eslintMod = new Module(eslintLoc); - eslintMod.filename = eslintLoc; - eslintMod.paths = Module._nodeModulePaths(path.dirname(eslintLoc)); - - try { - var escope = eslintMod.require("eslint-scope"); - var Definition = eslintMod.require("eslint-scope/lib/definition") - .Definition; - var referencer = eslintMod.require("eslint-scope/lib/referencer"); - } catch (err) { - escope = eslintMod.require("escope"); - Definition = eslintMod.require("escope/lib/definition").Definition; - referencer = eslintMod.require("escope/lib/referencer"); - } - - var estraverse = eslintMod.require("estraverse"); - - if (referencer.__esModule) referencer = referencer.default; - - return { - Definition, - escope, - estraverse, - referencer, - }; -} - -function monkeypatch(modules) { - var Definition = modules.Definition; - var escope = modules.escope; - var estraverse = modules.estraverse; - var referencer = modules.referencer; - - Object.assign(estraverse.VisitorKeys, t.VISITOR_KEYS); - estraverse.VisitorKeys.MethodDefinition.push("decorators"); - estraverse.VisitorKeys.Property.push("decorators"); - - var analyze = escope.analyze; - escope.analyze = function(ast, opts) { - if (!opts || !opts._skipBabelEslintGlobals) { - opts = opts || {}; - opts.ecmaVersion = eslintOptions.ecmaVersion; - opts.sourceType = eslintOptions.sourceType; - if (eslintOptions.globalReturn !== undefined) { - opts.nodejsScope = eslintOptions.globalReturn; - } - } - - return analyze.call(this, ast, opts); - }; - - // if there are decorators, then visit each - function visitDecorators(node) { - if (!node.decorators) { - return; - } - for (var i = 0; i < node.decorators.length; i++) { - if (node.decorators[i].expression) { - this.visit(node.decorators[i]); - } - } - } - - // iterate through part of t.VISITOR_KEYS - var flowFlippedAliasKeys = t.FLIPPED_ALIAS_KEYS.Flow.concat([ - "ArrayPattern", - "ClassDeclaration", - "ClassExpression", - "FunctionDeclaration", - "FunctionExpression", - "Identifier", - "ObjectPattern", - "RestElement", - ]); - var visitorKeysMap = Object.keys(t.VISITOR_KEYS).reduce(function(acc, key) { - var value = t.VISITOR_KEYS[key]; - if (flowFlippedAliasKeys.indexOf(value) === -1) { - acc[key] = value; - } - return acc; - }, {}); - - var propertyTypes = { - // loops - callProperties: { type: "loop", values: ["value"] }, - indexers: { type: "loop", values: ["key", "value"] }, - properties: { type: "loop", values: ["argument", "value"] }, - types: { type: "loop" }, - params: { type: "loop" }, - // single property - argument: { type: "single" }, - elementType: { type: "single" }, - qualification: { type: "single" }, - rest: { type: "single" }, - returnType: { type: "single" }, - // others - typeAnnotation: { type: "typeAnnotation" }, - typeParameters: { type: "typeParameters" }, - id: { type: "id" }, - }; - - function visitTypeAnnotation(node) { - // get property to check (params, id, etc...) - var visitorValues = visitorKeysMap[node.type]; - if (!visitorValues) { - return; - } - - // can have multiple properties - for (var i = 0; i < visitorValues.length; i++) { - var visitorValue = visitorValues[i]; - var propertyType = propertyTypes[visitorValue]; - var nodeProperty = node[visitorValue]; - // check if property or type is defined - if (propertyType == null || nodeProperty == null) { - continue; - } - if (propertyType.type === "loop") { - for (var j = 0; j < nodeProperty.length; j++) { - if (Array.isArray(propertyType.values)) { - for (var k = 0; k < propertyType.values.length; k++) { - var loopPropertyNode = nodeProperty[j][propertyType.values[k]]; - if (loopPropertyNode) { - checkIdentifierOrVisit.call(this, loopPropertyNode); - } - } - } else { - checkIdentifierOrVisit.call(this, nodeProperty[j]); - } - } - } else if (propertyType.type === "single") { - checkIdentifierOrVisit.call(this, nodeProperty); - } else if (propertyType.type === "typeAnnotation") { - visitTypeAnnotation.call(this, node.typeAnnotation); - } else if (propertyType.type === "typeParameters") { - for (var l = 0; l < node.typeParameters.params.length; l++) { - checkIdentifierOrVisit.call(this, node.typeParameters.params[l]); - } - } else if (propertyType.type === "id") { - if (node.id.type === "Identifier") { - checkIdentifierOrVisit.call(this, node.id); - } else { - visitTypeAnnotation.call(this, node.id); - } - } - } - } - - function checkIdentifierOrVisit(node) { - if (node.typeAnnotation) { - visitTypeAnnotation.call(this, node.typeAnnotation); - } else if (node.type === "Identifier") { - this.visit(node); - } else { - visitTypeAnnotation.call(this, node); - } - } - - function nestTypeParamScope(manager, node) { - var parentScope = manager.__currentScope; - var scope = new escope.Scope( - manager, - "type-parameters", - parentScope, - node, - false - ); - manager.__nestScope(scope); - for (var j = 0; j < node.typeParameters.params.length; j++) { - var name = node.typeParameters.params[j]; - scope.__define(name, new Definition("TypeParameter", name, name)); - if (name.typeAnnotation) { - checkIdentifierOrVisit.call(this, name); - } - } - scope.__define = function() { - return parentScope.__define.apply(parentScope, arguments); - }; - return scope; - } - - // visit decorators that are in: ClassDeclaration / ClassExpression - var visitClass = referencer.prototype.visitClass; - referencer.prototype.visitClass = function(node) { - visitDecorators.call(this, node); - var typeParamScope; - if (node.typeParameters) { - typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node); - } - // visit flow type: ClassImplements - if (node.implements) { - for (var i = 0; i < node.implements.length; i++) { - checkIdentifierOrVisit.call(this, node.implements[i]); - } - } - if (node.superTypeParameters) { - for (var k = 0; k < node.superTypeParameters.params.length; k++) { - checkIdentifierOrVisit.call(this, node.superTypeParameters.params[k]); - } - } - visitClass.call(this, node); - if (typeParamScope) { - this.close(node); - } - }; - - // visit decorators that are in: Property / MethodDefinition - var visitProperty = referencer.prototype.visitProperty; - referencer.prototype.visitProperty = function(node) { - if (node.value && node.value.type === "TypeCastExpression") { - visitTypeAnnotation.call(this, node.value); - } - visitDecorators.call(this, node); - visitProperty.call(this, node); - }; - - function visitClassProperty(node) { - if (node.typeAnnotation) { - visitTypeAnnotation.call(this, node.typeAnnotation); - } - this.visitProperty(node); - } - - // visit ClassProperty as a Property. - referencer.prototype.ClassProperty = visitClassProperty; - - // visit ClassPrivateProperty as a Property. - referencer.prototype.ClassPrivateProperty = visitClassProperty; - - // visit flow type in FunctionDeclaration, FunctionExpression, ArrowFunctionExpression - var visitFunction = referencer.prototype.visitFunction; - referencer.prototype.visitFunction = function(node) { - var typeParamScope; - if (node.typeParameters) { - typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node); - } - if (node.returnType) { - checkIdentifierOrVisit.call(this, node.returnType); - } - // only visit if function parameters have types - if (node.params) { - for (var i = 0; i < node.params.length; i++) { - var param = node.params[i]; - if (param.typeAnnotation) { - checkIdentifierOrVisit.call(this, param); - } else if (t.isAssignmentPattern(param)) { - if (param.left.typeAnnotation) { - checkIdentifierOrVisit.call(this, param.left); - } - } - } - } - // set ArrayPattern/ObjectPattern visitor keys back to their original. otherwise - // escope will traverse into them and include the identifiers within as declarations - estraverse.VisitorKeys.ObjectPattern = ["properties"]; - estraverse.VisitorKeys.ArrayPattern = ["elements"]; - visitFunction.call(this, node); - // set them back to normal... - estraverse.VisitorKeys.ObjectPattern = t.VISITOR_KEYS.ObjectPattern; - estraverse.VisitorKeys.ArrayPattern = t.VISITOR_KEYS.ArrayPattern; - if (typeParamScope) { - this.close(node); - } - }; - - // visit flow type in VariableDeclaration - var variableDeclaration = referencer.prototype.VariableDeclaration; - referencer.prototype.VariableDeclaration = function(node) { - if (node.declarations) { - for (var i = 0; i < node.declarations.length; i++) { - var id = node.declarations[i].id; - var typeAnnotation = id.typeAnnotation; - if (typeAnnotation) { - checkIdentifierOrVisit.call(this, typeAnnotation); - } - } - } - variableDeclaration.call(this, node); - }; - - function createScopeVariable(node, name) { - this.currentScope().variableScope.__define( - name, - new Definition("Variable", name, node, null, null, null) - ); - } - - referencer.prototype.InterfaceDeclaration = function(node) { - createScopeVariable.call(this, node, node.id); - var typeParamScope; - if (node.typeParameters) { - typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node); - } - // TODO: Handle mixins - for (var i = 0; i < node.extends.length; i++) { - visitTypeAnnotation.call(this, node.extends[i]); - } - visitTypeAnnotation.call(this, node.body); - if (typeParamScope) { - this.close(node); - } - }; - - referencer.prototype.TypeAlias = function(node) { - createScopeVariable.call(this, node, node.id); - var typeParamScope; - if (node.typeParameters) { - typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node); - } - if (node.right) { - visitTypeAnnotation.call(this, node.right); - } - if (typeParamScope) { - this.close(node); - } - }; - - referencer.prototype.DeclareModule = referencer.prototype.DeclareFunction = referencer.prototype.DeclareVariable = referencer.prototype.DeclareClass = function( - node - ) { - if (node.id) { - createScopeVariable.call(this, node, node.id); - } - - var typeParamScope; - if (node.typeParameters) { - typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node); - } - if (typeParamScope) { - this.close(node); - } - }; - - referencer._babelEslintPatched = true; -} +var patchEscope = require("./patch-eslint-scope"); module.exports = function(code, options) { - options = options || {}; - eslintOptions.ecmaVersion = options.ecmaVersion = options.ecmaVersion || 6; - eslintOptions.sourceType = options.sourceType = - options.sourceType || "module"; - eslintOptions.allowImportExportEverywhere = options.allowImportExportEverywhere = - options.allowImportExportEverywhere || false; - if (options.sourceType === "module") { - eslintOptions.globalReturn = false; - } else { - delete eslintOptions.globalReturn; - } - - if (!hasPatched) { - hasPatched = true; - try { - monkeypatch(getModules()); - } catch (err) { - console.error(err.stack); - process.exit(1); - } - } - + patchEscope(options); return parse(code, options); }; diff --git a/lib/parse-with-scope.js b/lib/parse-with-scope.js index a222b79a..8892076e 100644 --- a/lib/parse-with-scope.js +++ b/lib/parse-with-scope.js @@ -5,12 +5,6 @@ const analyzeScope = require("./analyze-scope"); const parse = require("./parse"); module.exports = function(code, options) { - options = options || {}; - options.ecmaVersion = options.ecmaVersion || 6; - options.sourceType = options.sourceType || "module"; - options.allowImportExportEverywhere = - options.allowImportExportEverywhere || false; - const ast = parse(code, options); const scopeManager = analyzeScope(ast, options); const visitorKeys = t.VISITOR_KEYS; diff --git a/lib/patch-eslint-scope.js b/lib/patch-eslint-scope.js new file mode 100644 index 00000000..44691439 --- /dev/null +++ b/lib/patch-eslint-scope.js @@ -0,0 +1,376 @@ +"use strict"; + +var Module = require("module"); +var path = require("path"); +var t = require("@babel/types"); + +function getModules() { + try { + // avoid importing a local copy of eslint, try to find a peer dependency + var eslintLoc = Module._resolveFilename("eslint", module.parent); + } catch (err) { + try { + // avoids breaking in jest where module.parent is undefined + eslintLoc = require.resolve("eslint"); + } catch (err) { + throw new ReferenceError("couldn't resolve eslint"); + } + } + + // get modules relative to what eslint will load + var eslintMod = new Module(eslintLoc); + eslintMod.filename = eslintLoc; + eslintMod.paths = Module._nodeModulePaths(path.dirname(eslintLoc)); + + try { + var escope = eslintMod.require("eslint-scope"); + var Definition = eslintMod.require("eslint-scope/lib/definition") + .Definition; + var referencer = eslintMod.require("eslint-scope/lib/referencer"); + } catch (err) { + escope = eslintMod.require("escope"); + Definition = eslintMod.require("escope/lib/definition").Definition; + referencer = eslintMod.require("escope/lib/referencer"); + } + + var estraverse = eslintMod.require("estraverse"); + + if (referencer.__esModule) referencer = referencer.default; + + return { + Definition, + escope, + estraverse, + referencer, + }; +} + +function monkeypatch(modules) { + var Definition = modules.Definition; + var escope = modules.escope; + var estraverse = modules.estraverse; + var referencer = modules.referencer; + + Object.assign(estraverse.VisitorKeys, t.VISITOR_KEYS); + estraverse.VisitorKeys.MethodDefinition.push("decorators"); + estraverse.VisitorKeys.Property.push("decorators"); + + // if there are decorators, then visit each + function visitDecorators(node) { + if (!node.decorators) { + return; + } + for (var i = 0; i < node.decorators.length; i++) { + if (node.decorators[i].expression) { + this.visit(node.decorators[i]); + } + } + } + + // iterate through part of t.VISITOR_KEYS + var flowFlippedAliasKeys = t.FLIPPED_ALIAS_KEYS.Flow.concat([ + "ArrayPattern", + "ClassDeclaration", + "ClassExpression", + "FunctionDeclaration", + "FunctionExpression", + "Identifier", + "ObjectPattern", + "RestElement", + ]); + var visitorKeysMap = Object.keys(t.VISITOR_KEYS).reduce(function(acc, key) { + var value = t.VISITOR_KEYS[key]; + if (flowFlippedAliasKeys.indexOf(value) === -1) { + acc[key] = value; + } + return acc; + }, {}); + + var propertyTypes = { + // loops + callProperties: { type: "loop", values: ["value"] }, + indexers: { type: "loop", values: ["key", "value"] }, + properties: { type: "loop", values: ["argument", "value"] }, + types: { type: "loop" }, + params: { type: "loop" }, + // single property + argument: { type: "single" }, + elementType: { type: "single" }, + qualification: { type: "single" }, + rest: { type: "single" }, + returnType: { type: "single" }, + // others + typeAnnotation: { type: "typeAnnotation" }, + typeParameters: { type: "typeParameters" }, + id: { type: "id" }, + }; + + function visitTypeAnnotation(node) { + // get property to check (params, id, etc...) + var visitorValues = visitorKeysMap[node.type]; + if (!visitorValues) { + return; + } + + // can have multiple properties + for (var i = 0; i < visitorValues.length; i++) { + var visitorValue = visitorValues[i]; + var propertyType = propertyTypes[visitorValue]; + var nodeProperty = node[visitorValue]; + // check if property or type is defined + if (propertyType == null || nodeProperty == null) { + continue; + } + if (propertyType.type === "loop") { + for (var j = 0; j < nodeProperty.length; j++) { + if (Array.isArray(propertyType.values)) { + for (var k = 0; k < propertyType.values.length; k++) { + var loopPropertyNode = nodeProperty[j][propertyType.values[k]]; + if (loopPropertyNode) { + checkIdentifierOrVisit.call(this, loopPropertyNode); + } + } + } else { + checkIdentifierOrVisit.call(this, nodeProperty[j]); + } + } + } else if (propertyType.type === "single") { + checkIdentifierOrVisit.call(this, nodeProperty); + } else if (propertyType.type === "typeAnnotation") { + visitTypeAnnotation.call(this, node.typeAnnotation); + } else if (propertyType.type === "typeParameters") { + for (var l = 0; l < node.typeParameters.params.length; l++) { + checkIdentifierOrVisit.call(this, node.typeParameters.params[l]); + } + } else if (propertyType.type === "id") { + if (node.id.type === "Identifier") { + checkIdentifierOrVisit.call(this, node.id); + } else { + visitTypeAnnotation.call(this, node.id); + } + } + } + } + + function checkIdentifierOrVisit(node) { + if (node.typeAnnotation) { + visitTypeAnnotation.call(this, node.typeAnnotation); + } else if (node.type === "Identifier") { + this.visit(node); + } else { + visitTypeAnnotation.call(this, node); + } + } + + function nestTypeParamScope(manager, node) { + var parentScope = manager.__currentScope; + var scope = new escope.Scope( + manager, + "type-parameters", + parentScope, + node, + false + ); + manager.__nestScope(scope); + for (var j = 0; j < node.typeParameters.params.length; j++) { + var name = node.typeParameters.params[j]; + scope.__define(name, new Definition("TypeParameter", name, name)); + if (name.typeAnnotation) { + checkIdentifierOrVisit.call(this, name); + } + } + scope.__define = function() { + return parentScope.__define.apply(parentScope, arguments); + }; + return scope; + } + + // visit decorators that are in: ClassDeclaration / ClassExpression + var visitClass = referencer.prototype.visitClass; + referencer.prototype.visitClass = function(node) { + visitDecorators.call(this, node); + var typeParamScope; + if (node.typeParameters) { + typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node); + } + // visit flow type: ClassImplements + if (node.implements) { + for (var i = 0; i < node.implements.length; i++) { + checkIdentifierOrVisit.call(this, node.implements[i]); + } + } + if (node.superTypeParameters) { + for (var k = 0; k < node.superTypeParameters.params.length; k++) { + checkIdentifierOrVisit.call(this, node.superTypeParameters.params[k]); + } + } + visitClass.call(this, node); + if (typeParamScope) { + this.close(node); + } + }; + + // visit decorators that are in: Property / MethodDefinition + var visitProperty = referencer.prototype.visitProperty; + referencer.prototype.visitProperty = function(node) { + if (node.value && node.value.type === "TypeCastExpression") { + visitTypeAnnotation.call(this, node.value); + } + visitDecorators.call(this, node); + visitProperty.call(this, node); + }; + + function visitClassProperty(node) { + if (node.typeAnnotation) { + visitTypeAnnotation.call(this, node.typeAnnotation); + } + this.visitProperty(node); + } + + // visit ClassProperty as a Property. + referencer.prototype.ClassProperty = visitClassProperty; + + // visit ClassPrivateProperty as a Property. + referencer.prototype.ClassPrivateProperty = visitClassProperty; + + // visit flow type in FunctionDeclaration, FunctionExpression, ArrowFunctionExpression + var visitFunction = referencer.prototype.visitFunction; + referencer.prototype.visitFunction = function(node) { + var typeParamScope; + if (node.typeParameters) { + typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node); + } + if (node.returnType) { + checkIdentifierOrVisit.call(this, node.returnType); + } + // only visit if function parameters have types + if (node.params) { + for (var i = 0; i < node.params.length; i++) { + var param = node.params[i]; + if (param.typeAnnotation) { + checkIdentifierOrVisit.call(this, param); + } else if (t.isAssignmentPattern(param)) { + if (param.left.typeAnnotation) { + checkIdentifierOrVisit.call(this, param.left); + } + } + } + } + // set ArrayPattern/ObjectPattern visitor keys back to their original. otherwise + // escope will traverse into them and include the identifiers within as declarations + estraverse.VisitorKeys.ObjectPattern = ["properties"]; + estraverse.VisitorKeys.ArrayPattern = ["elements"]; + visitFunction.call(this, node); + // set them back to normal... + estraverse.VisitorKeys.ObjectPattern = t.VISITOR_KEYS.ObjectPattern; + estraverse.VisitorKeys.ArrayPattern = t.VISITOR_KEYS.ArrayPattern; + if (typeParamScope) { + this.close(node); + } + }; + + // visit flow type in VariableDeclaration + var variableDeclaration = referencer.prototype.VariableDeclaration; + referencer.prototype.VariableDeclaration = function(node) { + if (node.declarations) { + for (var i = 0; i < node.declarations.length; i++) { + var id = node.declarations[i].id; + var typeAnnotation = id.typeAnnotation; + if (typeAnnotation) { + checkIdentifierOrVisit.call(this, typeAnnotation); + } + } + } + variableDeclaration.call(this, node); + }; + + function createScopeVariable(node, name) { + this.currentScope().variableScope.__define( + name, + new Definition("Variable", name, node, null, null, null) + ); + } + + referencer.prototype.InterfaceDeclaration = function(node) { + createScopeVariable.call(this, node, node.id); + var typeParamScope; + if (node.typeParameters) { + typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node); + } + // TODO: Handle mixins + for (var i = 0; i < node.extends.length; i++) { + visitTypeAnnotation.call(this, node.extends[i]); + } + visitTypeAnnotation.call(this, node.body); + if (typeParamScope) { + this.close(node); + } + }; + + referencer.prototype.TypeAlias = function(node) { + createScopeVariable.call(this, node, node.id); + var typeParamScope; + if (node.typeParameters) { + typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node); + } + if (node.right) { + visitTypeAnnotation.call(this, node.right); + } + if (typeParamScope) { + this.close(node); + } + }; + + referencer.prototype.DeclareModule = referencer.prototype.DeclareFunction = referencer.prototype.DeclareVariable = referencer.prototype.DeclareClass = function( + node + ) { + if (node.id) { + createScopeVariable.call(this, node, node.id); + } + + var typeParamScope; + if (node.typeParameters) { + typeParamScope = nestTypeParamScope.call(this, this.scopeManager, node); + } + if (typeParamScope) { + this.close(node); + } + }; + + referencer._babelEslintPatched = true; +} + +// To patch for each call. +var escope = null; +var escopeAnalyze = null; + +module.exports = function(parserOptions) { + // Patch `Referencer.prototype` once. + if (!escope) { + try { + const modules = getModules(); + monkeypatch(modules); + + // Store to patch for each call. + escope = modules.escope; + escopeAnalyze = modules.escope.analyze; + } catch (err) { + console.error(err.stack); + process.exit(1); + } + } + + // Patch `escope.analyze` based on the current parserOptions. + escope.analyze = function(ast, opts) { + opts = opts || {}; + opts.ecmaVersion = parserOptions.ecmaVersion; + opts.sourceType = parserOptions.sourceType; + opts.nodejsScope = + ast.sourceType === "script" && + (parserOptions.ecmaFeatures && + parserOptions.ecmaFeatures.globalReturn) === true; + + var results = escopeAnalyze.call(this, ast, opts); + return results; + }; +}; diff --git a/package.json b/package.json index 42f0ffdd..7338678d 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "scripts": { "test": "npm run lint && npm run test-only", - "test-only": "mocha", + "test-only": "mocha && mocha --require test/fixtures/preprocess-to-patch.js && mocha --require test/fixtures/use-eslint-old.js", "lint": "eslint index.js babylon-to-espree test", "fix": "eslint index.js babylon-to-espree test --fix", "precommit": "lint-staged", diff --git a/test/babel-eslint.js b/test/babel-eslint.js index 655ca04e..9218a783 100644 --- a/test/babel-eslint.js +++ b/test/babel-eslint.js @@ -4,48 +4,7 @@ var espree = require("espree"); var escope = require("eslint-scope"); var util = require("util"); var unpad = require("dedent"); - -// Checks if the source ast implements the target ast. Ignores extra keys on source ast -function assertImplementsAST(target, source, path) { - if (!path) { - path = []; - } - - function error(text) { - var err = new Error(`At ${path.join(".")}: ${text}:`); - err.depth = path.length + 1; - throw err; - } - - var typeA = target === null ? "null" : typeof target; - var typeB = source === null ? "null" : typeof source; - if (typeA !== typeB) { - error( - `have different types (${typeA} !== ${typeB}) (${target} !== ${source})` - ); - } else if ( - typeA === "object" && - ["RegExp"].indexOf(target.constructor.name) !== -1 && - target.constructor.name !== source.constructor.name - ) { - error( - `object have different constructors (${target.constructor - .name} !== ${source.constructor.name}` - ); - } else if (typeA === "object") { - var keysTarget = Object.keys(target); - for (var i in keysTarget) { - var key = keysTarget[i]; - path.push(key); - assertImplementsAST(target[key], source[key], path); - path.pop(); - } - } else if (target !== source) { - error( - `are different (${JSON.stringify(target)} !== ${JSON.stringify(source)})` - ); - } -} +var assertImplementsAST = require("./fixtures/assert-implements-ast"); function lookup(obj, keypath, backwardsDepth) { if (!keypath) { @@ -539,31 +498,4 @@ describe("Public API", () => { babelEslint.parseNoPatch("foo", {}) ); }); - - /* - * This test ensures that the enhanced referencer does not get used if eslint-scope has already been - * monkeypatched, because this causes some correctness issues. For example, if the enhanced referencer - * is used after the original referencer is monkeypatched, type annotation references are counted twice. - */ - it("does not visit type annotations multiple times after monkeypatching and calling parseForESLint()", () => { - assertImplementsAST( - espree.parse("foo", { sourceType: "module" }), - babelEslint.parse("foo", {}) - ); - const parseResult = babelEslint.parseForESLint( - "type Foo = {}; function x(): Foo {}", - { - eslintVisitorKeys: true, - eslintScopeManager: true, - } - ); - assert(parseResult.visitorKeys); - assert(parseResult.scopeManager); - - const fooVariable = parseResult.scopeManager.getDeclaredVariables( - parseResult.ast.body[0] - )[0]; - - assert.strictEqual(fooVariable.references.length, 1); - }); }); diff --git a/test/fixtures/assert-implements-ast.js b/test/fixtures/assert-implements-ast.js new file mode 100644 index 00000000..61e77e21 --- /dev/null +++ b/test/fixtures/assert-implements-ast.js @@ -0,0 +1,41 @@ +// Checks if the source ast implements the target ast. Ignores extra keys on source ast +module.exports = function assertImplementsAST(target, source, path) { + if (!path) { + path = []; + } + + function error(text) { + var err = new Error(`At ${path.join(".")}: ${text}:`); + err.depth = path.length + 1; + throw err; + } + + var typeA = target === null ? "null" : typeof target; + var typeB = source === null ? "null" : typeof source; + if (typeA !== typeB) { + error( + `have different types (${typeA} !== ${typeB}) (${target} !== ${source})` + ); + } else if ( + typeA === "object" && + ["RegExp"].indexOf(target.constructor.name) !== -1 && + target.constructor.name !== source.constructor.name + ) { + error( + `object have different constructors (${target.constructor + .name} !== ${source.constructor.name}` + ); + } else if (typeA === "object") { + var keysTarget = Object.keys(target); + for (var i in keysTarget) { + var key = keysTarget[i]; + path.push(key); + assertImplementsAST(target[key], source[key], path); + path.pop(); + } + } else if (target !== source) { + error( + `are different (${JSON.stringify(target)} !== ${JSON.stringify(source)})` + ); + } +}; diff --git a/test/fixtures/preprocess-to-patch.js b/test/fixtures/preprocess-to-patch.js new file mode 100644 index 00000000..1dbfc172 --- /dev/null +++ b/test/fixtures/preprocess-to-patch.js @@ -0,0 +1,5 @@ +"use strict" +const babelEslint = require("../..") + +// Apply monkeypatch to eslint-scope. +babelEslint.parse("var x = 0;") diff --git a/test/fixtures/use-eslint-old.js b/test/fixtures/use-eslint-old.js new file mode 100644 index 00000000..e26a39c8 --- /dev/null +++ b/test/fixtures/use-eslint-old.js @@ -0,0 +1,12 @@ +"use strict" + +var Module = require('module'); +var originalRequire = Module.prototype.require; + +// Override to eslint-old +Module.prototype.require = function () { + if (arguments[0] === "eslint") { + arguments[0] = "eslint-old"; + } + return originalRequire.apply(this, arguments); +}; diff --git a/test/non-regression.js b/test/non-regression.js index e21a95a0..8ff223b6 100644 --- a/test/non-regression.js +++ b/test/non-regression.js @@ -1,7 +1,6 @@ /*eslint-env mocha*/ "use strict"; var eslint = require("eslint"); -var oldEslint = require("eslint-old"); var unpad = require("dedent"); function verifyAndAssertMessagesWithSpecificESLint( @@ -71,16 +70,14 @@ function verifyAndAssertMessages( sourceType, overrideConfig ) { - [new eslint.Linter(), new oldEslint.Linter()].forEach(linter => { - verifyAndAssertMessagesWithSpecificESLint( - code, - rules, - expectedMessages, - sourceType, - overrideConfig, - linter - ); - }); + verifyAndAssertMessagesWithSpecificESLint( + code, + rules, + expectedMessages, + sourceType, + overrideConfig, + new eslint.Linter() + ); } describe("verify", () => { diff --git a/test/z_eslint-plugin-import.js b/test/z_eslint-plugin-import.js deleted file mode 100644 index 01a80ec2..00000000 --- a/test/z_eslint-plugin-import.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; - -const eslint = require("eslint"); - -describe("https://github.com/babel/babel-eslint/issues/558", () => { - it("don't crash with eslint-plugin-import", () => { - const engine = new eslint.CLIEngine({ ignore: false }); - engine.executeOnFiles([ - "test/fixtures/eslint-plugin-import/a.js", - "test/fixtures/eslint-plugin-import/b.js", - "test/fixtures/eslint-plugin-import/c.js", - ]); - }); -}); diff --git a/test/z_parser-for-eslint-after-patched.js b/test/z_parser-for-eslint-after-patched.js new file mode 100644 index 00000000..a7d6882d --- /dev/null +++ b/test/z_parser-for-eslint-after-patched.js @@ -0,0 +1,45 @@ +"use strict"; + +const eslint = require("eslint"); +const assert = require("assert"); +const babelEslint = require(".."); +const espree = require("espree"); +var assertImplementsAST = require("./fixtures/assert-implements-ast"); + +describe("https://github.com/babel/babel-eslint/issues/558", () => { + it("don't crash with eslint-plugin-import", () => { + const engine = new eslint.CLIEngine({ ignore: false }); + engine.executeOnFiles([ + "test/fixtures/eslint-plugin-import/a.js", + "test/fixtures/eslint-plugin-import/b.js", + "test/fixtures/eslint-plugin-import/c.js", + ]); + }); + + /* + * This test ensures that the enhanced referencer does not get used if eslint-scope has already been + * monkeypatched, because this causes some correctness issues. For example, if the enhanced referencer + * is used after the original referencer is monkeypatched, type annotation references are counted twice. + */ + it("does not visit type annotations multiple times after monkeypatching and calling parseForESLint()", () => { + assertImplementsAST( + espree.parse("foo", { sourceType: "module" }), + babelEslint.parse("foo", {}) + ); + const parseResult = babelEslint.parseForESLint( + "type Foo = {}; function x(): Foo {}", + { + eslintVisitorKeys: true, + eslintScopeManager: true, + } + ); + assert(parseResult.visitorKeys); + assert(parseResult.scopeManager); + + const fooVariable = parseResult.scopeManager.getDeclaredVariables( + parseResult.ast.body[0] + )[0]; + + assert.strictEqual(fooVariable.references.length, 1); + }); +}); From 11dce78878aac3b293076259528de907ce226093 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Mon, 25 Dec 2017 15:38:47 +0900 Subject: [PATCH 6/6] remove process.exit(1) --- lib/patch-eslint-scope.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/patch-eslint-scope.js b/lib/patch-eslint-scope.js index 44691439..aec71fc6 100644 --- a/lib/patch-eslint-scope.js +++ b/lib/patch-eslint-scope.js @@ -347,17 +347,12 @@ var escopeAnalyze = null; module.exports = function(parserOptions) { // Patch `Referencer.prototype` once. if (!escope) { - try { - const modules = getModules(); - monkeypatch(modules); + const modules = getModules(); + monkeypatch(modules); - // Store to patch for each call. - escope = modules.escope; - escopeAnalyze = modules.escope.analyze; - } catch (err) { - console.error(err.stack); - process.exit(1); - } + // Store to patch for each call. + escope = modules.escope; + escopeAnalyze = modules.escope.analyze; } // Patch `escope.analyze` based on the current parserOptions. @@ -370,7 +365,6 @@ module.exports = function(parserOptions) { (parserOptions.ecmaFeatures && parserOptions.ecmaFeatures.globalReturn) === true; - var results = escopeAnalyze.call(this, ast, opts); - return results; + return escopeAnalyze.call(this, ast, opts); }; };