From c680079086227dd0561bc6b84e2899b1c7088c28 Mon Sep 17 00:00:00 2001 From: JounQin Date: Tue, 12 Mar 2024 13:22:02 +0800 Subject: [PATCH] feat!: republish as eslint-plugin-import-x (#30) --- .babelrc | 18 - .coveralls.yml | 2 - .editorconfig | 14 +- .eslintignore | 14 +- .eslintrc | 248 - .eslintrc.js | 68 + .github/FUNDING.yml | 12 - .github/workflows/node-4+.yml | 123 - .github/workflows/node-pretest.yml | 31 - .github/workflows/packages.yml | 60 - .github/workflows/rebase.yml | 9 - .github/workflows/require-allow-edits.yml | 17 - .gitignore | 15 - .npmrc | 2 - .nycrc | 19 - .prettierrc.mjs | 14 + .travis.yml | 40 - CHANGELOG.md | 1937 ------ CONTRIBUTING.md | 32 +- README.md | 249 +- RELEASE.md | 54 - SECURITY.md | 11 - appveyor.yml | 165 - babel.config.js | 25 + config/electron.js | 4 +- config/errors.js | 15 +- config/react-native.js | 4 +- config/react.js | 6 +- config/recommended.js | 20 +- config/stage-0.js | 6 +- config/typescript.js | 16 +- config/warnings.js | 10 +- docs/rules/consistent-type-specifier-style.md | 10 +- docs/rules/default.md | 26 +- docs/rules/dynamic-import-chunkname.md | 95 +- docs/rules/export.md | 12 +- docs/rules/exports-last.md | 2 +- docs/rules/extensions.md | 73 +- docs/rules/first.md | 14 +- docs/rules/group-exports.md | 19 +- docs/rules/imports-first.md | 2 +- docs/rules/max-dependencies.md | 30 +- docs/rules/named.md | 16 +- docs/rules/namespace.md | 19 +- docs/rules/newline-after-import.md | 85 +- docs/rules/no-absolute-path.md | 38 +- docs/rules/no-amd.md | 14 +- docs/rules/no-anonymous-default-export.md | 20 +- docs/rules/no-commonjs.md | 46 +- docs/rules/no-cycle.md | 39 +- docs/rules/no-default-export.md | 20 +- docs/rules/no-deprecated.md | 20 +- docs/rules/no-duplicates.md | 8 +- docs/rules/no-dynamic-require.md | 14 +- docs/rules/no-empty-named-blocks.md | 4 +- docs/rules/no-extraneous-dependencies.md | 30 +- docs/rules/no-import-module-exports.md | 26 +- docs/rules/no-internal-modules.md | 46 +- docs/rules/no-mutable-exports.md | 6 +- docs/rules/no-named-as-default-member.md | 16 +- docs/rules/no-named-as-default.md | 22 +- docs/rules/no-named-default.md | 14 +- docs/rules/no-named-export.md | 24 +- docs/rules/no-namespace.md | 16 +- docs/rules/no-nodejs-modules.md | 28 +- docs/rules/no-relative-packages.md | 14 +- docs/rules/no-relative-parent-imports.md | 26 +- docs/rules/no-restricted-paths.md | 75 +- docs/rules/no-self-import.md | 14 +- docs/rules/no-unassigned-import.md | 16 +- docs/rules/no-unresolved.md | 48 +- docs/rules/no-unused-modules.md | 30 +- docs/rules/no-useless-path-segments.md | 40 +- docs/rules/no-webpack-loader-syntax.md | 20 +- docs/rules/order.md | 264 +- docs/rules/prefer-default-export.md | 57 +- docs/rules/unambiguous.md | 24 +- import.sublime-project | 27 - jest.config.js | 19 + memo-parser/.nycrc | 19 - memo-parser/README.md | 21 - memo-parser/index.js | 41 - memo-parser/package.json | 34 - package.json | 147 +- resolvers/README.md | 90 - resolvers/node/.nycrc | 15 - resolvers/node/CHANGELOG.md | 79 - resolvers/node/README.md | 44 - resolvers/node/index.js | 66 - resolvers/node/package.json | 41 - resolvers/node/test/data.json | 0 resolvers/node/test/dot-node.node | 0 resolvers/node/test/native.js | 1 - resolvers/node/test/native.mjs | 1 - resolvers/node/test/native.node | 0 .../test/package-mains/jsnext/package.json | 4 - .../module-and-jsnext/package.json | 5 - .../test/package-mains/module-broken/main.js | 1 - .../package-mains/module-broken/package.json | 4 - .../test/package-mains/module/package.json | 4 - .../node/test/package-mains/package.json | 1 - resolvers/node/test/packageMains.js | 31 - resolvers/node/test/paths.js | 58 - resolvers/webpack/.babelrc | 1 - resolvers/webpack/.nycrc | 15 - resolvers/webpack/CHANGELOG.md | 246 - resolvers/webpack/README.md | 99 - resolvers/webpack/config.js | 8 - resolvers/webpack/index.js | 451 -- resolvers/webpack/package.json | 60 - resolvers/webpack/test/alias.js | 139 - .../test/config-extensions/package.json | 1 - .../test/config-extensions/src/main-module.js | 0 .../config-extensions/webpack.config.babel.js | 23 - resolvers/webpack/test/config.js | 158 - .../webpack/test/custom-extensions/bar.coffee | 0 .../webpack/test/custom-extensions/baz.web.js | 0 .../webpack/test/custom-extensions/foo.js | 0 .../test/custom-extensions/package.json | 1 - .../test/custom-extensions/webpack.config.js | 3 - resolvers/webpack/test/example.js | 11 - resolvers/webpack/test/extensions.js | 33 - resolvers/webpack/test/externals.js | 68 - resolvers/webpack/test/fallback.js | 29 - .../test/files/bower_components/typeahead.js | 0 .../webpack/test/files/fallback/fb-module.js | 0 .../test/files/fallback/jsx/some-fb-file.js | 0 .../test/files/fallback/jsx/some-file.js | 0 .../test/files/fallback/main-module.js | 0 resolvers/webpack/test/files/foo.web.js | 0 .../files/node_modules/some-module/index.js | 0 .../webpack-resolver-plugin-test/index.js | 57 - resolvers/webpack/test/files/package.json | 1 - .../some/absolute.path.webpack.config.js | 21 - .../files/some/absolutely/goofy/path/foo.js | 0 resolvers/webpack/test/files/some/bar/bar.js | 0 .../webpack/test/files/some/goofy/path/bar.js | 0 .../webpack/test/files/some/goofy/path/foo.js | 0 .../webpack/test/files/src/jsx/some-file.js | 0 .../webpack/test/files/src/main-module.js | 0 .../test/files/webpack.array-root.config.js | 11 - .../test/files/webpack.config.async.js | 7 - .../test/files/webpack.config.babel.js | 23 - .../test/files/webpack.config.garbage.js | 1 - .../webpack/test/files/webpack.config.js | 40 - .../test/files/webpack.config.multiple.js | 25 - ...webpack.config.webpack5.async-externals.js | 21 - .../test/files/webpack.config.webpack5.js | 27 - .../test/files/webpack.function.config.js | 44 - .../files/webpack.function.config.multiple.js | 43 - resolvers/webpack/test/loaders.js | 33 - resolvers/webpack/test/modules.js | 23 - .../webpack/test/package-mains/jam/index.js | 0 .../webpack/test/package-mains/jam/jam.js | 0 .../test/package-mains/jam/package.json | 4 - .../test/package-mains/jsnext/package.json | 4 - .../test/package-mains/jsnext/src/index.js | 0 .../module-and-jsnext/package.json | 5 - .../module-and-jsnext/src/index.js | 0 .../test/package-mains/module-broken/main.js | 1 - .../package-mains/module-broken/package.json | 4 - .../test/package-mains/module/package.json | 4 - .../test/package-mains/module/src/index.js | 0 .../webpack/test/package-mains/package.json | 1 - .../test/package-mains/webpack.alt.config.js | 3 - .../test/package-mains/webpack.config.js | 0 .../test/package-mains/webpack/index.js | 0 .../test/package-mains/webpack/package.json | 4 - .../test/package-mains/webpack/webpack.js | 0 resolvers/webpack/test/packageMains.js | 57 - resolvers/webpack/test/plugins.js | 31 - resolvers/webpack/test/root.js | 46 - scripts/GetCI/GetCI.psm1 | 12 - scripts/ci.cmd | 8 - scripts/copyMetafiles.js | 22 - scripts/resolverDirectories.js | 3 - scripts/testAll.js | 20 - src/ExportMap.js | 834 ++- src/core/importType.js | 146 +- src/core/packagePath.js | 22 +- src/core/staticRequire.js | 16 +- src/docsUrl.js | 6 +- src/importDeclaration.js | 4 +- src/index.js | 4 +- src/rules/consistent-type-specifier-style.js | 181 +- src/rules/default.js | 29 +- src/rules/dynamic-import-chunkname.js | 113 +- src/rules/export.js | 203 +- src/rules/exports-last.js | 30 +- src/rules/extensions.js | 146 +- src/rules/first.js | 155 +- src/rules/group-exports.js | 121 +- src/rules/imports-first.js | 10 +- src/rules/max-dependencies.js | 41 +- src/rules/named.js | 142 +- src/rules/namespace.js | 194 +- src/rules/newline-after-import.js | 185 +- src/rules/no-absolute-path.js | 29 +- src/rules/no-amd.js | 34 +- src/rules/no-anonymous-default-export.js | 59 +- src/rules/no-commonjs.js | 112 +- src/rules/no-cycle.js | 184 +- src/rules/no-default-export.js | 34 +- src/rules/no-deprecated.js | 163 +- src/rules/no-duplicates.js | 342 +- src/rules/no-dynamic-require.js | 42 +- src/rules/no-empty-named-blocks.js | 102 +- src/rules/no-extraneous-dependencies.js | 284 +- src/rules/no-import-module-exports.js | 93 +- src/rules/no-internal-modules.js | 95 +- src/rules/no-mutable-exports.js | 27 +- src/rules/no-named-as-default-member.js | 77 +- src/rules/no-named-as-default.js | 38 +- src/rules/no-named-default.js | 18 +- src/rules/no-named-export.js | 21 +- src/rules/no-namespace.js | 215 +- src/rules/no-nodejs-modules.js | 23 +- src/rules/no-relative-packages.js | 63 +- src/rules/no-relative-parent-imports.js | 40 +- src/rules/no-restricted-paths.js | 205 +- src/rules/no-self-import.js | 23 +- src/rules/no-unassigned-import.js | 53 +- src/rules/no-unresolved.js | 44 +- src/rules/no-unused-modules.js | 896 +-- src/rules/no-useless-path-segments.js | 92 +- src/rules/no-webpack-loader-syntax.js | 20 +- src/rules/order.js | 781 ++- src/rules/prefer-default-export.js | 116 +- src/rules/unambiguous.js | 16 +- src/utils/ModuleCache.d.ts | 24 + {utils => src/utils}/ModuleCache.js | 44 +- src/utils/declaredScope.d.ts | 8 + src/utils/declaredScope.js | 13 + src/utils/hash.d.ts | 14 + src/utils/hash.js | 69 + src/utils/ignore.d.ts | 15 + src/utils/ignore.js | 75 + src/utils/module-require.d.ts | 3 + src/utils/module-require.js | 40 + src/utils/moduleVisitor.d.ts | 26 + {utils => src/utils}/moduleVisitor.js | 157 +- src/utils/parse.d.ts | 9 + {utils => src/utils}/parse.js | 157 +- src/utils/pkgDir.d.ts | 3 + src/utils/pkgDir.js | 12 + src/utils/pkgUp.d.ts | 3 + {utils => src/utils}/pkgUp.js | 26 +- src/utils/readPkgUp.d.ts | 8 + {utils => src/utils}/readPkgUp.js | 20 +- src/utils/resolve.d.ts | 45 + src/utils/resolve.js | 274 + src/utils/types.d.ts | 9 + src/utils/unambiguous.d.ts | 7 + {utils => src/utils}/unambiguous.js | 17 +- src/utils/visit.d.ts | 10 + src/utils/visit.js | 30 + test/cli.spec.js | 151 + test/config/typescript.spec.js | 12 + test/core/docsUrl.spec.js | 16 + {tests/src => test}/core/eslintParser.js | 4 +- test/core/getExports.spec.js | 520 ++ test/core/hash.spec.js | 108 + test/core/ignore.spec.js | 101 + test/core/importType.spec.js | 379 ++ test/core/parse.spec.js | 255 + {tests/src => test}/core/parseStubParser.js | 2 +- test/core/resolve.spec.js | 684 ++ test/fixtures/.eslintrc.js | 325 + .../fixtures}/@importType/index.js | 0 .../files => test/fixtures}/@my-alias/fn.js | 0 {tests/files => test/fixtures}/CaseyKasem.js | 0 .../alternate-root/in-alternate-root.js | 0 {tests/files => test/fixtures}/bar.coffee | 0 test/fixtures/bar.js | 4 + {tests/files => test/fixtures}/bar.json | 0 {tests/files => test/fixtures}/bar.jsx | 0 {tests/files => test/fixtures}/bar/index.js | 0 .../fixtures}/broken-trampoline.js | 0 .../as-array-bundle-deps/package.json | 6 + .../as-object/package.json | 6 + .../race-condition/package.json | 9 + test/fixtures/color.js | 1 + .../files => test/fixtures}/common-module.js | 4 +- {tests/files => test/fixtures}/common.js | 2 +- .../fixtures}/commonjs-namespace/a.js | 0 .../fixtures}/commonjs-namespace/b.js | 0 {tests/files => test/fixtures}/component.html | 22 +- test/fixtures/constants/index.js | 1 + .../fixtures}/cycles/depth-zero.js | 0 test/fixtures/cycles/es6/depth-one-dynamic.js | 1 + .../fixtures/cycles/es6/depth-one-reexport.js | 1 + test/fixtures/cycles/es6/depth-one.js | 2 + .../cycles/es6/depth-three-indirect.js | 2 +- test/fixtures/cycles/es6/depth-three-star.js | 2 + test/fixtures/cycles/es6/depth-two.js | 2 + test/fixtures/cycles/external-depth-two.js | 2 + test/fixtures/cycles/external/depth-one.js | 2 + test/fixtures/cycles/flow-typeof.js | 4 + test/fixtures/cycles/flow-types-depth-one.js | 6 + .../fixtures}/cycles/flow-types-depth-two.js | 0 ...low-types-only-importing-multiple-types.js | 3 + .../cycles/flow-types-only-importing-type.js | 3 + .../cycles/flow-types-some-type-imports.js | 0 test/fixtures/cycles/flow-types.js | 6 + test/fixtures/cycles/ignore/.eslintrc | 5 + test/fixtures/cycles/ignore/index.js | 2 + test/fixtures/cycles/intermediate-ignore.js | 2 + test/fixtures/data.json | 3 + .../fixtures}/deep-deprecated.js | 2 +- {tests/files => test/fixtures}/deep-es7/a.js | 0 {tests/files => test/fixtures}/deep-es7/b.js | 0 {tests/files => test/fixtures}/deep-es7/c.js | 0 test/fixtures/deep-es7/d.js | 1 + {tests/files => test/fixtures}/deep/a.js | 2 +- {tests/files => test/fixtures}/deep/b.js | 0 {tests/files => test/fixtures}/deep/c.js | 2 +- .../files => test/fixtures}/deep/cache-1.js | 0 .../files => test/fixtures}/deep/cache-2a.js | 0 .../files => test/fixtures}/deep/cache-2b.js | 0 test/fixtures/deep/d.js | 1 + .../files => test/fixtures}/deep/default.js | 2 +- test/fixtures/default-class.js | 1 + .../default-export-default-property.js | 3 +- .../fixtures}/default-export-from-ignored.js | 0 .../fixtures}/default-export-from-named.js | 0 .../fixtures}/default-export-from.js | 0 .../default-export-namespace-string.js | 1 + test/fixtures/default-export-string.js | 5 + test/fixtures/default-export.js | 3 + .../fixtures}/deprecated-file.js | 2 +- {tests/files => test/fixtures}/deprecated.js | 41 +- test/fixtures/dynamic-import-in-commonjs.js | 5 + .../fixtures}/empty-folder/anchor.txt | 0 test/fixtures/empty-named-blocks.js | 1 + .../fixtures}/empty/package.json | 0 {tests/files => test/fixtures}/export-all.js | 0 .../export-default-string-and-named.js | 4 + .../files => test/fixtures}/export-props.js | 0 test/fixtures/export-star-2/middle.js | 1 + test/fixtures/export-star-2/upstream.js | 1 + test/fixtures/export-star-3/b.ts | 1 + .../fixtures}/export-star-3/c.ts | 0 .../fixtures/export-star-4/module/feature.jsx | 3 + test/fixtures/export-star-4/module/index.ts | 1 + test/fixtures/export-star/extfield.js | 1 + test/fixtures/export-star/extfield2.js | 1 + test/fixtures/export-star/models.js | 2 + .../fixtures}/exports-calc-keys.js | 0 .../fixtures}/exports-missing.js | 0 .../files => test/fixtures}/file.with.dot.js | 0 {tests/files => test/fixtures}/flowtypes.js | 9 +- test/fixtures/foo-bar-resolver-invalid.js | 1 + .../fixtures}/foo-bar-resolver-no-version.js | 2 +- .../fixtures}/foo-bar-resolver-v1.js | 10 +- .../fixtures}/foo-bar-resolver-v2.js | 6 +- {tests/files => test/fixtures}/foobar.json | 0 .../fixtures}/ignore.invalid.extension | 0 .../fixtures}/importType/index.js | 0 {tests/files => test/fixtures}/index.js | 0 .../internal-modules/api/service}/index.js | 0 .../fixtures}/internal-modules/package.json | 0 .../internal-modules/plugins/plugin.js | 0 .../plugins/plugin2/app}/index.js | 0 .../plugins/plugin2}/index.js | 0 .../plugins/plugin2/internal.js | 0 .../fixtures}/internal-modules/test.js | 0 .../typescript/plugin2/app/index.ts | 0 .../typescript/plugin2/index.ts | 0 .../typescript/plugin2/internal.ts | 0 .../internal-modules/typescript/plugins.ts | 0 test/fixtures/issue-195/Bookings.js | 1 + test/fixtures/issue-195/Endpoints.js | 2 + test/fixtures/issue-195/Users.js | 1 + .../issue-370-commonjs-namespace/bar.js | 2 + .../issue-370-commonjs-namespace/foo.js | 2 + .../fixtures}/issue210.config.js | 2 +- test/fixtures/issue210.js | 1 + {tests/files => test/fixtures}/jsx.js | 0 .../fixtures}/jsx/AnotherComponent.jsx | 0 {tests/files => test/fixtures}/jsx/App.js | 6 +- test/fixtures/jsx/FooES7.js | 17 + .../fixtures}/jsx/MyCoolComponent.jsx | 6 +- .../fixtures}/jsx/MyUnCoolComponent.jsx | 6 +- .../files => test/fixtures}/jsx/bar/baz.jsx | 10 +- test/fixtures/jsx/bar/index.js | 2 + .../files => test/fixtures}/jsx/bar/qux.jsx | 7 +- {tests/files => test/fixtures}/jsx/named.jsx | 0 test/fixtures/jsx/re-export.js | 1 + .../fixtures}/just-json-files/.eslintrc.json | 10 +- .../fixtures}/just-json-files/invalid.json | 0 .../fixtures}/load-error-resolver.js | 0 {tests/files => test/fixtures}/malformed.js | 0 .../fixtures}/missing-entrypoint/package.json | 0 .../files => test/fixtures}/mixed-exports.js | 2 +- .../fixtures}/monorepo/package.json | 0 .../packages/nested-package/package.json | 0 {tests/files => test/fixtures}/mutator.js | 0 .../fixtures}/named-default-export.js | 0 test/fixtures/named-export-collision/a.js | 1 + test/fixtures/named-export-collision/b.js | 1 + test/fixtures/named-exports.js | 20 + test/fixtures/named-trampoline.js | 2 + test/fixtures/narcissist.js | 2 + test/fixtures/nested-common.js | 3 + .../fixtures}/no-self-import-folder/index.js | 0 .../files => test/fixtures}/no-self-import.js | 0 ...rary-module-namespace-identifier-name-a.js | 2 + ...rary-module-namespace-identifier-name-b.js | 1 + ...rary-module-namespace-identifier-name-c.js | 2 + .../fixtures}/no-unused-modules/bin.js | 0 .../no-unused-modules/binObject/index.js | 0 .../no-unused-modules/binObject/package.json | 4 +- .../fixtures}/no-unused-modules/browser.js | 0 .../no-unused-modules/browserObject/index.js | 0 .../browserObject/package.json | 0 .../fixtures}/no-unused-modules/cjs.js | 4 +- .../no-unused-modules/destructuring-a.js | 1 + .../no-unused-modules/destructuring-b.js | 5 + .../no-unused-modules/dynamic-import-js-2.js | 12 + .../no-unused-modules/dynamic-import-js.js | 5 + .../fixtures}/no-unused-modules/empty_file.js | 0 .../exports-for-dynamic-js-2.js | 0 .../exports-for-dynamic-js.js | 5 + .../fixtures}/no-unused-modules/file-0.js | 2 +- .../fixtures}/no-unused-modules/file-a.js | 0 .../fixtures}/no-unused-modules/file-b.js | 0 .../fixtures}/no-unused-modules/file-c.js | 0 .../fixtures}/no-unused-modules/file-d.js | 0 .../no-unused-modules/file-destructured-1.js | 2 + .../no-unused-modules/file-destructured-2.js | 1 + .../fixtures}/no-unused-modules/file-e.js | 0 .../fixtures}/no-unused-modules/file-f.js | 0 .../fixtures}/no-unused-modules/file-g.js | 0 .../fixtures}/no-unused-modules/file-h.js | 0 .../fixtures}/no-unused-modules/file-i.js | 0 .../no-unused-modules/file-ignored-a.js | 0 .../no-unused-modules/file-ignored-b.js | 0 .../no-unused-modules/file-ignored-c.js | 0 .../no-unused-modules/file-ignored-d.js | 0 .../no-unused-modules/file-ignored-e.js | 0 .../no-unused-modules/file-ignored-l.js | 0 .../fixtures}/no-unused-modules/file-j.js | 0 .../fixtures}/no-unused-modules/file-k.js | 0 .../fixtures}/no-unused-modules/file-l.js | 0 .../fixtures}/no-unused-modules/file-m.js | 0 .../fixtures}/no-unused-modules/file-n.js | 0 .../fixtures}/no-unused-modules/file-o.js | 0 test/fixtures/no-unused-modules/file-p.js | 1 + .../fixtures}/no-unused-modules/file-q.js | 0 .../fixtures}/no-unused-modules/file-s.js | 0 .../fixtures}/no-unused-modules/filte-r.js | 0 .../fixtures/no-unused-modules/flow/flow-0.js | 1 + .../fixtures/no-unused-modules/flow/flow-1.js | 3 + .../fixtures/no-unused-modules/flow/flow-2.js | 3 + .../fixtures/no-unused-modules/flow/flow-3.js | 1 + .../fixtures/no-unused-modules/flow/flow-4.js | 3 + .../no-unused-modules/import-export-1.js | 2 + .../no-unused-modules/import-export-2.js | 2 + .../no-unused-modules/jsx/file-jsx-a.jsx | 3 + .../no-unused-modules/jsx/file-jsx-b.jsx | 1 + .../fixtures}/no-unused-modules/main/index.js | 0 .../no-unused-modules/node_modules.js | 0 test/fixtures/no-unused-modules/package.json | 5 + .../no-unused-modules/prefix-child.js | 1 + .../no-unused-modules/prefix-parent-bom.js | 1 + .../prefix-parent-bomhashbang.js | 2 + .../prefix-parent-hashbang.js | 2 + .../no-unused-modules/prefix-parent.js | 2 + .../no-unused-modules/privatePkg/index.js | 0 .../no-unused-modules/privatePkg/package.json | 4 + .../renameDefault-2/ComponentA.js | 0 .../renameDefault-2/ComponentB.js | 0 .../renameDefault-2/components.js | 2 + .../renameDefault-2/usage.js | 0 .../renameDefault/Component.js | 0 .../renameDefault/components.js | 0 .../no-unused-modules/renameDefault/usage.js | 0 .../typescript/dynamic-import-ts.ts | 5 + .../typescript/exports-for-dynamic-ts.ts | 0 .../typescript/file-ts-a-import-type.ts | 9 + .../no-unused-modules/typescript/file-ts-a.ts | 8 + .../typescript/file-ts-b-unused.ts | 1 + .../typescript/file-ts-b-used-as-type.ts | 1 + .../no-unused-modules/typescript/file-ts-b.ts | 1 + .../typescript/file-ts-c-unused.ts | 1 + .../typescript/file-ts-c-used-as-type.ts | 1 + .../no-unused-modules/typescript/file-ts-c.ts | 1 + .../typescript/file-ts-d-unused.ts | 1 + .../typescript/file-ts-d-used-as-type.ts | 1 + .../no-unused-modules/typescript/file-ts-d.ts | 1 + .../typescript/file-ts-e-unused.ts | 3 + .../typescript/file-ts-e-used-as-type.ts | 3 + .../no-unused-modules/typescript/file-ts-e.ts | 3 + .../typescript/file-ts-f-import-type.ts | 1 + .../no-unused-modules/typescript/file-ts-f.ts | 1 + .../typescript/file-ts-g-used-as-type.ts | 0 .../no-unused-modules/typescript/file-ts-g.ts | 0 .../order-redirect-scoped/module/package.json | 0 .../other-module/file.js | 0 .../order-redirect-scoped/package.json | 0 .../order-redirect/module/package.json | 0 .../order-redirect/other-module/file.js | 0 .../fixtures}/order-redirect/package.json | 0 .../fixtures/package-named}/index.js | 0 .../fixtures}/package-named/package.json | 2 +- .../fixtures/package-scoped}/index.js | 0 .../fixtures}/package-scoped/package.json | 0 {tests/files => test/fixtures}/package.json | 16 +- .../fixtures/package}/index.js | 0 .../fixtures}/package/package.json | 2 +- {tests/files => test/fixtures}/qc.js | 2 +- .../fixtures}/re-export-common-star.js | 0 .../fixtures}/re-export-common.js | 0 .../fixtures}/re-export-default.js | 2 +- .../fixtures}/re-export-names.js | 0 .../fixtures}/re-export-node_modules.js | 0 {tests/files => test/fixtures}/re-export.js | 0 {tests/files => test/fixtures}/redux.js | 0 .../fixtures}/restricted-paths/client/a.js | 0 .../restricted-paths/client/one/a.js | 0 .../fixtures}/restricted-paths/server/b.js | 0 .../fixtures}/restricted-paths/server/c.js | 0 .../fixtures}/restricted-paths/server/c.ts | 0 .../restricted-paths/server/one/a.js | 0 .../restricted-paths/server/one/b.js | 0 .../restricted-paths/server/three/a.js | 0 .../restricted-paths/server/two-new/a.js | 0 .../restricted-paths/server/two/a.js | 0 .../fixtures}/sibling-with-names.js | 0 .../fixtures}/src-root/src-bar.js | 0 .../fixtures}/symlinked-module/index.js | 0 .../fixtures}/symlinked-module/package.json | 0 test/fixtures/test-module/index.js | 1 + {tests/files => test/fixtures}/test.coffee | 0 {tests/files => test/fixtures}/test.giffy | 0 .../fixtures}/tomdoc-deprecated.js | 18 +- {tests/files => test/fixtures}/trampoline.js | 0 .../files => test/fixtures}/ts-deprecated.ts | 0 .../fixtures}/typescript-d-ts/.eslintrc | 8 +- test/fixtures/typescript-d-ts/file1.ts | 6 + .../fixtures}/typescript-d-ts/file2.ts | 0 .../typescript-declare-interface.d.ts | 4 +- test/fixtures/typescript-declare-module.ts | 3 + .../fixtures}/typescript-declare-nested.d.ts | 8 +- .../fixtures}/typescript-declare.d.ts | 12 +- test/fixtures/typescript-default.ts | 1 + .../index.d.ts | 0 .../tsconfig.json | 0 .../index.d.ts | 2 +- .../tsconfig.json | 5 + ...pescript-export-assign-default-reexport.ts | 2 + .../typescript-export-assign-default.d.ts | 3 + .../typescript-export-assign-function.ts | 1 + .../typescript-export-assign-mixed.d.ts | 6 +- ...script-export-assign-namespace-merged.d.ts | 14 +- .../typescript-export-assign-namespace.d.ts | 14 +- .../typescript-export-assign-object/index.ts | 4 +- .../tsconfig.json | 5 + .../typescript-export-assign-property.ts | 3 + .../index.d.ts | 19 + .../tsconfig.json | 5 + .../typescript-extended-config}/index.d.ts | 2 +- .../tsconfig.base.json | 5 + .../typescript-extended-config/tsconfig.json | 0 .../typescript-no-compiler-options/index.d.ts | 0 .../tsconfig.json | 1 + {tests/files => test/fixtures}/typescript.ts | 8 +- test/fixtures/umd.js | 9 + .../unused-modules-reexport-crash/src/App.tsx | 5 + .../src/index.tsx | 3 + .../src/magic/index.js | 0 .../src/magic/test.js | 6 +- .../files => test/fixtures}/webpack.config.js | 4 +- test/fixtures/webpack.empty.config.js | 1 + .../flow-typed/npm/myflowtyped_v1.x.x.js | 3 +- .../fixtures}/with-flow-typed/package.json | 0 .../fixtures}/with-syntax-error/package.json | 0 .../package.json | 0 test/index.js | 1 + test/mocha.opts | 3 - test/package.spec.js | 75 + .../consistent-type-specifier-style.spec.js | 302 +- test/rules/default.spec.js | 348 + .../rules/dynamic-import-chunkname.spec.js | 1330 ++-- test/rules/export.spec.js | 605 ++ .../rules/exports-last.spec.js | 14 +- .../rules/extensions.spec.js | 234 +- .../first.js => test/rules/first.spec.js | 105 +- .../rules/group-exports.spec.js | 230 +- test/rules/max-dependencies.spec.js | 143 + .../named.js => test/rules/named.spec.js | 441 +- test/rules/namespace.spec.js | 469 ++ .../rules/newline-after-import.spec.js | 565 +- .../rules/no-absolute-path.spec.js | 12 +- test/rules/no-amd.spec.js | 59 + .../rules/no-anonymous-default-export.spec.js | 160 + test/rules/no-commonjs.spec.js | 197 + test/rules/no-cycle.spec.js | 325 + .../rules/no-default-export.spec.js | 26 +- .../rules/no-deprecated.spec.js | 176 +- test/rules/no-duplicates.spec.js | 933 +++ .../rules/no-dynamic-require.spec.js | 38 +- test/rules/no-empty-named-blocks.spec.js | 141 + test/rules/no-extraneous-dependencies.spec.js | 692 ++ .../rules/no-import-module-exports.spec.js | 47 +- .../rules/no-internal-modules.spec.js | 392 +- .../rules/no-mutable-exports.spec.js | 35 +- test/rules/no-named-as-default-member.spec.js | 82 + test/rules/no-named-as-default.spec.js | 111 + .../rules/no-named-default.spec.js | 46 +- test/rules/no-named-export.spec.js | 223 + test/rules/no-namespace.spec.js | 144 + test/rules/no-nodejs-modules.spec.js | 180 + .../rules/no-relative-packages.spec.js | 64 +- test/rules/no-relative-parent-imports.spec.js | 127 + test/rules/no-restricted-paths.spec.js | 1138 ++++ .../rules/no-self-import.spec.js | 12 +- .../rules/no-unassigned-import.spec.js | 14 +- .../rules/no-unresolved.spec.js | 235 +- .../rules/no-unused-modules.spec.js | 900 +-- .../rules/no-useless-path-segments.spec.js | 148 +- .../rules/no-webpack-loader-syntax.spec.js | 90 +- .../order.js => test/rules/order.spec.js | 1870 +++--- .../rules/prefer-default-export.spec.js | 330 +- .../rules/unambiguous.spec.js | 12 +- {tests/src => test}/utils.js | 75 +- tests/dep-time-travel.sh | 52 - tests/files/.eslintrc.js | 321 - tests/files/bar.js | 2 - .../node_modules/@generated/bar/index.js | 0 .../node_modules/@generated/foo/index.js | 0 .../as-array-bundle-deps/package.json | 4 - .../node_modules/@generated/bar/index.js | 0 .../node_modules/@generated/foo/index.js | 0 .../as-object/package.json | 4 - .../node_modules/@generated/bar/index.js | 0 .../node_modules/@generated/foo/index.js | 0 .../race-condition/package.json | 5 - tests/files/color.js | 1 - tests/files/constants/index.js | 1 - tests/files/cycles/es6/depth-one-dynamic.js | 1 - tests/files/cycles/es6/depth-one-reexport.js | 1 - tests/files/cycles/es6/depth-one.js | 2 - tests/files/cycles/es6/depth-three-star.js | 2 - tests/files/cycles/es6/depth-two.js | 2 - tests/files/cycles/external-depth-two.js | 2 - tests/files/cycles/external/depth-one.js | 2 - tests/files/cycles/flow-typeof.js | 4 - tests/files/cycles/flow-types-depth-one.js | 6 - ...low-types-only-importing-multiple-types.js | 3 - .../cycles/flow-types-only-importing-type.js | 3 - tests/files/cycles/flow-types.js | 6 - tests/files/cycles/ignore/.eslintrc | 5 - tests/files/cycles/ignore/index.js | 2 - tests/files/cycles/intermediate-ignore.js | 2 - tests/files/data.json | 1 - tests/files/deep-es7/d.js | 1 - tests/files/deep/d.js | 1 - tests/files/default-class.js | 3 - .../files/default-export-namespace-string.js | 1 - tests/files/default-export-string.js | 3 - tests/files/default-export.js | 1 - tests/files/dynamic-import-in-commonjs.js | 5 - tests/files/empty-named-blocks.js | 1 - .../files/export-default-string-and-named.js | 4 - tests/files/export-star-2/middle.js | 1 - tests/files/export-star-2/upstream.js | 1 - tests/files/export-star-3/b.ts | 1 - tests/files/export-star-4/module/feature.jsx | 3 - tests/files/export-star-4/module/index.ts | 1 - tests/files/export-star/extfield.js | 1 - tests/files/export-star/extfield2.js | 1 - tests/files/export-star/models.js | 2 - tests/files/foo-bar-resolver-invalid.js | 1 - .../internal-modules/api/service/index.js | 0 .../plugins/plugin2/app/index.js | 0 .../internal-modules/plugins/plugin2/index.js | 0 tests/files/issue-195/Bookings.js | 1 - tests/files/issue-195/Endpoints.js | 2 - tests/files/issue-195/Users.js | 1 - .../files/issue-370-commonjs-namespace/bar.js | 2 - .../files/issue-370-commonjs-namespace/foo.js | 2 - tests/files/issue210.js | 1 - tests/files/jsx/FooES7.js | 17 - tests/files/jsx/bar/index.js | 2 - tests/files/jsx/re-export.js | 1 - tests/files/named-export-collision/a.js | 1 - tests/files/named-export-collision/b.js | 1 - tests/files/named-exports.js | 21 - tests/files/named-trampoline.js | 2 - tests/files/narcissist.js | 2 - tests/files/nested-common.js | 5 - ...rary-module-namespace-identifier-name-a.js | 2 - ...rary-module-namespace-identifier-name-b.js | 1 - ...rary-module-namespace-identifier-name-c.js | 2 - .../no-unused-modules/destructuring-a.js | 1 - .../no-unused-modules/destructuring-b.js | 2 - .../no-unused-modules/dynamic-import-js-2.js | 13 - .../no-unused-modules/dynamic-import-js.js | 5 - .../exports-for-dynamic-js-2.js | 5 - .../no-unused-modules/file-destructured-1.js | 2 - .../no-unused-modules/file-destructured-2.js | 1 - tests/files/no-unused-modules/file-p.js | 1 - tests/files/no-unused-modules/flow/flow-0.js | 1 - tests/files/no-unused-modules/flow/flow-1.js | 3 - tests/files/no-unused-modules/flow/flow-2.js | 3 - tests/files/no-unused-modules/flow/flow-3.js | 1 - tests/files/no-unused-modules/flow/flow-4.js | 3 - .../no-unused-modules/import-export-1.js | 2 - .../no-unused-modules/import-export-2.js | 2 - .../no-unused-modules/jsx/file-jsx-a.jsx | 3 - .../no-unused-modules/jsx/file-jsx-b.jsx | 1 - tests/files/no-unused-modules/package.json | 5 - tests/files/no-unused-modules/prefix-child.js | 1 - .../no-unused-modules/prefix-parent-bom.js | 1 - .../prefix-parent-bomhashbang.js | 2 - .../prefix-parent-hashbang.js | 2 - .../files/no-unused-modules/prefix-parent.js | 2 - .../no-unused-modules/privatePkg/package.json | 4 - .../renameDefault-2/components.js | 2 - .../typescript/dynamic-import-ts.ts | 6 - .../typescript/file-ts-a-import-type.ts | 9 - .../no-unused-modules/typescript/file-ts-a.ts | 8 - .../typescript/file-ts-b-unused.ts | 1 - .../typescript/file-ts-b-used-as-type.ts | 1 - .../no-unused-modules/typescript/file-ts-b.ts | 1 - .../typescript/file-ts-c-unused.ts | 1 - .../typescript/file-ts-c-used-as-type.ts | 1 - .../no-unused-modules/typescript/file-ts-c.ts | 1 - .../typescript/file-ts-d-unused.ts | 1 - .../typescript/file-ts-d-used-as-type.ts | 1 - .../no-unused-modules/typescript/file-ts-d.ts | 1 - .../typescript/file-ts-e-unused.ts | 1 - .../typescript/file-ts-e-used-as-type.ts | 1 - .../no-unused-modules/typescript/file-ts-e.ts | 1 - .../typescript/file-ts-f-import-type.ts | 1 - .../no-unused-modules/typescript/file-ts-f.ts | 1 - .../node_modules/@generated/bar/index.js | 0 .../node_modules/@generated/bar/package.json | 3 - .../node_modules/@generated/foo/index.js | 0 .../node_modules/@generated/foo/package.json | 3 - .../node_modules/@org/not-a-dependency/foo.js | 0 .../@org/not-a-dependency/index.js | 0 .../@org/not-a-dependency/package.json | 3 - .../files/node_modules/@org/package/index.js | 0 .../node_modules/@org/package/internal.js | 0 .../node_modules/@org/package/package.json | 3 - tests/files/node_modules/a/index.js | 1 - tests/files/node_modules/a/package.json | 3 - tests/files/node_modules/chai/index.js | 0 tests/files/node_modules/chai/package.json | 3 - tests/files/node_modules/es6-module/index.js | 1 - .../node_modules/es6-module/package.json | 3 - .../eslint-import-resolver-foo/index.js | 1 - .../eslint-import-resolver-foo/package.json | 3 - .../esm-module/index.js | 0 .../esm-module/package.json | 4 - .../esm-package-not-in-pkg-json/index.js | 0 .../esm-package-not-in-pkg-json/package.json | 5 - .../esm-package/esm-module/index.js | 0 .../esm-package/esm-module/package.json | 4 - tests/files/node_modules/esm-package/index.js | 0 .../node_modules/esm-package/package.json | 5 - tests/files/node_modules/exceljs/excel.js | 0 tests/files/node_modules/exceljs/package.json | 4 - .../files/node_modules/jquery/dist/jquery.js | 1 - tests/files/node_modules/jquery/package.json | 3 - tests/files/node_modules/jsx-module/foo.jsx | 1 - .../node_modules/jsx-module/package.json | 3 - tests/files/node_modules/left-pad/index.js | 0 .../node_modules/left-pad/not-a-dependency | 0 .../files/node_modules/left-pad/package.json | 3 - .../node_modules/not-a-dependency/index.js | 0 .../not-a-dependency/package.json | 3 - tests/files/node_modules/react/index.js | 1 - .../files/node_modules/react/not-a-dependency | 0 tests/files/node_modules/react/package.json | 3 - .../node_modules/rxjs/operators/package.json | 5 - tests/files/node_modules/rxjs/package.json | 5 - tests/files/package-scoped/index.js | 1 - tests/files/package/index.js | 1 - tests/files/test-module/index.js | 1 - tests/files/typescript-d-ts/file1.ts | 6 - tests/files/typescript-declare-module.ts | 3 - tests/files/typescript-default.ts | 1 - .../tsconfig.json | 5 - .../tsconfig.json | 5 - ...pescript-export-assign-default-reexport.ts | 2 - .../typescript-export-assign-default.d.ts | 3 - .../typescript-export-assign-function.ts | 1 - .../tsconfig.json | 5 - .../typescript-export-assign-property.ts | 3 - .../index.d.ts | 19 - .../tsconfig.json | 5 - .../tsconfig.json | 2 - tests/files/umd.js | 5 - .../unused-modules-reexport-crash/src/App.tsx | 5 - .../src/index.tsx | 3 - tests/files/webpack.empty.config.js | 1 - tests/index.js | 1 - tests/src/cli.js | 150 - tests/src/config/typescript.js | 14 - tests/src/core/docsUrl.js | 14 - tests/src/core/getExports.js | 465 -- tests/src/core/hash.js | 76 - tests/src/core/ignore.js | 90 - tests/src/core/importType.js | 286 - tests/src/core/parse.js | 141 - tests/src/core/resolve.js | 468 -- tests/src/package.js | 71 - tests/src/rules/default.js | 316 - tests/src/rules/export.js | 593 -- tests/src/rules/max-dependencies.js | 134 - tests/src/rules/namespace.js | 377 -- tests/src/rules/no-amd.js | 37 - .../src/rules/no-anonymous-default-export.js | 62 - tests/src/rules/no-commonjs.js | 115 - tests/src/rules/no-cycle.js | 293 - tests/src/rules/no-duplicates.js | 802 --- tests/src/rules/no-empty-named-blocks.js | 115 - tests/src/rules/no-extraneous-dependencies.js | 524 -- tests/src/rules/no-named-as-default-member.js | 63 - tests/src/rules/no-named-as-default.js | 85 - tests/src/rules/no-named-export.js | 186 - tests/src/rules/no-namespace.js | 114 - tests/src/rules/no-nodejs-modules.js | 146 - tests/src/rules/no-relative-parent-imports.js | 106 - tests/src/rules/no-restricted-paths.js | 981 --- utils/.attw.json | 5 - utils/.npmignore | 1 - utils/.nycrc | 15 - utils/CHANGELOG.md | 198 - utils/ModuleCache.d.ts | 22 - utils/declaredScope.d.ts | 8 - utils/declaredScope.js | 11 - utils/hash.d.ts | 14 - utils/hash.js | 64 - utils/ignore.d.ts | 12 - utils/ignore.js | 69 - utils/module-require.d.ts | 3 - utils/module-require.js | 36 - utils/moduleVisitor.d.ts | 26 - utils/package.json | 49 - utils/parse.d.ts | 11 - utils/pkgDir.d.ts | 3 - utils/pkgDir.js | 12 - utils/pkgUp.d.ts | 3 - utils/readPkgUp.d.ts | 5 - utils/resolve.d.ts | 30 - utils/resolve.js | 253 - utils/tsconfig.json | 11 - utils/types.d.ts | 9 - utils/unambiguous.d.ts | 7 - utils/visit.d.ts | 9 - utils/visit.js | 30 - yarn.lock | 5848 +++++++++++++++++ 856 files changed, 25633 insertions(+), 21358 deletions(-) delete mode 100644 .babelrc delete mode 100644 .coveralls.yml delete mode 100644 .eslintrc create mode 100644 .eslintrc.js delete mode 100644 .github/FUNDING.yml delete mode 100644 .github/workflows/node-4+.yml delete mode 100644 .github/workflows/node-pretest.yml delete mode 100644 .github/workflows/packages.yml delete mode 100644 .github/workflows/rebase.yml delete mode 100644 .github/workflows/require-allow-edits.yml delete mode 100644 .npmrc delete mode 100644 .nycrc create mode 100644 .prettierrc.mjs delete mode 100644 .travis.yml delete mode 100644 CHANGELOG.md delete mode 100644 RELEASE.md delete mode 100644 SECURITY.md delete mode 100644 appveyor.yml create mode 100644 babel.config.js delete mode 100644 import.sublime-project create mode 100644 jest.config.js delete mode 100644 memo-parser/.nycrc delete mode 100644 memo-parser/README.md delete mode 100644 memo-parser/index.js delete mode 100644 memo-parser/package.json delete mode 100644 resolvers/README.md delete mode 100644 resolvers/node/.nycrc delete mode 100644 resolvers/node/CHANGELOG.md delete mode 100644 resolvers/node/README.md delete mode 100644 resolvers/node/index.js delete mode 100644 resolvers/node/package.json delete mode 100644 resolvers/node/test/data.json delete mode 100644 resolvers/node/test/dot-node.node delete mode 100644 resolvers/node/test/native.js delete mode 100644 resolvers/node/test/native.mjs delete mode 100644 resolvers/node/test/native.node delete mode 100644 resolvers/node/test/package-mains/jsnext/package.json delete mode 100644 resolvers/node/test/package-mains/module-and-jsnext/package.json delete mode 100644 resolvers/node/test/package-mains/module-broken/main.js delete mode 100644 resolvers/node/test/package-mains/module-broken/package.json delete mode 100644 resolvers/node/test/package-mains/module/package.json delete mode 100644 resolvers/node/test/package-mains/package.json delete mode 100644 resolvers/node/test/packageMains.js delete mode 100644 resolvers/node/test/paths.js delete mode 120000 resolvers/webpack/.babelrc delete mode 100644 resolvers/webpack/.nycrc delete mode 100644 resolvers/webpack/CHANGELOG.md delete mode 100644 resolvers/webpack/README.md delete mode 100644 resolvers/webpack/config.js delete mode 100644 resolvers/webpack/index.js delete mode 100644 resolvers/webpack/package.json delete mode 100644 resolvers/webpack/test/alias.js delete mode 100644 resolvers/webpack/test/config-extensions/package.json delete mode 100644 resolvers/webpack/test/config-extensions/src/main-module.js delete mode 100644 resolvers/webpack/test/config-extensions/webpack.config.babel.js delete mode 100644 resolvers/webpack/test/config.js delete mode 100644 resolvers/webpack/test/custom-extensions/bar.coffee delete mode 100644 resolvers/webpack/test/custom-extensions/baz.web.js delete mode 100644 resolvers/webpack/test/custom-extensions/foo.js delete mode 100644 resolvers/webpack/test/custom-extensions/package.json delete mode 100644 resolvers/webpack/test/custom-extensions/webpack.config.js delete mode 100644 resolvers/webpack/test/example.js delete mode 100644 resolvers/webpack/test/extensions.js delete mode 100644 resolvers/webpack/test/externals.js delete mode 100644 resolvers/webpack/test/fallback.js delete mode 100644 resolvers/webpack/test/files/bower_components/typeahead.js delete mode 100644 resolvers/webpack/test/files/fallback/fb-module.js delete mode 100644 resolvers/webpack/test/files/fallback/jsx/some-fb-file.js delete mode 100644 resolvers/webpack/test/files/fallback/jsx/some-file.js delete mode 100644 resolvers/webpack/test/files/fallback/main-module.js delete mode 100644 resolvers/webpack/test/files/foo.web.js delete mode 100644 resolvers/webpack/test/files/node_modules/some-module/index.js delete mode 100644 resolvers/webpack/test/files/node_modules/webpack-resolver-plugin-test/index.js delete mode 100644 resolvers/webpack/test/files/package.json delete mode 100644 resolvers/webpack/test/files/some/absolute.path.webpack.config.js delete mode 100644 resolvers/webpack/test/files/some/absolutely/goofy/path/foo.js delete mode 100644 resolvers/webpack/test/files/some/bar/bar.js delete mode 100644 resolvers/webpack/test/files/some/goofy/path/bar.js delete mode 100644 resolvers/webpack/test/files/some/goofy/path/foo.js delete mode 100644 resolvers/webpack/test/files/src/jsx/some-file.js delete mode 100644 resolvers/webpack/test/files/src/main-module.js delete mode 100644 resolvers/webpack/test/files/webpack.array-root.config.js delete mode 100644 resolvers/webpack/test/files/webpack.config.async.js delete mode 100644 resolvers/webpack/test/files/webpack.config.babel.js delete mode 100644 resolvers/webpack/test/files/webpack.config.garbage.js delete mode 100644 resolvers/webpack/test/files/webpack.config.js delete mode 100644 resolvers/webpack/test/files/webpack.config.multiple.js delete mode 100644 resolvers/webpack/test/files/webpack.config.webpack5.async-externals.js delete mode 100644 resolvers/webpack/test/files/webpack.config.webpack5.js delete mode 100644 resolvers/webpack/test/files/webpack.function.config.js delete mode 100644 resolvers/webpack/test/files/webpack.function.config.multiple.js delete mode 100644 resolvers/webpack/test/loaders.js delete mode 100644 resolvers/webpack/test/modules.js delete mode 100644 resolvers/webpack/test/package-mains/jam/index.js delete mode 100644 resolvers/webpack/test/package-mains/jam/jam.js delete mode 100644 resolvers/webpack/test/package-mains/jam/package.json delete mode 100644 resolvers/webpack/test/package-mains/jsnext/package.json delete mode 100644 resolvers/webpack/test/package-mains/jsnext/src/index.js delete mode 100644 resolvers/webpack/test/package-mains/module-and-jsnext/package.json delete mode 100644 resolvers/webpack/test/package-mains/module-and-jsnext/src/index.js delete mode 100644 resolvers/webpack/test/package-mains/module-broken/main.js delete mode 100644 resolvers/webpack/test/package-mains/module-broken/package.json delete mode 100644 resolvers/webpack/test/package-mains/module/package.json delete mode 100644 resolvers/webpack/test/package-mains/module/src/index.js delete mode 100644 resolvers/webpack/test/package-mains/package.json delete mode 100644 resolvers/webpack/test/package-mains/webpack.alt.config.js delete mode 100644 resolvers/webpack/test/package-mains/webpack.config.js delete mode 100644 resolvers/webpack/test/package-mains/webpack/index.js delete mode 100644 resolvers/webpack/test/package-mains/webpack/package.json delete mode 100644 resolvers/webpack/test/package-mains/webpack/webpack.js delete mode 100644 resolvers/webpack/test/packageMains.js delete mode 100644 resolvers/webpack/test/plugins.js delete mode 100644 resolvers/webpack/test/root.js delete mode 100644 scripts/GetCI/GetCI.psm1 delete mode 100644 scripts/ci.cmd delete mode 100644 scripts/copyMetafiles.js delete mode 100644 scripts/resolverDirectories.js delete mode 100644 scripts/testAll.js create mode 100644 src/utils/ModuleCache.d.ts rename {utils => src/utils}/ModuleCache.js (54%) create mode 100644 src/utils/declaredScope.d.ts create mode 100644 src/utils/declaredScope.js create mode 100644 src/utils/hash.d.ts create mode 100644 src/utils/hash.js create mode 100644 src/utils/ignore.d.ts create mode 100644 src/utils/ignore.js create mode 100644 src/utils/module-require.d.ts create mode 100644 src/utils/module-require.js create mode 100644 src/utils/moduleVisitor.d.ts rename {utils => src/utils}/moduleVisitor.js (54%) create mode 100644 src/utils/parse.d.ts rename {utils => src/utils}/parse.js (52%) create mode 100644 src/utils/pkgDir.d.ts create mode 100644 src/utils/pkgDir.js create mode 100644 src/utils/pkgUp.d.ts rename {utils => src/utils}/pkgUp.js (79%) create mode 100644 src/utils/readPkgUp.d.ts rename {utils => src/utils}/readPkgUp.js (88%) create mode 100644 src/utils/resolve.d.ts create mode 100644 src/utils/resolve.js create mode 100644 src/utils/types.d.ts create mode 100644 src/utils/unambiguous.d.ts rename {utils => src/utils}/unambiguous.js (72%) create mode 100644 src/utils/visit.d.ts create mode 100644 src/utils/visit.js create mode 100644 test/cli.spec.js create mode 100644 test/config/typescript.spec.js create mode 100644 test/core/docsUrl.spec.js rename {tests/src => test}/core/eslintParser.js (87%) create mode 100644 test/core/getExports.spec.js create mode 100644 test/core/hash.spec.js create mode 100644 test/core/ignore.spec.js create mode 100644 test/core/importType.spec.js create mode 100644 test/core/parse.spec.js rename {tests/src => test}/core/parseStubParser.js (97%) create mode 100644 test/core/resolve.spec.js create mode 100644 test/fixtures/.eslintrc.js rename {tests/files => test/fixtures}/@importType/index.js (100%) rename {tests/files => test/fixtures}/@my-alias/fn.js (100%) rename {tests/files => test/fixtures}/CaseyKasem.js (100%) rename {tests/files => test/fixtures}/alternate-root/in-alternate-root.js (100%) rename {tests/files => test/fixtures}/bar.coffee (100%) create mode 100644 test/fixtures/bar.js rename {tests/files => test/fixtures}/bar.json (100%) rename {tests/files => test/fixtures}/bar.jsx (100%) rename {tests/files => test/fixtures}/bar/index.js (100%) rename {tests/files => test/fixtures}/broken-trampoline.js (100%) create mode 100644 test/fixtures/bundled-dependencies/as-array-bundle-deps/package.json create mode 100644 test/fixtures/bundled-dependencies/as-object/package.json create mode 100644 test/fixtures/bundled-dependencies/race-condition/package.json create mode 100644 test/fixtures/color.js rename {tests/files => test/fixtures}/common-module.js (50%) rename {tests/files => test/fixtures}/common.js (87%) rename {tests/files => test/fixtures}/commonjs-namespace/a.js (100%) rename {tests/files => test/fixtures}/commonjs-namespace/b.js (100%) rename {tests/files => test/fixtures}/component.html (93%) create mode 100644 test/fixtures/constants/index.js rename {tests/files => test/fixtures}/cycles/depth-zero.js (100%) create mode 100644 test/fixtures/cycles/es6/depth-one-dynamic.js create mode 100644 test/fixtures/cycles/es6/depth-one-reexport.js create mode 100644 test/fixtures/cycles/es6/depth-one.js rename {tests/files => test/fixtures}/cycles/es6/depth-three-indirect.js (62%) create mode 100644 test/fixtures/cycles/es6/depth-three-star.js create mode 100644 test/fixtures/cycles/es6/depth-two.js create mode 100644 test/fixtures/cycles/external-depth-two.js create mode 100644 test/fixtures/cycles/external/depth-one.js create mode 100644 test/fixtures/cycles/flow-typeof.js create mode 100644 test/fixtures/cycles/flow-types-depth-one.js rename {tests/files => test/fixtures}/cycles/flow-types-depth-two.js (100%) create mode 100644 test/fixtures/cycles/flow-types-only-importing-multiple-types.js create mode 100644 test/fixtures/cycles/flow-types-only-importing-type.js rename {tests/files => test/fixtures}/cycles/flow-types-some-type-imports.js (100%) create mode 100644 test/fixtures/cycles/flow-types.js create mode 100644 test/fixtures/cycles/ignore/.eslintrc create mode 100644 test/fixtures/cycles/ignore/index.js create mode 100644 test/fixtures/cycles/intermediate-ignore.js create mode 100644 test/fixtures/data.json rename {tests/files => test/fixtures}/deep-deprecated.js (67%) rename {tests/files => test/fixtures}/deep-es7/a.js (100%) rename {tests/files => test/fixtures}/deep-es7/b.js (100%) rename {tests/files => test/fixtures}/deep-es7/c.js (100%) create mode 100644 test/fixtures/deep-es7/d.js rename {tests/files => test/fixtures}/deep/a.js (65%) rename {tests/files => test/fixtures}/deep/b.js (100%) rename {tests/files => test/fixtures}/deep/c.js (65%) rename {tests/files => test/fixtures}/deep/cache-1.js (100%) rename {tests/files => test/fixtures}/deep/cache-2a.js (100%) rename {tests/files => test/fixtures}/deep/cache-2b.js (100%) create mode 100644 test/fixtures/deep/d.js rename {tests/files => test/fixtures}/deep/default.js (59%) create mode 100644 test/fixtures/default-class.js rename {tests/files => test/fixtures}/default-export-default-property.js (51%) rename {tests/files => test/fixtures}/default-export-from-ignored.js (100%) rename {tests/files => test/fixtures}/default-export-from-named.js (100%) rename {tests/files => test/fixtures}/default-export-from.js (100%) create mode 100644 test/fixtures/default-export-namespace-string.js create mode 100644 test/fixtures/default-export-string.js create mode 100644 test/fixtures/default-export.js rename {tests/files => test/fixtures}/deprecated-file.js (81%) rename {tests/files => test/fixtures}/deprecated.js (54%) create mode 100644 test/fixtures/dynamic-import-in-commonjs.js rename {tests/files => test/fixtures}/empty-folder/anchor.txt (100%) create mode 100644 test/fixtures/empty-named-blocks.js rename {tests/files => test/fixtures}/empty/package.json (100%) rename {tests/files => test/fixtures}/export-all.js (100%) create mode 100644 test/fixtures/export-default-string-and-named.js rename {tests/files => test/fixtures}/export-props.js (100%) create mode 100644 test/fixtures/export-star-2/middle.js create mode 100644 test/fixtures/export-star-2/upstream.js create mode 100644 test/fixtures/export-star-3/b.ts rename {tests/files => test/fixtures}/export-star-3/c.ts (100%) create mode 100644 test/fixtures/export-star-4/module/feature.jsx create mode 100644 test/fixtures/export-star-4/module/index.ts create mode 100644 test/fixtures/export-star/extfield.js create mode 100644 test/fixtures/export-star/extfield2.js create mode 100644 test/fixtures/export-star/models.js rename {tests/files => test/fixtures}/exports-calc-keys.js (100%) rename {tests/files => test/fixtures}/exports-missing.js (100%) rename {tests/files => test/fixtures}/file.with.dot.js (100%) rename {tests/files => test/fixtures}/flowtypes.js (60%) create mode 100644 test/fixtures/foo-bar-resolver-invalid.js rename {tests/files => test/fixtures}/foo-bar-resolver-no-version.js (94%) rename {tests/files => test/fixtures}/foo-bar-resolver-v1.js (73%) rename {tests/files => test/fixtures}/foo-bar-resolver-v2.js (85%) rename {tests/files => test/fixtures}/foobar.json (100%) rename {tests/files => test/fixtures}/ignore.invalid.extension (100%) rename {tests/files => test/fixtures}/importType/index.js (100%) rename {tests/files => test/fixtures}/index.js (100%) rename {resolvers/node/test/package-mains/jsnext/src => test/fixtures/internal-modules/api/service}/index.js (100%) rename {tests/files => test/fixtures}/internal-modules/package.json (100%) rename {tests/files => test/fixtures}/internal-modules/plugins/plugin.js (100%) rename {resolvers/node/test/package-mains/module-and-jsnext/src => test/fixtures/internal-modules/plugins/plugin2/app}/index.js (100%) rename {resolvers/node/test/package-mains/module/src => test/fixtures/internal-modules/plugins/plugin2}/index.js (100%) rename {tests/files => test/fixtures}/internal-modules/plugins/plugin2/internal.js (100%) rename {tests/files => test/fixtures}/internal-modules/test.js (100%) rename {tests/files => test/fixtures}/internal-modules/typescript/plugin2/app/index.ts (100%) rename {tests/files => test/fixtures}/internal-modules/typescript/plugin2/index.ts (100%) rename {tests/files => test/fixtures}/internal-modules/typescript/plugin2/internal.ts (100%) rename {tests/files => test/fixtures}/internal-modules/typescript/plugins.ts (100%) create mode 100644 test/fixtures/issue-195/Bookings.js create mode 100644 test/fixtures/issue-195/Endpoints.js create mode 100644 test/fixtures/issue-195/Users.js create mode 100644 test/fixtures/issue-370-commonjs-namespace/bar.js create mode 100644 test/fixtures/issue-370-commonjs-namespace/foo.js rename {tests/files => test/fixtures}/issue210.config.js (96%) create mode 100644 test/fixtures/issue210.js rename {tests/files => test/fixtures}/jsx.js (100%) rename {tests/files => test/fixtures}/jsx/AnotherComponent.jsx (100%) rename {tests/files => test/fixtures}/jsx/App.js (65%) create mode 100644 test/fixtures/jsx/FooES7.js rename {tests/files => test/fixtures}/jsx/MyCoolComponent.jsx (60%) rename {tests/files => test/fixtures}/jsx/MyUnCoolComponent.jsx (60%) rename {tests/files => test/fixtures}/jsx/bar/baz.jsx (74%) create mode 100644 test/fixtures/jsx/bar/index.js rename {tests/files => test/fixtures}/jsx/bar/qux.jsx (92%) rename {tests/files => test/fixtures}/jsx/named.jsx (100%) create mode 100644 test/fixtures/jsx/re-export.js rename {tests/files => test/fixtures}/just-json-files/.eslintrc.json (74%) rename {tests/files => test/fixtures}/just-json-files/invalid.json (100%) rename {tests/files => test/fixtures}/load-error-resolver.js (100%) rename {tests/files => test/fixtures}/malformed.js (100%) rename {tests/files => test/fixtures}/missing-entrypoint/package.json (100%) rename {tests/files => test/fixtures}/mixed-exports.js (67%) rename {tests/files => test/fixtures}/monorepo/package.json (100%) rename {tests/files => test/fixtures}/monorepo/packages/nested-package/package.json (100%) rename {tests/files => test/fixtures}/mutator.js (100%) rename {tests/files => test/fixtures}/named-default-export.js (100%) create mode 100644 test/fixtures/named-export-collision/a.js create mode 100644 test/fixtures/named-export-collision/b.js create mode 100644 test/fixtures/named-exports.js create mode 100644 test/fixtures/named-trampoline.js create mode 100644 test/fixtures/narcissist.js create mode 100644 test/fixtures/nested-common.js rename {tests/files => test/fixtures}/no-self-import-folder/index.js (100%) rename {tests/files => test/fixtures}/no-self-import.js (100%) create mode 100644 test/fixtures/no-unused-modules/arbitrary-module-namespace-identifier-name-a.js create mode 100644 test/fixtures/no-unused-modules/arbitrary-module-namespace-identifier-name-b.js create mode 100644 test/fixtures/no-unused-modules/arbitrary-module-namespace-identifier-name-c.js rename {tests/files => test/fixtures}/no-unused-modules/bin.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/binObject/index.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/binObject/package.json (65%) rename {tests/files => test/fixtures}/no-unused-modules/browser.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/browserObject/index.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/browserObject/package.json (100%) rename {tests/files => test/fixtures}/no-unused-modules/cjs.js (69%) create mode 100644 test/fixtures/no-unused-modules/destructuring-a.js create mode 100644 test/fixtures/no-unused-modules/destructuring-b.js create mode 100644 test/fixtures/no-unused-modules/dynamic-import-js-2.js create mode 100644 test/fixtures/no-unused-modules/dynamic-import-js.js rename {tests/files => test/fixtures}/no-unused-modules/empty_file.js (100%) rename tests/files/no-unused-modules/exports-for-dynamic-js.js => test/fixtures/no-unused-modules/exports-for-dynamic-js-2.js (100%) create mode 100644 test/fixtures/no-unused-modules/exports-for-dynamic-js.js rename {tests/files => test/fixtures}/no-unused-modules/file-0.js (93%) rename {tests/files => test/fixtures}/no-unused-modules/file-a.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-b.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-c.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-d.js (100%) create mode 100644 test/fixtures/no-unused-modules/file-destructured-1.js create mode 100644 test/fixtures/no-unused-modules/file-destructured-2.js rename {tests/files => test/fixtures}/no-unused-modules/file-e.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-f.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-g.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-h.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-i.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-ignored-a.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-ignored-b.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-ignored-c.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-ignored-d.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-ignored-e.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-ignored-l.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-j.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-k.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-l.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-m.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-n.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-o.js (100%) create mode 100644 test/fixtures/no-unused-modules/file-p.js rename {tests/files => test/fixtures}/no-unused-modules/file-q.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/file-s.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/filte-r.js (100%) create mode 100644 test/fixtures/no-unused-modules/flow/flow-0.js create mode 100644 test/fixtures/no-unused-modules/flow/flow-1.js create mode 100644 test/fixtures/no-unused-modules/flow/flow-2.js create mode 100644 test/fixtures/no-unused-modules/flow/flow-3.js create mode 100644 test/fixtures/no-unused-modules/flow/flow-4.js create mode 100644 test/fixtures/no-unused-modules/import-export-1.js create mode 100644 test/fixtures/no-unused-modules/import-export-2.js create mode 100644 test/fixtures/no-unused-modules/jsx/file-jsx-a.jsx create mode 100644 test/fixtures/no-unused-modules/jsx/file-jsx-b.jsx rename {tests/files => test/fixtures}/no-unused-modules/main/index.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/node_modules.js (100%) create mode 100644 test/fixtures/no-unused-modules/package.json create mode 100644 test/fixtures/no-unused-modules/prefix-child.js create mode 100644 test/fixtures/no-unused-modules/prefix-parent-bom.js create mode 100644 test/fixtures/no-unused-modules/prefix-parent-bomhashbang.js create mode 100644 test/fixtures/no-unused-modules/prefix-parent-hashbang.js create mode 100644 test/fixtures/no-unused-modules/prefix-parent.js rename {tests/files => test/fixtures}/no-unused-modules/privatePkg/index.js (100%) create mode 100644 test/fixtures/no-unused-modules/privatePkg/package.json rename {tests/files => test/fixtures}/no-unused-modules/renameDefault-2/ComponentA.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/renameDefault-2/ComponentB.js (100%) create mode 100644 test/fixtures/no-unused-modules/renameDefault-2/components.js rename {tests/files => test/fixtures}/no-unused-modules/renameDefault-2/usage.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/renameDefault/Component.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/renameDefault/components.js (100%) rename {tests/files => test/fixtures}/no-unused-modules/renameDefault/usage.js (100%) create mode 100644 test/fixtures/no-unused-modules/typescript/dynamic-import-ts.ts rename {tests/files => test/fixtures}/no-unused-modules/typescript/exports-for-dynamic-ts.ts (100%) create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-a-import-type.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-a.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-b-unused.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-b-used-as-type.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-b.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-c-unused.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-c-used-as-type.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-c.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-d-unused.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-d-used-as-type.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-d.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-e-unused.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-e-used-as-type.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-e.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-f-import-type.ts create mode 100644 test/fixtures/no-unused-modules/typescript/file-ts-f.ts rename {tests/files => test/fixtures}/no-unused-modules/typescript/file-ts-g-used-as-type.ts (100%) rename {tests/files => test/fixtures}/no-unused-modules/typescript/file-ts-g.ts (100%) rename {tests/files => test/fixtures}/order-redirect-scoped/module/package.json (100%) rename {tests/files => test/fixtures}/order-redirect-scoped/other-module/file.js (100%) rename {tests/files => test/fixtures}/order-redirect-scoped/package.json (100%) rename {tests/files => test/fixtures}/order-redirect/module/package.json (100%) rename {tests/files => test/fixtures}/order-redirect/other-module/file.js (100%) rename {tests/files => test/fixtures}/order-redirect/package.json (100%) rename {tests/files/node_modules/rxjs => test/fixtures/package-named}/index.js (100%) rename {tests/files => test/fixtures}/package-named/package.json (97%) rename {tests/files/node_modules/rxjs/operators => test/fixtures/package-scoped}/index.js (100%) rename {tests/files => test/fixtures}/package-scoped/package.json (100%) rename {tests/files => test/fixtures}/package.json (74%) rename {tests/files/package-named => test/fixtures/package}/index.js (100%) rename {tests/files => test/fixtures}/package/package.json (98%) rename {tests/files => test/fixtures}/qc.js (82%) rename {tests/files => test/fixtures}/re-export-common-star.js (100%) rename {tests/files => test/fixtures}/re-export-common.js (100%) rename {tests/files => test/fixtures}/re-export-default.js (85%) rename {tests/files => test/fixtures}/re-export-names.js (100%) rename {tests/files => test/fixtures}/re-export-node_modules.js (100%) rename {tests/files => test/fixtures}/re-export.js (100%) rename {tests/files => test/fixtures}/redux.js (100%) rename {tests/files => test/fixtures}/restricted-paths/client/a.js (100%) rename {tests/files => test/fixtures}/restricted-paths/client/one/a.js (100%) rename {tests/files => test/fixtures}/restricted-paths/server/b.js (100%) rename {tests/files => test/fixtures}/restricted-paths/server/c.js (100%) rename {tests/files => test/fixtures}/restricted-paths/server/c.ts (100%) rename {tests/files => test/fixtures}/restricted-paths/server/one/a.js (100%) rename {tests/files => test/fixtures}/restricted-paths/server/one/b.js (100%) rename {tests/files => test/fixtures}/restricted-paths/server/three/a.js (100%) rename {tests/files => test/fixtures}/restricted-paths/server/two-new/a.js (100%) rename {tests/files => test/fixtures}/restricted-paths/server/two/a.js (100%) rename {tests/files => test/fixtures}/sibling-with-names.js (100%) rename {tests/files => test/fixtures}/src-root/src-bar.js (100%) rename {tests/files => test/fixtures}/symlinked-module/index.js (100%) rename {tests/files => test/fixtures}/symlinked-module/package.json (100%) create mode 100644 test/fixtures/test-module/index.js rename {tests/files => test/fixtures}/test.coffee (100%) rename {tests/files => test/fixtures}/test.giffy (100%) rename {tests/files => test/fixtures}/tomdoc-deprecated.js (58%) rename {tests/files => test/fixtures}/trampoline.js (100%) rename {tests/files => test/fixtures}/ts-deprecated.ts (100%) rename {tests/files => test/fixtures}/typescript-d-ts/.eslintrc (74%) create mode 100644 test/fixtures/typescript-d-ts/file1.ts rename {tests/files => test/fixtures}/typescript-d-ts/file2.ts (100%) rename {tests/files => test/fixtures}/typescript-declare-interface.d.ts (80%) create mode 100644 test/fixtures/typescript-declare-module.ts rename {tests/files => test/fixtures}/typescript-declare-nested.d.ts (64%) rename {tests/files => test/fixtures}/typescript-declare.d.ts (67%) create mode 100644 test/fixtures/typescript-default.ts rename {tests/files => test/fixtures}/typescript-export-as-default-namespace/index.d.ts (100%) rename tests/files/typescript-extended-config/tsconfig.base.json => test/fixtures/typescript-export-as-default-namespace/tsconfig.json (100%) rename {tests/files/typescript-extended-config => test/fixtures/typescript-export-assign-default-namespace}/index.d.ts (63%) create mode 100644 test/fixtures/typescript-export-assign-default-namespace/tsconfig.json create mode 100644 test/fixtures/typescript-export-assign-default-reexport.ts create mode 100644 test/fixtures/typescript-export-assign-default.d.ts create mode 100644 test/fixtures/typescript-export-assign-function.ts rename {tests/files => test/fixtures}/typescript-export-assign-mixed.d.ts (61%) rename {tests/files => test/fixtures}/typescript-export-assign-namespace-merged.d.ts (71%) rename {tests/files => test/fixtures}/typescript-export-assign-namespace.d.ts (69%) rename {tests/files => test/fixtures}/typescript-export-assign-object/index.ts (61%) create mode 100644 test/fixtures/typescript-export-assign-object/tsconfig.json create mode 100644 test/fixtures/typescript-export-assign-property.ts create mode 100644 test/fixtures/typescript-export-react-test-renderer/index.d.ts create mode 100644 test/fixtures/typescript-export-react-test-renderer/tsconfig.json rename {tests/files/typescript-export-assign-default-namespace => test/fixtures/typescript-extended-config}/index.d.ts (63%) create mode 100644 test/fixtures/typescript-extended-config/tsconfig.base.json rename {tests/files => test/fixtures}/typescript-extended-config/tsconfig.json (100%) rename {tests/files => test/fixtures}/typescript-no-compiler-options/index.d.ts (100%) create mode 100644 test/fixtures/typescript-no-compiler-options/tsconfig.json rename {tests/files => test/fixtures}/typescript.ts (86%) create mode 100644 test/fixtures/umd.js create mode 100644 test/fixtures/unused-modules-reexport-crash/src/App.tsx create mode 100644 test/fixtures/unused-modules-reexport-crash/src/index.tsx rename {tests/files => test/fixtures}/unused-modules-reexport-crash/src/magic/index.js (100%) rename {tests/files => test/fixtures}/unused-modules-reexport-crash/src/magic/test.js (50%) rename {tests/files => test/fixtures}/webpack.config.js (66%) create mode 100644 test/fixtures/webpack.empty.config.js rename {tests/files => test/fixtures}/with-flow-typed/flow-typed/npm/myflowtyped_v1.x.x.js (73%) rename {tests/files => test/fixtures}/with-flow-typed/package.json (100%) rename {tests/files => test/fixtures}/with-syntax-error/package.json (100%) rename {tests/files => test/fixtures}/with-typescript-dev-dependencies/package.json (100%) create mode 100644 test/index.js delete mode 100644 test/mocha.opts create mode 100644 test/package.spec.js rename tests/src/rules/consistent-type-specifier-style.js => test/rules/consistent-type-specifier-style.spec.js (59%) create mode 100644 test/rules/default.spec.js rename tests/src/rules/dynamic-import-chunkname.js => test/rules/dynamic-import-chunkname.spec.js (68%) create mode 100644 test/rules/export.spec.js rename tests/src/rules/exports-last.js => test/rules/exports-last.spec.js (92%) rename tests/src/rules/extensions.js => test/rules/extensions.spec.js (75%) rename tests/src/rules/first.js => test/rules/first.spec.js (59%) rename tests/src/rules/group-exports.js => test/rules/group-exports.spec.js (64%) create mode 100644 test/rules/max-dependencies.spec.js rename tests/src/rules/named.js => test/rules/named.spec.js (59%) create mode 100644 test/rules/namespace.spec.js rename tests/src/rules/newline-after-import.js => test/rules/newline-after-import.spec.js (75%) rename tests/src/rules/no-absolute-path.js => test/rules/no-absolute-path.spec.js (95%) create mode 100644 test/rules/no-amd.spec.js create mode 100644 test/rules/no-anonymous-default-export.spec.js create mode 100644 test/rules/no-commonjs.spec.js create mode 100644 test/rules/no-cycle.spec.js rename tests/src/rules/no-default-export.js => test/rules/no-default-export.spec.js (87%) rename tests/src/rules/no-deprecated.js => test/rules/no-deprecated.spec.js (56%) create mode 100644 test/rules/no-duplicates.spec.js rename tests/src/rules/no-dynamic-require.js => test/rules/no-dynamic-require.spec.js (86%) create mode 100644 test/rules/no-empty-named-blocks.spec.js create mode 100644 test/rules/no-extraneous-dependencies.spec.js rename tests/src/rules/no-import-module-exports.js => test/rules/no-import-module-exports.spec.js (85%) rename tests/src/rules/no-internal-modules.js => test/rules/no-internal-modules.spec.js (64%) rename tests/src/rules/no-mutable-exports.js => test/rules/no-mutable-exports.spec.js (71%) create mode 100644 test/rules/no-named-as-default-member.spec.js create mode 100644 test/rules/no-named-as-default.spec.js rename tests/src/rules/no-named-default.js => test/rules/no-named-default.spec.js (62%) create mode 100644 test/rules/no-named-export.spec.js create mode 100644 test/rules/no-namespace.spec.js create mode 100644 test/rules/no-nodejs-modules.spec.js rename tests/src/rules/no-relative-packages.js => test/rules/no-relative-packages.spec.js (55%) create mode 100644 test/rules/no-relative-parent-imports.spec.js create mode 100644 test/rules/no-restricted-paths.spec.js rename tests/src/rules/no-self-import.js => test/rules/no-self-import.spec.js (94%) rename tests/src/rules/no-unassigned-import.js => test/rules/no-unassigned-import.spec.js (93%) rename tests/src/rules/no-unresolved.js => test/rules/no-unresolved.spec.js (71%) rename tests/src/rules/no-unused-modules.js => test/rules/no-unused-modules.spec.js (69%) rename tests/src/rules/no-useless-path-segments.js => test/rules/no-useless-path-segments.spec.js (60%) rename tests/src/rules/no-webpack-loader-syntax.js => test/rules/no-webpack-loader-syntax.spec.js (50%) rename tests/src/rules/order.js => test/rules/order.spec.js (72%) rename tests/src/rules/prefer-default-export.js => test/rules/prefer-default-export.spec.js (60%) rename tests/src/rules/unambiguous.js => test/rules/unambiguous.spec.js (88%) rename {tests/src => test}/utils.js (59%) delete mode 100755 tests/dep-time-travel.sh delete mode 100644 tests/files/.eslintrc.js delete mode 100644 tests/files/bar.js delete mode 100644 tests/files/bundled-dependencies/as-array-bundle-deps/node_modules/@generated/bar/index.js delete mode 100644 tests/files/bundled-dependencies/as-array-bundle-deps/node_modules/@generated/foo/index.js delete mode 100644 tests/files/bundled-dependencies/as-array-bundle-deps/package.json delete mode 100644 tests/files/bundled-dependencies/as-object/node_modules/@generated/bar/index.js delete mode 100644 tests/files/bundled-dependencies/as-object/node_modules/@generated/foo/index.js delete mode 100644 tests/files/bundled-dependencies/as-object/package.json delete mode 100644 tests/files/bundled-dependencies/race-condition/node_modules/@generated/bar/index.js delete mode 100644 tests/files/bundled-dependencies/race-condition/node_modules/@generated/foo/index.js delete mode 100644 tests/files/bundled-dependencies/race-condition/package.json delete mode 100644 tests/files/color.js delete mode 100644 tests/files/constants/index.js delete mode 100644 tests/files/cycles/es6/depth-one-dynamic.js delete mode 100644 tests/files/cycles/es6/depth-one-reexport.js delete mode 100644 tests/files/cycles/es6/depth-one.js delete mode 100644 tests/files/cycles/es6/depth-three-star.js delete mode 100644 tests/files/cycles/es6/depth-two.js delete mode 100644 tests/files/cycles/external-depth-two.js delete mode 100644 tests/files/cycles/external/depth-one.js delete mode 100644 tests/files/cycles/flow-typeof.js delete mode 100644 tests/files/cycles/flow-types-depth-one.js delete mode 100644 tests/files/cycles/flow-types-only-importing-multiple-types.js delete mode 100644 tests/files/cycles/flow-types-only-importing-type.js delete mode 100644 tests/files/cycles/flow-types.js delete mode 100644 tests/files/cycles/ignore/.eslintrc delete mode 100644 tests/files/cycles/ignore/index.js delete mode 100644 tests/files/cycles/intermediate-ignore.js delete mode 100644 tests/files/data.json delete mode 100644 tests/files/deep-es7/d.js delete mode 100644 tests/files/deep/d.js delete mode 100644 tests/files/default-class.js delete mode 100644 tests/files/default-export-namespace-string.js delete mode 100644 tests/files/default-export-string.js delete mode 100644 tests/files/default-export.js delete mode 100644 tests/files/dynamic-import-in-commonjs.js delete mode 100644 tests/files/empty-named-blocks.js delete mode 100644 tests/files/export-default-string-and-named.js delete mode 100644 tests/files/export-star-2/middle.js delete mode 100644 tests/files/export-star-2/upstream.js delete mode 100644 tests/files/export-star-3/b.ts delete mode 100644 tests/files/export-star-4/module/feature.jsx delete mode 100644 tests/files/export-star-4/module/index.ts delete mode 100644 tests/files/export-star/extfield.js delete mode 100644 tests/files/export-star/extfield2.js delete mode 100644 tests/files/export-star/models.js delete mode 100644 tests/files/foo-bar-resolver-invalid.js delete mode 100644 tests/files/internal-modules/api/service/index.js delete mode 100644 tests/files/internal-modules/plugins/plugin2/app/index.js delete mode 100644 tests/files/internal-modules/plugins/plugin2/index.js delete mode 100644 tests/files/issue-195/Bookings.js delete mode 100644 tests/files/issue-195/Endpoints.js delete mode 100644 tests/files/issue-195/Users.js delete mode 100644 tests/files/issue-370-commonjs-namespace/bar.js delete mode 100644 tests/files/issue-370-commonjs-namespace/foo.js delete mode 100644 tests/files/issue210.js delete mode 100644 tests/files/jsx/FooES7.js delete mode 100644 tests/files/jsx/bar/index.js delete mode 100644 tests/files/jsx/re-export.js delete mode 100644 tests/files/named-export-collision/a.js delete mode 100644 tests/files/named-export-collision/b.js delete mode 100644 tests/files/named-exports.js delete mode 100644 tests/files/named-trampoline.js delete mode 100644 tests/files/narcissist.js delete mode 100644 tests/files/nested-common.js delete mode 100644 tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-a.js delete mode 100644 tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-b.js delete mode 100644 tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-c.js delete mode 100644 tests/files/no-unused-modules/destructuring-a.js delete mode 100644 tests/files/no-unused-modules/destructuring-b.js delete mode 100644 tests/files/no-unused-modules/dynamic-import-js-2.js delete mode 100644 tests/files/no-unused-modules/dynamic-import-js.js delete mode 100644 tests/files/no-unused-modules/exports-for-dynamic-js-2.js delete mode 100644 tests/files/no-unused-modules/file-destructured-1.js delete mode 100644 tests/files/no-unused-modules/file-destructured-2.js delete mode 100644 tests/files/no-unused-modules/file-p.js delete mode 100644 tests/files/no-unused-modules/flow/flow-0.js delete mode 100644 tests/files/no-unused-modules/flow/flow-1.js delete mode 100644 tests/files/no-unused-modules/flow/flow-2.js delete mode 100644 tests/files/no-unused-modules/flow/flow-3.js delete mode 100644 tests/files/no-unused-modules/flow/flow-4.js delete mode 100644 tests/files/no-unused-modules/import-export-1.js delete mode 100644 tests/files/no-unused-modules/import-export-2.js delete mode 100644 tests/files/no-unused-modules/jsx/file-jsx-a.jsx delete mode 100644 tests/files/no-unused-modules/jsx/file-jsx-b.jsx delete mode 100644 tests/files/no-unused-modules/package.json delete mode 100644 tests/files/no-unused-modules/prefix-child.js delete mode 100644 tests/files/no-unused-modules/prefix-parent-bom.js delete mode 100644 tests/files/no-unused-modules/prefix-parent-bomhashbang.js delete mode 100644 tests/files/no-unused-modules/prefix-parent-hashbang.js delete mode 100644 tests/files/no-unused-modules/prefix-parent.js delete mode 100644 tests/files/no-unused-modules/privatePkg/package.json delete mode 100644 tests/files/no-unused-modules/renameDefault-2/components.js delete mode 100644 tests/files/no-unused-modules/typescript/dynamic-import-ts.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-a-import-type.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-a.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-b-unused.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-b-used-as-type.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-b.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-c-unused.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-c-used-as-type.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-c.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-d-unused.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-d-used-as-type.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-d.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-e-unused.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-e-used-as-type.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-e.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-f-import-type.ts delete mode 100644 tests/files/no-unused-modules/typescript/file-ts-f.ts delete mode 100644 tests/files/node_modules/@generated/bar/index.js delete mode 100644 tests/files/node_modules/@generated/bar/package.json delete mode 100644 tests/files/node_modules/@generated/foo/index.js delete mode 100644 tests/files/node_modules/@generated/foo/package.json delete mode 100644 tests/files/node_modules/@org/not-a-dependency/foo.js delete mode 100644 tests/files/node_modules/@org/not-a-dependency/index.js delete mode 100644 tests/files/node_modules/@org/not-a-dependency/package.json delete mode 100644 tests/files/node_modules/@org/package/index.js delete mode 100644 tests/files/node_modules/@org/package/internal.js delete mode 100644 tests/files/node_modules/@org/package/package.json delete mode 100644 tests/files/node_modules/a/index.js delete mode 100644 tests/files/node_modules/a/package.json delete mode 100644 tests/files/node_modules/chai/index.js delete mode 100644 tests/files/node_modules/chai/package.json delete mode 100644 tests/files/node_modules/es6-module/index.js delete mode 100644 tests/files/node_modules/es6-module/package.json delete mode 120000 tests/files/node_modules/eslint-import-resolver-foo/index.js delete mode 100644 tests/files/node_modules/eslint-import-resolver-foo/package.json delete mode 100644 tests/files/node_modules/esm-package-not-in-pkg-json/esm-module/index.js delete mode 100644 tests/files/node_modules/esm-package-not-in-pkg-json/esm-module/package.json delete mode 100644 tests/files/node_modules/esm-package-not-in-pkg-json/index.js delete mode 100644 tests/files/node_modules/esm-package-not-in-pkg-json/package.json delete mode 100644 tests/files/node_modules/esm-package/esm-module/index.js delete mode 100644 tests/files/node_modules/esm-package/esm-module/package.json delete mode 100644 tests/files/node_modules/esm-package/index.js delete mode 100644 tests/files/node_modules/esm-package/package.json delete mode 100644 tests/files/node_modules/exceljs/excel.js delete mode 100644 tests/files/node_modules/exceljs/package.json delete mode 100644 tests/files/node_modules/jquery/dist/jquery.js delete mode 100644 tests/files/node_modules/jquery/package.json delete mode 100644 tests/files/node_modules/jsx-module/foo.jsx delete mode 100644 tests/files/node_modules/jsx-module/package.json delete mode 100644 tests/files/node_modules/left-pad/index.js delete mode 100644 tests/files/node_modules/left-pad/not-a-dependency delete mode 100644 tests/files/node_modules/left-pad/package.json delete mode 100644 tests/files/node_modules/not-a-dependency/index.js delete mode 100644 tests/files/node_modules/not-a-dependency/package.json delete mode 100644 tests/files/node_modules/react/index.js delete mode 100644 tests/files/node_modules/react/not-a-dependency delete mode 100644 tests/files/node_modules/react/package.json delete mode 100644 tests/files/node_modules/rxjs/operators/package.json delete mode 100644 tests/files/node_modules/rxjs/package.json delete mode 100644 tests/files/package-scoped/index.js delete mode 100644 tests/files/package/index.js delete mode 100644 tests/files/test-module/index.js delete mode 100644 tests/files/typescript-d-ts/file1.ts delete mode 100644 tests/files/typescript-declare-module.ts delete mode 100644 tests/files/typescript-default.ts delete mode 100644 tests/files/typescript-export-as-default-namespace/tsconfig.json delete mode 100644 tests/files/typescript-export-assign-default-namespace/tsconfig.json delete mode 100644 tests/files/typescript-export-assign-default-reexport.ts delete mode 100644 tests/files/typescript-export-assign-default.d.ts delete mode 100644 tests/files/typescript-export-assign-function.ts delete mode 100644 tests/files/typescript-export-assign-object/tsconfig.json delete mode 100644 tests/files/typescript-export-assign-property.ts delete mode 100644 tests/files/typescript-export-react-test-renderer/index.d.ts delete mode 100644 tests/files/typescript-export-react-test-renderer/tsconfig.json delete mode 100644 tests/files/typescript-no-compiler-options/tsconfig.json delete mode 100644 tests/files/umd.js delete mode 100644 tests/files/unused-modules-reexport-crash/src/App.tsx delete mode 100644 tests/files/unused-modules-reexport-crash/src/index.tsx delete mode 100644 tests/files/webpack.empty.config.js delete mode 100644 tests/index.js delete mode 100644 tests/src/cli.js delete mode 100644 tests/src/config/typescript.js delete mode 100644 tests/src/core/docsUrl.js delete mode 100644 tests/src/core/getExports.js delete mode 100644 tests/src/core/hash.js delete mode 100644 tests/src/core/ignore.js delete mode 100644 tests/src/core/importType.js delete mode 100644 tests/src/core/parse.js delete mode 100644 tests/src/core/resolve.js delete mode 100644 tests/src/package.js delete mode 100644 tests/src/rules/default.js delete mode 100644 tests/src/rules/export.js delete mode 100644 tests/src/rules/max-dependencies.js delete mode 100644 tests/src/rules/namespace.js delete mode 100644 tests/src/rules/no-amd.js delete mode 100644 tests/src/rules/no-anonymous-default-export.js delete mode 100644 tests/src/rules/no-commonjs.js delete mode 100644 tests/src/rules/no-cycle.js delete mode 100644 tests/src/rules/no-duplicates.js delete mode 100644 tests/src/rules/no-empty-named-blocks.js delete mode 100644 tests/src/rules/no-extraneous-dependencies.js delete mode 100644 tests/src/rules/no-named-as-default-member.js delete mode 100644 tests/src/rules/no-named-as-default.js delete mode 100644 tests/src/rules/no-named-export.js delete mode 100644 tests/src/rules/no-namespace.js delete mode 100644 tests/src/rules/no-nodejs-modules.js delete mode 100644 tests/src/rules/no-relative-parent-imports.js delete mode 100644 tests/src/rules/no-restricted-paths.js delete mode 100644 utils/.attw.json delete mode 100644 utils/.npmignore delete mode 100644 utils/.nycrc delete mode 100644 utils/CHANGELOG.md delete mode 100644 utils/ModuleCache.d.ts delete mode 100644 utils/declaredScope.d.ts delete mode 100644 utils/declaredScope.js delete mode 100644 utils/hash.d.ts delete mode 100644 utils/hash.js delete mode 100644 utils/ignore.d.ts delete mode 100644 utils/ignore.js delete mode 100644 utils/module-require.d.ts delete mode 100644 utils/module-require.js delete mode 100644 utils/moduleVisitor.d.ts delete mode 100644 utils/package.json delete mode 100644 utils/parse.d.ts delete mode 100644 utils/pkgDir.d.ts delete mode 100644 utils/pkgDir.js delete mode 100644 utils/pkgUp.d.ts delete mode 100644 utils/readPkgUp.d.ts delete mode 100644 utils/resolve.d.ts delete mode 100644 utils/resolve.js delete mode 100644 utils/tsconfig.json delete mode 100644 utils/types.d.ts delete mode 100644 utils/unambiguous.d.ts delete mode 100644 utils/visit.d.ts delete mode 100644 utils/visit.js create mode 100644 yarn.lock diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 883c03b79..000000000 --- a/.babelrc +++ /dev/null @@ -1,18 +0,0 @@ -{ - "presets": ["airbnb"], - "sourceMaps": "inline", - "retainLines": true, - "env": { - "test": { - "plugins": [ - "istanbul", - ["module-resolver", { "root": ["./src/"] }], - ] - }, - "testCompiled": { - "plugins": [ - ["module-resolver", { "root": ["./lib/"] }], - ] - } - } -} diff --git a/.coveralls.yml b/.coveralls.yml deleted file mode 100644 index b8ebe05a1..000000000 --- a/.coveralls.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -repo_token: fW3moW39Z8pKOgqTnUMT68DnNCd2SM8Ly diff --git a/.editorconfig b/.editorconfig index e2bfac523..8d2699e51 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,9 @@ -root = true +root=true [*] -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true -indent_style = space -indent_size = 2 -end_of_line = lf +charset=utf-8 +trim_trailing_whitespace=true +insert_final_newline=true +indent_style=space +indent_size=2 +end_of_line=lf diff --git a/.eslintignore b/.eslintignore index 9d2200682..4222d0786 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,12 +1,10 @@ lib coverage -.nyc_output node_modules -tests/files/malformed.js -tests/files/with-syntax-error -tests/files/just-json-files/invalid.json -tests/files/typescript-d-ts/ -resolvers/webpack/test/files -# we want to ignore "tests/files" here, but unfortunately doing so would +test/fixtures/malformed.js +test/fixtures/with-syntax-error +test/fixtures/just-json-files/invalid.json +test/fixtures/typescript-d-ts/ +# we want to ignore "test/fixtures" here, but unfortunately doing so would # interfere with unit test and fail it for some reason. -# tests/files +# test/fixtures diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index ddf7bc562..000000000 --- a/.eslintrc +++ /dev/null @@ -1,248 +0,0 @@ -{ - "root": true, - "plugins": [ - "eslint-plugin", - "import", - ], - "extends": [ - "eslint:recommended", - "plugin:eslint-plugin/recommended", - "plugin:import/recommended", - ], - "env": { - "node": true, - "es6": true, - "es2017": true, - }, - "parserOptions": { - "sourceType": "module", - "ecmaVersion": 2020, - }, - "rules": { - "array-bracket-spacing": [2, "never"], - "arrow-body-style": [2, "as-needed"], - "arrow-parens": [2, "always"], - "arrow-spacing": [2, { "before": true, "after": true }], - "block-spacing": [2, "always"], - "brace-style": [2, "1tbs", { "allowSingleLine": true }], - "comma-dangle": ["error", { - "arrays": "always-multiline", - "objects": "always-multiline", - "imports": "always-multiline", - "exports": "always-multiline", - "functions": "always-multiline", - }], - "comma-spacing": [2, { "before": false, "after": true }], - "comma-style": [2, "last"], - "computed-property-spacing": [2, "never"], - "curly": [2, "all"], - "default-case": [2, { "commentPattern": "(?:)" }], - "default-case-last": [2], - "default-param-last": [2], - "dot-location": [2, "property"], - "dot-notation": [2, { "allowKeywords": true, "allowPattern": "throws" }], - "eol-last": [2, "always"], - "eqeqeq": [2, "allow-null"], - "for-direction": [2], - "function-call-argument-newline": [2, "consistent"], - "func-call-spacing": [2, "never"], - "implicit-arrow-linebreak": [2, "beside"], - "indent": [2, 2, { - "SwitchCase": 1, - "VariableDeclarator": 1, - "outerIIFEBody": 1, - "FunctionDeclaration": { - "parameters": 1, - "body": 1 - }, - "FunctionExpression": { - "parameters": 1, - "body": 1 - }, - "CallExpression": { - "arguments": 1 - }, - "ArrayExpression": 1, - "ObjectExpression": 1, - "ImportDeclaration": 1, - "flatTernaryExpressions": false, - }], - "jsx-quotes": [2, "prefer-double"], - "key-spacing": [2, { - "beforeColon": false, - "afterColon": true, - "mode": "strict", - }], - "keyword-spacing": ["error", { - "before": true, - "after": true, - "overrides": { - "return": { "after": true }, - "throw": { "after": true }, - "case": { "after": true } - } - }], - "linebreak-style": [2, "unix"], - "lines-around-directive": [2, { - "before": "always", - "after": "always", - }], - "max-len": 0, - "new-parens": 2, - "no-array-constructor": 2, - "no-compare-neg-zero": 2, - "no-cond-assign": [2, "always"], - "no-extra-parens": 2, - "no-multiple-empty-lines": [2, { "max": 1, "maxEOF": 1, "maxBOF": 0 }], - "no-return-assign": [2, "always"], - "no-trailing-spaces": 2, - "no-var": 2, - "object-curly-spacing": [2, "always"], - "object-shorthand": ["error", "always", { - "ignoreConstructors": false, - "avoidQuotes": false, - "avoidExplicitReturnArrows": true, - }], - "one-var": [2, "never"], - "operator-linebreak": [2, "none", { - "overrides": { - "?": "before", - ":": "before", - "&&": "before", - "||": "before", - }, - }], - "prefer-const": 2, - "prefer-object-spread": 2, - "prefer-rest-params": 2, - "prefer-template": 2, - "quote-props": [2, "as-needed", { "keywords": false }], - "quotes": [2, "single", { - "allowTemplateLiterals": true, - "avoidEscape": true, - }], - "rest-spread-spacing": [2, "never"], - "semi": [2, "always"], - "semi-spacing": [2, { "before": false, "after": true }], - "semi-style": [2, "last"], - "space-before-blocks": [2, { "functions": "always", "keywords": "always", "classes": "always" }], - "space-before-function-paren": ["error", { - "anonymous": "always", - "named": "never", - "asyncArrow": "always", - }], - "space-in-parens": [2, "never"], - "space-infix-ops": [2], - "space-unary-ops": [2, { "words": true, "nonwords": false }], - "switch-colon-spacing": [2, { "after": true, "before": false }], - "template-curly-spacing": [2, "never"], - "template-tag-spacing": [2, "never"], - "unicode-bom": [2, "never"], - "use-isnan": [2, { "enforceForSwitchCase": true }], - "valid-typeof": [2], - "wrap-iife": [2, "outside", { "functionPrototypeMethods": true }], - "wrap-regex": [2], - "yield-star-spacing": [2, { "before": false, "after": true }], - "yoda": [2, "never", { "exceptRange": true, "onlyEquality": false }], - - "eslint-plugin/consistent-output": [ - "error", - "always", - ], - "eslint-plugin/meta-property-ordering": "error", - "eslint-plugin/no-deprecated-context-methods": "error", - "eslint-plugin/no-deprecated-report-api": "off", - "eslint-plugin/prefer-replace-text": "error", - "eslint-plugin/report-message-format": "error", - "eslint-plugin/require-meta-docs-description": ["error", { "pattern": "^(Enforce|Ensure|Prefer|Forbid).+\\.$" }], - "eslint-plugin/require-meta-schema": "error", - "eslint-plugin/require-meta-type": "error", - - // dog fooding - "import/no-extraneous-dependencies": ["error", { - "devDependencies": [ - "tests/**", - "resolvers/*/test/**", - "scripts/**" - ], - "optionalDependencies": false, - "peerDependencies": true, - "bundledDependencies": false, - }], - "import/unambiguous": "off", - }, - - "settings": { - "import/resolver": { - "node": { - "paths": [ - "src", - ], - }, - }, - }, - - "overrides": [ - { - "files": "scripts/**", - "rules": { - "no-console": "off", - }, - }, - { - "files": [ - "resolvers/**", - "utils/**", - ], - "env": { - "es6": false, - }, - "parserOptions": { - "sourceType": "module", - "ecmaVersion": 2016, - }, - "rules": { - "comma-dangle": ["error", { - "arrays": "always-multiline", - "objects": "always-multiline", - "imports": "always-multiline", - "exports": "always-multiline", - "functions": "never" - }], - "prefer-destructuring": "off", - "prefer-object-spread": "off", - "prefer-rest-params": "off", - "prefer-spread": "off", - "prefer-template": "off", - } - }, - { - "files": [ - "resolvers/webpack/**", - "utils/**", - ], - "rules": { - "no-console": 1, - }, - }, - { - "files": [ - "resolvers/*/test/**/*", - ], - "env": { - "mocha": true, - "es6": false - }, - }, - { - "files": "tests/**", - "env": { - "mocha": true, - }, - "rules": { - "max-len": 0, - "import/default": 0, - }, - }, - ], -} diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..a577a3f41 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,68 @@ +module.exports = { + root: true, + plugins: ['eslint-plugin', 'import-x'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:eslint-plugin/recommended', + 'plugin:import-x/recommended', + 'plugin:prettier/recommended', + ], + env: { + node: true, + es6: true, + es2017: true, + }, + parserOptions: { + sourceType: 'module', + ecmaVersion: 2020, + }, + rules: { + '@typescript-eslint/no-var-requires': 'off', + + 'eslint-plugin/consistent-output': ['error', 'always'], + 'eslint-plugin/meta-property-ordering': 'error', + 'eslint-plugin/no-deprecated-context-methods': 'error', + 'eslint-plugin/no-deprecated-report-api': 'off', + 'eslint-plugin/prefer-replace-text': 'error', + 'eslint-plugin/report-message-format': 'error', + 'eslint-plugin/require-meta-docs-description': [ + 'error', + { pattern: '^(Enforce|Ensure|Prefer|Forbid).+\\.$' }, + ], + 'eslint-plugin/require-meta-schema': 'error', + 'eslint-plugin/require-meta-type': 'error', + + // dog fooding + 'import-x/no-extraneous-dependencies': [ + 'error', + { + devDependencies: ['test/**'], + optionalDependencies: false, + peerDependencies: true, + bundledDependencies: false, + }, + ], + 'import-x/unambiguous': 'off', + }, + + settings: { + 'import-x/resolver': { + node: { + paths: ['src'], + }, + }, + }, + + overrides: [ + { + files: 'test/**', + env: { + jest: true, + }, + rules: { + 'import-x/default': 0, + }, + }, + ], +} diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 0ef28872f..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,12 +0,0 @@ -# These are supported funding model platforms - -github: [ljharb] -patreon: # Replace with a single Patreon username -open_collective: eslint-plugin-import # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: npm/eslint-plugin-import -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/workflows/node-4+.yml b/.github/workflows/node-4+.yml deleted file mode 100644 index 2925adda8..000000000 --- a/.github/workflows/node-4+.yml +++ /dev/null @@ -1,123 +0,0 @@ -name: 'Tests: node.js' - -on: [pull_request, push] - -permissions: - contents: read - -jobs: - matrix: - runs-on: ubuntu-latest - outputs: - latest: ${{ steps.set-matrix.outputs.requireds }} - minors: ${{ steps.set-matrix.outputs.optionals }} - steps: - - uses: ljharb/actions/node/matrix@main - id: set-matrix - with: - versionsAsRoot: true - type: majors - preset: '>= 6' # preset: '>=4' # see https://github.com/import-js/eslint-plugin-import/issues/2053 - - latest: - needs: [matrix] - name: 'majors' - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - node-version: ${{ fromJson(needs.matrix.outputs.latest) }} - eslint: - - 8 - - 7 - - 6 - - 5 - - 4 - - 3 - - 2 - include: - - node-version: 'lts/*' - eslint: 7 - ts-parser: 4 - env: - TS_PARSER: 4 - - node-version: 'lts/*' - eslint: 7 - ts-parser: 3 - env: - TS_PARSER: 3 - - node-version: 'lts/*' - eslint: 7 - ts-parser: 2 - env: - TS_PARSER: 2 - exclude: - - node-version: 15 - eslint: 8 - - node-version: 13 - eslint: 8 - - node-version: 11 - eslint: 8 - - node-version: 10 - eslint: 8 - - node-version: 9 - eslint: 8 - - node-version: 9 - eslint: 7 - - node-version: 8 - eslint: 8 - - node-version: 8 - eslint: 7 - - node-version: 7 - eslint: 8 - - node-version: 7 - eslint: 7 - - node-version: 7 - eslint: 6 - - node-version: 6 - eslint: 8 - - node-version: 6 - eslint: 7 - - node-version: 6 - eslint: 6 - - node-version: 5 - eslint: 8 - - node-version: 5 - eslint: 7 - - node-version: 5 - eslint: 6 - - node-version: 5 - eslint: 5 - - node-version: 4 - eslint: 8 - - node-version: 4 - eslint: 7 - - node-version: 4 - eslint: 6 - - node-version: 4 - eslint: 5 - - steps: - - uses: actions/checkout@v3 - - uses: ljharb/actions/node/install@main - continue-on-error: ${{ matrix.eslint == 4 && matrix.node-version == 4 }} - name: 'nvm install ${{ matrix.node-version }} && npm install, with eslint ${{ matrix.eslint }}' - env: - NPM_CONFIG_LEGACY_PEER_DEPS: ${{ matrix.node-version == 11 && false || true }} - ESLINT_VERSION: ${{ matrix.eslint }} - TRAVIS_NODE_VERSION: ${{ matrix.node-version }} - with: - node-version: ${{ matrix.node-version }} - after_install: npm run copy-metafiles && ./tests/dep-time-travel.sh - skip-ls-check: true - - run: npm run pretest - - run: npm run tests-only - - uses: codecov/codecov-action@v3 - - node: - name: 'node 4+' - needs: [latest] - runs-on: ubuntu-latest - steps: - - run: 'echo tests completed' diff --git a/.github/workflows/node-pretest.yml b/.github/workflows/node-pretest.yml deleted file mode 100644 index e4340018e..000000000 --- a/.github/workflows/node-pretest.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: 'Tests: pretest/posttest' - -on: [pull_request, push] - -permissions: - contents: read - -jobs: - # pretest: - # runs-on: ubuntu-latest - - # steps: - # - uses: actions/checkout@v3 - # - uses: ljharb/actions/node/install@main - # name: 'nvm install lts/* && npm install' - # with: - # node-version: 'lts/*' - # skip-ls-check: true - # - run: npm run pretest - - posttest: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - uses: ljharb/actions/node/install@main - name: 'nvm install lts/* && npm install' - with: - node-version: 'lts/*' - skip-ls-check: true - - run: npm run posttest diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml deleted file mode 100644 index a6fb4e4cb..000000000 --- a/.github/workflows/packages.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: 'Tests: packages' - -on: [pull_request, push] - -permissions: - contents: read - -jobs: - matrix: - runs-on: ubuntu-latest - outputs: - latest: ${{ steps.set-matrix.outputs.requireds }} - minors: ${{ steps.set-matrix.outputs.optionals }} - steps: - - uses: ljharb/actions/node/matrix@main - id: set-matrix - with: - type: 'majors' - preset: '>= 6' # preset: '>=4' # see https://github.com/import-js/eslint-plugin-import/issues/2053 - versionsAsRoot: true - - tests: - needs: [matrix] - name: 'packages' - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - node-version: ${{ fromJson(needs.matrix.outputs.latest) }} - eslint: - - 8 - - 7 - package: - - resolvers/node - - resolvers/webpack - # - memo-parser - # - utils - - steps: - - uses: actions/checkout@v3 - - uses: ljharb/actions/node/install@main - name: 'nvm install ${{ matrix.node-version }} && npm install' - env: - NPM_CONFIG_LEGACY_PEER_DEPS: ${{ matrix.node-version == 11 && false || true }} - ESLINT_VERSION: ${{ matrix.eslint }} - TRAVIS_NODE_VERSION: ${{ matrix.node-version }} - with: - node-version: ${{ matrix.node-version }} - after_install: npm run copy-metafiles && ./tests/dep-time-travel.sh && cd ${{ matrix.package }} && npm install - skip-ls-check: true - - run: cd ${{ matrix.package }} && npm run tests-only - - uses: codecov/codecov-action@v3 - - packages: - name: 'packages: all tests' - needs: [tests] - runs-on: ubuntu-latest - steps: - - run: 'echo tests completed' diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml deleted file mode 100644 index b9e1712fc..000000000 --- a/.github/workflows/rebase.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: Automatic Rebase - -on: [pull_request_target] - -jobs: - _: - uses: ljharb/actions/.github/workflows/rebase.yml@main - secrets: - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/require-allow-edits.yml b/.github/workflows/require-allow-edits.yml deleted file mode 100644 index eb3631b9e..000000000 --- a/.github/workflows/require-allow-edits.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Require “Allow Edits” - -on: [pull_request_target] - -permissions: - contents: read - -jobs: - _: - permissions: - pull-requests: read # for ljharb/require-allow-edits to check 'allow edits' on PR - name: "Require “Allow Edits”" - - runs-on: ubuntu-latest - - steps: - - uses: ljharb/require-allow-edits@main diff --git a/.gitignore b/.gitignore index 587dbd928..ef4aaa800 100644 --- a/.gitignore +++ b/.gitignore @@ -19,16 +19,6 @@ coverage # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release -# Copied from ./LICENSE for the npm module releases -memo-parser/LICENSE -resolvers/node/LICENSE -resolvers/webpack/LICENSE -utils/LICENSE -memo-parser/.npmrc -resolvers/node/.npmrc -resolvers/webpack/.npmrc -utils/.npmrc - # Dependency directory # Commenting this out is preferred by some people, see # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- @@ -45,10 +35,5 @@ node_modules lib/ **/.nyc_output/ -# Only apps should have lockfiles -yarn.lock -package-lock.json -npm-shrinkwrap.json - # macOS .DS_Store diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 6c93bcba7..000000000 --- a/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -package-lock=false -install-links=false diff --git a/.nycrc b/.nycrc deleted file mode 100644 index 5d75e2157..000000000 --- a/.nycrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "all": true, - "check-coverage": false, - "reporter": ["text-summary", "lcov", "text", "html", "json"], - "require": [ - "babel-register" - ], - "sourceMap": true, - "instrument": false, - "exclude": [ - "coverage", - "test", - "tests", - "resolvers/*/test", - "scripts", - "memo-parser", - "lib" - ] -} diff --git a/.prettierrc.mjs b/.prettierrc.mjs new file mode 100644 index 000000000..4fe0732d6 --- /dev/null +++ b/.prettierrc.mjs @@ -0,0 +1,14 @@ +import baseConfig from '@1stg/prettier-config' + +export default { + ...baseConfig, + overrides: [ + ...baseConfig.overrides, + { + files: ['**/*.js'], + options: { + parser: 'babel-flow', + }, + }, + ], +} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 21a7070fb..000000000 --- a/.travis.yml +++ /dev/null @@ -1,40 +0,0 @@ -language: node_js - -# osx backlog is often deep, so to be polite we can just hit these highlights -matrix: - include: - - os: osx - env: ESLINT_VERSION=5 - node_js: 14 - - os: osx - env: ESLINT_VERSION=5 - node_js: 12 - - os: osx - env: ESLINT_VERSION=5 - node_js: 10 - - os: osx - env: ESLINT_VERSION=4 - node_js: 8 - - os: osx - env: ESLINT_VERSION=3 - node_js: 6 - - os: osx - env: ESLINT_VERSION=2 - node_js: 4 - - fast_finish: true - -before_install: - - 'nvm install-latest-npm' - - 'NPM_CONFIG_LEGACY_PEER_DEPS=true npm install' - - 'npm run copy-metafiles' -install: - - 'NPM_CONFIG_LEGACY_PEER_DEPS=true npm install' - - 'if [ -n "${ESLINT_VERSION}" ]; then ./tests/dep-time-travel.sh; fi' - - 'npm run pretest' - -script: - - npm run tests-only - -after_success: - - bash <(curl -Os https://uploader.codecov.io/latest/linux/codecov) diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 06cdb922e..000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,1937 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -This project adheres to [Semantic Versioning](https://semver.org/). -This change log adheres to standards from [Keep a CHANGELOG](https://keepachangelog.com). - -## [Unreleased] - -### Added -- [`dynamic-import-chunkname`]: add `allowEmpty` option to allow empty leading comments ([#2942], thanks [@JiangWeixian]) - -### Changed -- [Docs] `no-extraneous-dependencies`: Make glob pattern description more explicit ([#2944], thanks [@mulztob]) -- [`no-unused-modules`]: add console message to help debug [#2866] - -## [2.29.1] - 2023-12-14 - -### Fixed -- [`no-extraneous-dependencies`]: ignore `export type { ... } from '...'` when `includeTypes` is `false` ([#2919], thanks [@Pandemic1617]) -- [`no-unused-modules`]: support export patterns with array destructuring ([#2930], thanks [@ljharb]) -- [Deps] update `tsconfig-paths` ([#2447], thanks [@domdomegg]) - -## [2.29.0] - 2023-10-22 - -### Added -- TypeScript config: add .cts and .mts extensions ([#2851], thanks [@Zamiell]) -- [`newline-after-import`]: new option `exactCount` and docs update ([#1933], thanks [@anikethsaha] and [@reosarevok]) -- [`newline-after-import`]: fix `exactCount` with `considerComments` false positive, when there is a leading comment ([#2884], thanks [@kinland]) - -## [2.28.1] - 2023-08-18 - -### Fixed -- [`order`]: revert breaking change to single nested group ([#2854], thanks [@yndajas]) - -### Changed -- [Docs] remove duplicate fixable notices in docs ([#2850], thanks [@bmish]) - -## [2.28.0] - 2023-07-27 - -### Fixed -- [`no-duplicates`]: remove duplicate identifiers in duplicate imports ([#2577], thanks [@joe-matsec]) -- [`consistent-type-specifier-style`]: fix accidental removal of comma in certain cases ([#2754], thanks [@bradzacher]) -- [Perf] `ExportMap`: Improve `ExportMap.for` performance on larger codebases ([#2756], thanks [@leipert]) -- [`no-extraneous-dependencies`]/TypeScript: do not error when importing inline type from dev dependencies ([#1820], thanks [@andyogo]) -- [`newline-after-import`]/TypeScript: do not error when re-exporting a namespaced import ([#2832], thanks [@laurens-dg]) -- [`order`]: partial fix for [#2687] (thanks [@ljharb]) -- [`no-duplicates`]: Detect across type and regular imports ([#2835], thanks [@benkrejci]) -- [`extensions`]: handle `.` and `..` properly ([#2778], thanks [@benasher44]) -- [`no-unused-modules`]: improve schema (thanks [@ljharb]) -- [`no-unused-modules`]: report error on binding instead of parent export ([#2842], thanks [@Chamion]) - -### Changed -- [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) -- [Docs] [`group-exports`]: fix syntax highlighting ([#2699], thanks [@devinrhode2]) -- [Docs] [`extensions`]: reference node ESM behavior ([#2748], thanks [@xM8WVqaG]) -- [Refactor] [`exports-last`]: use `array.prototype.findlastindex` (thanks [@ljharb]) -- [Refactor] [`no-anonymous-default-export`]: use `object.fromentries` (thanks [@ljharb]) -- [Refactor] [`no-unused-modules`]: use `array.prototype.flatmap` (thanks [@ljharb]) - -## [2.27.5] - 2023-01-16 - -### Fixed -- [`order]`: Fix group ranks order when alphabetizing ([#2674], thanks [@Pearce-Ropion]) - -## [2.27.4] - 2023-01-11 - -### Fixed -- `semver` should be a prod dep ([#2668]) - -## [2.27.3] - 2023-01-11 - -### Fixed -- [`no-empty-named-blocks`]: rewrite rule to only check import declarations ([#2666]) - -## [2.27.2] - 2023-01-11 - -### Fixed -- [`no-duplicates`]: do not unconditionally require `typescript` ([#2665]) - -## [2.27.1] - 2023-01-11 - -### Fixed -- `array.prototype.flatmap` should be a prod dep ([#2664], thanks [@cristobal]) - -## [2.27.0] - 2023-01-11 - -### Added -- [`newline-after-import`]: add `considerComments` option ([#2399], thanks [@pri1311]) -- [`no-cycle`]: add `allowUnsafeDynamicCyclicDependency` option ([#2387], thanks [@GerkinDev]) -- [`no-restricted-paths`]: support arrays for `from` and `target` options ([#2466], thanks [@AdriAt360]) -- [`no-anonymous-default-export`]: add `allowNew` option ([#2505], thanks [@DamienCassou]) -- [`order`]: Add `distinctGroup` option ([#2395], thanks [@hyperupcall]) -- [`no-extraneous-dependencies`]: Add `includeInternal` option ([#2541], thanks [@bdwain]) -- [`no-extraneous-dependencies`]: Add `includeTypes` option ([#2543], thanks [@bdwain]) -- [`order`]: new `alphabetize.orderImportKind` option to sort imports with same path based on their kind (`type`, `typeof`) ([#2544], thanks [@stropho]) -- [`consistent-type-specifier-style`]: add rule ([#2473], thanks [@bradzacher]) -- Add [`no-empty-named-blocks`] rule ([#2568], thanks [@guilhermelimak]) -- [`prefer-default-export`]: add "target" option ([#2602], thanks [@azyzz228]) -- [`no-absolute-path`]: add fixer ([#2613], thanks [@adipascu]) -- [`no-duplicates`]: support inline type import with `inlineTypeImport` option ([#2475], thanks [@snewcomer]) - -### Fixed -- [`order`]: move nested imports closer to main import entry ([#2396], thanks [@pri1311]) -- [`no-restricted-paths`]: fix an error message ([#2466], thanks [@AdriAt360]) -- [`no-restricted-paths`]: use `Minimatch.match` instead of `minimatch` to comply with Windows Native paths ([#2466], thanks [@AdriAt360]) -- [`order`]: require with member expression could not be fixed if alphabetize.order was used ([#2490], thanks [@msvab]) -- [`order`]: leave more space in rankings for consecutive path groups ([#2506], thanks [@Pearce-Ropion]) -- [`no-cycle`]: add ExportNamedDeclaration statements to dependencies ([#2511], thanks [@BenoitZugmeyer]) -- [`dynamic-import-chunkname`]: prevent false report on a valid webpack magic comment ([#2330], thanks [@mhmadhamster]) -- [`export`]: do not error on TS export overloads ([#1590], thanks [@ljharb]) -- [`no-unresolved`], [`extensions`]: ignore type only exports ([#2436], thanks [@Lukas-Kullmann]) -- `ExportMap`: add missing param to function ([#2589], thanks [@Fdawgs]) -- [`no-unused-modules`]: `checkPkgFieldObject` filters boolean fields from checks ([#2598], thanks [@mpint]) -- [`no-cycle`]: accept Flow `typeof` imports, just like `type` ([#2608], thanks [@gnprice]) -- [`no-import-module-exports`]: avoid a false positive for import variables ([#2315], thanks [@BarryThePenguin]) - -### Changed -- [Tests] [`named`]: Run all TypeScript test ([#2427], thanks [@ProdigySim]) -- [readme] note use of typescript in readme `import/extensions` section ([#2440], thanks [@OutdatedVersion]) -- [Docs] [`order`]: use correct default value ([#2392], thanks [@hyperupcall]) -- [meta] replace git.io link in comments with the original URL ([#2444], thanks [@liby]) -- [Docs] remove global install in readme ([#2412], thanks [@aladdin-add]) -- [readme] clarify `eslint-import-resolver-typescript` usage ([#2503], thanks [@JounQin]) -- [Refactor] [`no-cycle`]: Add per-run caching of traversed paths ([#2419], thanks [@nokel81]) -- [Performance] `ExportMap`: add caching after parsing for an ambiguous module ([#2531], thanks [@stenin-nikita]) -- [Docs] [`no-useless-path-segments`]: fix paths ([#2424], thanks [@s-h-a-d-o-w]) -- [Tests] [`no-cycle`]: add passing test cases ([#2438], thanks [@georeith]) -- [Refactor] [`no-extraneous-dependencies`] improve performance using cache ([#2374], thanks [@meowtec]) -- [meta] `CONTRIBUTING.md`: mention inactive PRs ([#2546], thanks [@stropho]) -- [readme] make json for setting groups multiline ([#2570], thanks [@bertyhell]) -- [Tests] [`no-restricted-paths`]: Tests for `import type` statements ([#2459], thanks [@golergka]) -- [Tests] [`no-restricted-paths`]: fix one failing `import type` test case, submitted by [@golergka], thanks [@azyzz228] -- [Docs] automate docs with eslint-doc-generator ([#2582], thanks [@bmish]) -- [readme] Increase clarity around typescript configuration ([#2588], thanks [@Nfinished]) -- [Docs] update `eslint-doc-generator` to v1.0.0 ([#2605], thanks [@bmish]) -- [Perf] [`no-cycle`], [`no-internal-modules`], [`no-restricted-paths`]: use `anyOf` instead of `oneOf` (thanks [@ljharb], [@remcohaszing]) - -## [2.26.0] - 2022-04-05 - -### Added -- [`no-named-default`], [`no-default-export`], [`prefer-default-export`], [`no-named-export`], [`export`], [`named`], [`namespace`], [`no-unused-modules`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) -- [`no-dynamic-require`]: support dynamic import with espree ([#2371], thanks [@sosukesuzuki]) -- [`no-relative-packages`]: add fixer ([#2381], thanks [@forivall]) - -### Fixed -- [`default`]: `typescript-eslint-parser`: avoid a crash on exporting as namespace (thanks [@ljharb]) -- [`export`]/TypeScript: false positive for typescript namespace merging ([#1964], thanks [@magarcia]) -- [`no-duplicates`]: ignore duplicate modules in different TypeScript module declarations ([#2378], thanks [@remcohaszing]) -- [`no-unused-modules`]: avoid a crash when processing re-exports ([#2388], thanks [@ljharb]) - -### Changed -- [Tests] [`no-nodejs-modules`]: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) -- [Tests] [`default`], [`no-anonymous-default-export`], [`no-mutable-exports`], [`no-named-as-default-member`], [`no-named-as-default`]: add tests for arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) -- [Docs] [`no-unresolved`]: Fix RegExp escaping in readme ([#2332], thanks [@stephtr]) -- [Refactor] [`namespace`]: try to improve performance ([#2340], thanks [@ljharb]) -- [Docs] make rule doc titles consistent ([#2393], thanks [@TheJaredWilcurt]) -- [Docs] [`order`]: TS code examples should use TS code blocks ([#2411], thanks [@MM25Zamanian]) -- [Docs] [`no-unresolved`]: fix link ([#2417], thanks [@kylemh]) - -## [2.25.4] - 2022-01-02 - -### Fixed -- `importType`: avoid crashing on a non-string' ([#2305], thanks [@ljharb]) -- [`first`]: prevent crash when parsing angular templates ([#2210], thanks [@ljharb]) -- `importType`: properly resolve `@/*`-aliased imports as internal ([#2334], thanks [@ombene]) -- [`named`]/`ExportMap`: handle named imports from CJS modules that use dynamic import ([#2341], thanks [@ludofischer]) - -### Changed -- [`no-default-import`]: report on the token "default" instead of the entire node ([#2299], thanks [@pmcelhaney]) -- [Docs] [`order`]: Remove duplicate mention of default ([#2280], thanks [@johnthagen]) -- [Deps] update `eslint-module-utils` - -## [2.25.3] - 2021-11-09 - -### Fixed -- [`extensions`]: ignore unresolveable type-only imports ([#2270], [#2271], thanks [@jablko]) -- `importType`: fix `isExternalModule` calculation ([#2282], thanks [@mx-bernhard]) -- [`no-import-module-exports`]: avoid false positives with a shadowed `module` or `exports` ([#2297], thanks [@ljharb]) - -### Changed -- [Docs] [`order`]: add type to the default groups ([#2272], thanks [@charpeni]) -- [readme] Add note to TypeScript docs to install appropriate resolver ([#2279], thanks [@johnthagen]) -- [Refactor] `importType`: combine redundant `isScoped` and `isScopedModule` (thanks [@ljharb]) -- [Docs] HTTP => HTTPS ([#2287], thanks [@Schweinepriester]) - -## [2.25.2] - 2021-10-12 - -### Fixed -- [Deps] update `eslint-module-utils` for real this time ([#2255], thanks [@ljharb]) - -## [2.25.1] - 2021-10-11 - -### Fixed -- [Deps] update `eslint-module-utils` - -## [2.25.0] - 2021-10-11 - -### Added -- Support `eslint` v8 ([#2191], thanks [@ota-meshi]) -- [`no-unresolved`]: add `caseSensitiveStrict` option ([#1262], thanks [@sergei-startsev]) -- [`no-unused-modules`]: add eslint v8 support ([#2194], thanks [@coderaiser]) -- [`no-restricted-paths`]: add/restore glob pattern support ([#2219], thanks [@stropho]) -- [`no-unused-modules`]: support dynamic imports ([#1660], [#2212], thanks [@maxkomarychev], [@aladdin-add], [@Hypnosphi]) - -### Fixed -- [`no-unresolved`]: ignore type-only imports ([#2220], thanks [@jablko]) -- [`order`]: fix sorting imports inside TypeScript module declarations ([#2226], thanks [@remcohaszing]) -- [`default`], `ExportMap`: Resolve extended TypeScript configuration files ([#2240], thanks [@mrmckeb]) - -### Changed -- [Refactor] switch to an internal replacement for `pkg-up` and `read-pkg-up` ([#2047], thanks [@mgwalker]) -- [patch] TypeScript config: remove `.d.ts` from [`import/parsers` setting] and [`import/extensions` setting] ([#2220], thanks [@jablko]) -- [Refactor] [`no-unresolved`], [`no-extraneous-dependencies`]: moduleVisitor usage ([#2233], thanks [@jablko]) - -## [2.24.2] - 2021-08-24 - -### Fixed -- [`named`], [`namespace`]: properly handle ExportAllDeclarations ([#2199], thanks [@ljharb]) - -## [2.24.1] - 2021-08-19 - -### Fixed -- `ExportMap`: Add default export when esModuleInterop is true and anything is exported ([#2184], thanks [@Maxim-Mazurok]) -- [`named`], [`namespace`]: properly set reexports on `export * as … from` ([#1998], [#2161], thanks [@ljharb]) -- [`no-duplicates`]: correctly handle case of mixed default/named type imports ([#2149], thanks [@GoodForOneFare], [@nwalters512]) -- [`no-duplicates`]: avoid crash with empty `import type {}` ([#2201], thanks [@ljharb]) - -### Changed -- [Docs] `max-dependencies`: 📖 Document `ignoreTypeImports` option ([#2196], thanks [@himynameisdave]) - -## [2.24.0] - 2021-08-08 - -### Added -- [`no-dynamic-require`]: add option `esmodule` ([#1223], thanks [@vikr01]) -- [`named`]: add `commonjs` option ([#1222], thanks [@vikr01]) -- [`no-namespace`]: Add `ignore` option ([#2112], thanks [@aberezkin]) -- [`max-dependencies`]: add option `ignoreTypeImports` ([#1847], thanks [@rfermann]) - -### Fixed -- [`no-duplicates`]: ensure autofix avoids excessive newlines ([#2028], thanks [@ertrzyiks]) -- [`extensions`]: avoid crashing on partially typed import/export statements ([#2118], thanks [@ljharb]) -- [`no-extraneous-dependencies`]: add ESM intermediate package.json support ([#2121], thanks [@paztis]) -- Use `context.getPhysicalFilename()` when available (ESLint 7.28+) ([#2160], thanks [@pmcelhaney]) -- [`extensions`]/`importType`: fix isScoped treating @/abc as scoped module ([#2146], thanks [@rperello]) - -### Changed -- [Docs] [`extensions`]: improved cases for using `@/...` ([#2140], thanks [@wenfangdu]) -- [Docs] [`extensions`]: removed incorrect cases ([#2138], thanks [@wenfangdu]) -- [Tests] [`order`]: add tests for `pathGroupsExcludedImportTypes: ['type']` ([#2158], thanks [@atav32]) -- [Docs] [`order`]: improve the documentation for the `pathGroupsExcludedImportTypes` option ([#2156], thanks [@liby]) -- [Tests] [`no-cycle`]: Restructure test files ([#1517], thanks [@soryy708]) -- [Docs] add description how to use plugin with yarn berry ([#2179], thanks [@KostyaZgara]) - -## [2.23.4] - 2021-05-29 - -### Fixed -- [`no-import-module-exports`]: Don't crash if packages have no entrypoint ([#2099], thanks [@eps1lon]) -- [`no-extraneous-dependencies`]: fix package name algorithm ([#2097], thanks [@paztis]) - -## [2.23.3] - 2021-05-21 - -### Fixed -- [`no-restricted-paths`]: fix false positive matches ([#2090], thanks [@malykhinvi]) -- [`no-cycle`]: ignore imports where imported file only imports types of importing file ([#2083], thanks [@cherryblossom000]) -- [`no-cycle`]: fix false negative when file imports a type after importing a value in Flow ([#2083], thanks [@cherryblossom000]) -- [`order`]: restore default behavior unless `type` is in groups ([#2087], thanks [@grit96]) - -### Changed -- [Docs] Add [`no-relative-packages`] to list of to the list of rules ([#2075], thanks [@arvigeus]) - -## [2.23.2] - 2021-05-15 - -### Changed -- [meta] add `safe-publish-latest`; use `prepublishOnly` script for npm 7+ - -## [2.23.1] - 2021-05-14 - -### Fixed -- [`newline-after-import`]: fix crash with `export {}` syntax ([#2063], [#2056], thanks [@ljharb]) -- `ExportMap`: do not crash when tsconfig lacks `.compilerOptions` ([#2067], thanks [@ljharb]) -- [`order`]: fix alphabetical sorting ([#2071], thanks [@grit96]) - -## [2.23.0] - 2021-05-13 - -### Added -- [`no-commonjs`]: Also detect require calls with expressionless template literals: ``` require(`x`) ``` ([#1958], thanks [@FloEdelmann]) -- [`no-internal-modules`]: Add `forbid` option ([#1846], thanks [@guillaumewuip]) -- add [`no-relative-packages`] ([#1860], [#966], thanks [@tapayne88] [@panrafal]) -- add [`no-import-module-exports`] rule: report import declarations with CommonJS exports ([#804], thanks [@kentcdodds] and [@ttmarek]) -- [`no-unused-modules`]: Support destructuring assignment for `export`. ([#1997], thanks [@s-h-a-d-o-w]) -- [`order`]: support type imports ([#2021], thanks [@grit96]) -- [`order`]: Add `warnOnUnassignedImports` option to enable warnings for out of order unassigned imports ([#1990], thanks [@hayes]) - -### Fixed -- [`export`]/TypeScript: properly detect export specifiers as children of a TS module block ([#1889], thanks [@andreubotella]) -- [`order`]: ignore non-module-level requires ([#1940], thanks [@golopot]) -- [`no-webpack-loader-syntax`]/TypeScript: avoid crash on missing name ([#1947], thanks [@leonardodino]) -- [`no-extraneous-dependencies`]: Add package.json cache ([#1948], thanks [@fa93hws]) -- [`prefer-default-export`]: handle empty array destructuring ([#1965], thanks [@ljharb]) -- [`no-unused-modules`]: make type imports mark a module as used (fixes #1924) ([#1974], thanks [@cherryblossom000]) -- [`no-cycle`]: fix perf regression ([#1944], thanks [@Blasz]) -- [`first`]: fix handling of `import = require` ([#1963], thanks [@MatthiasKunnen]) -- [`no-cycle`]/[`extensions`]: fix isExternalModule usage ([#1696], thanks [@paztis]) -- [`extensions`]/[`no-cycle`]/[`no-extraneous-dependencies`]: Correct module real path resolution ([#1696], thanks [@paztis]) -- [`no-named-default`]: ignore Flow import type and typeof ([#1983], thanks [@christianvuerings]) -- [`no-extraneous-dependencies`]: Exclude flow `typeof` imports ([#1534], thanks [@devongovett]) -- [`newline-after-import`]: respect decorator annotations ([#1985], thanks [@lilling]) -- [`no-restricted-paths`]: enhance performance for zones with `except` paths ([#2022], thanks [@malykhinvi]) -- [`no-unresolved`]: check import() ([#2026], thanks [@aladdin-add]) - -### Changed -- [Generic Import Callback] Make callback for all imports once in rules ([#1237], thanks [@ljqx]) -- [Docs] [`no-named-as-default`]: add semicolon ([#1897], thanks [@bicstone]) -- [Docs] [`no-extraneous-dependencies`]: correct peerDependencies option default to `true` ([#1993], thanks [@dwardu]) -- [Docs] [`order`]: Document options required to match ordering example ([#1992], thanks [@silviogutierrez]) -- [Tests] [`no-unresolved`]: add tests for `import()` ([#2012], thanks [@davidbonnet]) -- [Docs] Add import/recommended ruleset to README ([#2034], thanks [@edemaine]) - -## [2.22.1] - 2020-09-27 - -### Fixed -- [`default`]/TypeScript: avoid crash on `export =` with a MemberExpression ([#1841], thanks [@ljharb]) -- [`extensions`]/importType: Fix @/abc being treated as scoped module ([#1854], thanks [@3nuc]) -- allow using rest operator in named export ([#1878], thanks [@foray1010]) -- [`dynamic-import-chunkname`]: allow single quotes to match Webpack support ([#1848], thanks [@straub]) - -### Changed -- [`export`]: add tests for a name collision with `export * from` ([#1704], thanks @tomprats) - -## [2.22.0] - 2020-06-26 - -### Added -- [`no-unused-modules`]: consider exported TypeScript interfaces, types and enums ([#1819], thanks [@nicolashenry]) -- [`no-cycle`]: allow `maxDepth` option to be `"∞"` (thanks [@ljharb]) - -### Fixed -- [`order`]/TypeScript: properly support `import = object` expressions ([#1823], thanks [@manuth]) -- [`no-extraneous-dependencies`]/TypeScript: do not error when importing type from dev dependencies ([#1820], thanks [@fernandopasik]) -- [`default`]: avoid crash with `export =` ([#1822], thanks [@AndrewLeedham]) -- [`order`]/[`newline-after-import`]: ignore TypeScript's "export import object" ([#1830], thanks [@be5invis]) -- [`dynamic-import-chunkname`]/TypeScript: supports `@typescript-eslint/parser` ([#1833], thanks [@noelebrun]) -- [`order`]/TypeScript: ignore ordering of object imports ([#1831], thanks [@manuth]) -- [`namespace`]: do not report on shadowed import names ([#518], thanks [@ljharb]) -- [`export`]: avoid warning on `export * as` non-conflicts ([#1834], thanks [@ljharb]) - -### Changed -- [`no-extraneous-dependencies`]: add tests for importing types ([#1824], thanks [@taye]) -- [docs] [`no-default-export`]: Fix docs url ([#1836], thanks [@beatrizrezener]) -- [docs] [`imports-first`]: deprecation info and link to `first` docs ([#1835], thanks [@beatrizrezener]) - -## [2.21.2] - 2020-06-09 - -### Fixed -- [`order`]: avoid a crash on TypeScript’s `export import` syntax ([#1808], thanks [@ljharb]) -- [`newline-after-import`]: consider TypeScript `import =` syntax' ([#1811], thanks [@ljharb]) -- [`no-internal-modules`]: avoid a crash on a named export declaration ([#1814], thanks [@ljharb]) - -## [2.21.1] - 2020-06-07 - -### Fixed -- TypeScript: [`import/named`]: avoid requiring `typescript` when not using TS ([#1805], thanks [@ljharb]) - -## [2.21.0] - 2020-06-07 - -### Added -- [`import/default`]: support default export in TSExportAssignment ([#1528], thanks [@joaovieira]) -- [`no-cycle`]: add `ignoreExternal` option ([#1681], thanks [@sveyret]) -- [`order`]: Add support for TypeScript's "import equals"-expressions ([#1785], thanks [@manuth]) -- [`import/default`]: support default export in TSExportAssignment ([#1689], thanks [@Maxim-Mazurok]) -- [`no-restricted-paths`]: add custom message support ([#1802], thanks [@malykhinvi]) - -### Fixed -- [`group-exports`]: Flow type export awareness ([#1702], thanks [@ernestostifano]) -- [`order`]: Recognize pathGroup config for first group ([#1719], [#1724], thanks [@forivall], [@xpl]) -- [`no-unused-modules`]: Fix re-export not counting as usage when used in combination with import ([#1722], thanks [@Ephem]) -- [`no-duplicates`]: Handle TS import type ([#1676], thanks [@kmui2]) -- [`newline-after-import`]: recognize decorators ([#1139], thanks [@atos1990]) -- [`no-unused-modules`]: Revert "[flow] [`no-unused-modules`]: add flow type support" ([#1770], thanks [@Hypnosphi]) -- TypeScript: Add nested namespace handling ([#1763], thanks [@julien1619]) -- [`namespace`]/`ExportMap`: Fix interface declarations for TypeScript ([#1764], thanks [@julien1619]) -- [`no-unused-modules`]: avoid order-dependence ([#1744], thanks [@darkartur]) -- [`no-internal-modules`]: also check `export from` syntax ([#1691], thanks [@adjerbetian]) -- TypeScript: [`export`]: avoid a crash with `export =` ([#1801], thanks [@ljharb]) - -### Changed -- [Refactor] [`no-extraneous-dependencies`]: use moduleVisitor ([#1735], thanks [@adamborowski]) -- TypeScript config: Disable [`named`][] ([#1726], thanks [@astorije]) -- [readme] Remove duplicate [`no-unused-modules`] from docs ([#1690], thanks [@arvigeus]) -- [Docs] [`order`]: fix bad inline config ([#1788], thanks [@nickofthyme]) -- [Tests] Add fix for Windows Subsystem for Linux ([#1786], thanks [@manuth]) -- [Docs] [`no-unused-rules`]: Fix docs for unused exports ([#1776], thanks [@barbogast]) -- [eslint] bump minimum v7 version to v7.2.0 - -## [2.20.2] - 2020-03-28 - -### Fixed -- [`order`]: fix `isExternalModule` detect on windows ([#1651], thanks [@fisker]) -- [`order`]: recognize ".." as a "parent" path ([#1658], thanks [@golopot]) -- [`no-duplicates`]: fix fixer on cases with default import ([#1666], thanks [@golopot]) -- [`no-unused-modules`]: Handle `export { default } from` syntax ([#1631], thanks [@richardxia]) -- [`first`]: Add a way to disable `absolute-first` explicitly ([#1664], thanks [@TheCrueltySage]) -- [Docs] [`no-webpack-loader-syntax`]: Updates webpack URLs ([#1751], thanks [@MikeyBeLike]) - -## [2.20.1] - 2020-02-01 - -### Fixed -- [`export`]: Handle function overloading in `*.d.ts` ([#1619], thanks [@IvanGoncharov]) -- [`no-absolute-path`]: fix a crash with invalid import syntax ([#1616], thanks [@ljharb]) -- [`import/external-module-folders` setting] now correctly works with directories containing modules symlinked from `node_modules` ([#1605], thanks [@skozin]) -- [`extensions`]: for invalid code where `name` does not exist, do not crash ([#1613], thanks [@ljharb]) -- [`extensions`]: Fix scope regex ([#1611], thanks [@yordis]) -- [`no-duplicates`]: allow duplicate imports if one is a namespace and the other not ([#1612], thanks [@sveyret]) -- Add some missing rule meta schemas and types ([#1620], thanks [@bmish]) -- [`named`]: for importing from a module which re-exports named exports from a `node_modules` module ([#1569], [#1447], thanks [@redbugz], [@kentcdodds]) -- [`order`]: Fix alphabetize for mixed requires and imports ([#1626], thanks [@wschurman]) - -### Changed -- [`import/external-module-folders` setting] behavior is more strict now: it will only match complete path segments ([#1605], thanks [@skozin]) -- [meta] fix "files" field to include/exclude the proper files ([#1635], thanks [@ljharb]) -- [Tests] [`order`]: Add TS import type tests ([#1736], thanks [@kmui2]) - -## [2.20.0] - 2020-01-10 - -### Added -- [`order`]: added `caseInsensitive` as an additional option to `alphabetize` ([#1586], thanks [@dbrewer5]) -- [`no-restricted-paths`]: New `except` option per `zone`, allowing exceptions to be defined for a restricted zone ([#1238], thanks [@rsolomon]) -- [`order`]: add option pathGroupsExcludedImportTypes to allow ordering of external import types ([#1565], thanks [@Mairu]) - -### Fixed -- [`no-unused-modules`]: fix usage of [`import/extensions` setting] ([#1560], thanks [@stekycz]) -- [`extensions`]: ignore non-main modules ([#1563], thanks [@saschanaz]) -- TypeScript config: lookup for external modules in @types folder ([#1526], thanks [@joaovieira]) -- [`no-extraneous-dependencies`]: ensure `node.source` is truthy ([#1589], thanks [@ljharb]) -- [`extensions`]: Ignore query strings when checking for extensions ([#1572], thanks [@pcorpet]) - -### Docs -- [`extensions`]: improve `ignorePackages` docs ([#1248], thanks [@ivo-stefchev]) - -## [2.19.1] - 2019-12-08 - -### Fixed -- [`no-extraneous-dependencies`]: ensure `node.source` exists - -## [2.19.0] - 2019-12-08 - -### Added -- [`internal-regex` setting]: regex pattern for marking packages "internal" ([#1491], thanks [@Librazy]) -- [`group-exports`]: make aggregate module exports valid ([#1472], thanks [@atikenny]) -- [`no-namespace`]: Make rule fixable ([#1401], thanks [@TrevorBurnham]) -- support `parseForESLint` from custom parser ([#1435], thanks [@JounQin]) -- [`no-extraneous-dependencies`]: Implement support for [bundledDependencies](https://npm.github.io/using-pkgs-docs/package-json/types/bundleddependencies.html) ([#1436], thanks [@schmidsi])) -- [`no-unused-modules`]: add flow type support ([#1542], thanks [@rfermann]) -- [`order`]: Adds support for pathGroups to allow ordering by defined patterns ([#795], [#1386], thanks [@Mairu]) -- [`no-duplicates`]: Add `considerQueryString` option : allow duplicate imports with different query strings ([#1107], thanks [@pcorpet]). -- [`order`]: Add support for alphabetical sorting of import paths within import groups ([#1360], [#1105], [#629], thanks [@duncanbeevers], [@stropho], [@luczsoma], [@randallreedjr]) -- [`no-commonjs`]: add `allowConditionalRequire` option ([#1439], thanks [@Pessimistress]) - -### Fixed -- [`default`]: make error message less confusing ([#1470], thanks [@golopot]) -- Improve performance of `ExportMap.for` by only loading paths when necessary. ([#1519], thanks [@brendo]) -- Support export of a merged TypeScript namespace declaration ([#1495], thanks [@benmunro]) -- [`order`]: fix autofix to not move imports across fn calls ([#1253], thanks [@tihonove]) -- [`prefer-default-export`]: fix false positive with type export ([#1506], thanks [@golopot]) -- [`extensions`]: Fix `ignorePackages` to produce errors ([#1521], thanks [@saschanaz]) -- [`no-unused-modules`]: fix crash due to `export *` ([#1496], thanks [@Taranys]) -- [`no-cycle`]: should not warn for Flow imports ([#1494], thanks [@maxmalov]) -- [`order`]: fix `@someModule` considered as `unknown` instead of `internal` ([#1493], thanks [@aamulumi]) -- [`no-extraneous-dependencies`]: Check `export from` ([#1049], thanks [@marcusdarmstrong]) - -### Docs -- [`no-useless-path-segments`]: add docs for option `commonjs` ([#1507], thanks [@golopot]) - -### Changed -- [`no-unused-modules`]/`eslint-module-utils`: Avoid superfluous calls and code ([#1551], thanks [@brettz9]) - -## [2.18.2] - 2019-07-19 - -### Fixed -- Skip warning on type interfaces ([#1425], thanks [@lencioni]) - -## [2.18.1] - 2019-07-18 - -### Fixed -- Improve parse perf when using `@typescript-eslint/parser` ([#1409], thanks [@bradzacher]) -- [`prefer-default-export`]: don't warn on TypeAlias & TSTypeAliasDeclaration ([#1377], thanks [@sharmilajesupaul]) -- [`no-unused-modules`]: Exclude package "main"/"bin"/"browser" entry points ([#1404], thanks [@rfermann]) -- [`export`]: false positive for TypeScript overloads ([#1412], thanks [@golopot]) - -### Refactors -- [`no-extraneous-dependencies`], `importType`: remove lodash ([#1419], thanks [@ljharb]) - -## [2.18.0] - 2019-06-24 - -### Added -- Support eslint v6 ([#1393], thanks [@sheepsteak]) -- [`order`]: Adds support for correctly sorting unknown types into a single group ([#1375], thanks [@swernerx]) -- [`order`]: add fixer for destructuring commonjs import ([#1372], thanks [@golopot]) -- TypeScript config: add TS def extensions + defer to TS over JS ([#1366], thanks [@benmosher]) - -### Fixed -- [`no-unused-modules`]: handle ClassDeclaration ([#1371], thanks [@golopot]) - -### Docs -- [`no-cycle`]: split code examples so file separation is obvious ([#1370], thanks [@alex-page]) -- [`no-named-as-default-member`]: update broken link ([#1389], thanks [@fooloomanzoo]) - -## [2.17.3] - 2019-05-23 - -### Fixed -- [`no-common-js`]: Also throw an error when assigning ([#1354], thanks [@charlessuh]) -- [`no-unused-modules`]: don't crash when lint file outside src-folder ([#1347], thanks [@rfermann]) -- [`no-unused-modules`]: make `import { name as otherName }` work ([#1340], [#1342], thanks [@rfermann]) -- [`no-unused-modules`]: make appveyor tests passing ([#1333], thanks [@rfermann]) -- [`named`]: ignore Flow `typeof` imports and `type` exports ([#1345], thanks [@loganfsmyth]) -- [refactor] fix eslint 6 compat by fixing imports (thank [@ljharb]) -- Improve support for TypeScript declare structures ([#1356], thanks [@christophercurrie]) - -### Docs -- add missing [`no-unused-modules`] in README ([#1358], thanks [@golopot]) -- [`no-unused-modules`]: Indicates usage, plugin defaults to no-op, and add description to main README.md ([#1352], thanks [@johndevedu]) -- Document `env` option for `eslint-import-resolver-webpack` ([#1363], thanks [@kgregory]) - -## [2.17.2] - 2019-04-16 - -### Fixed -- [`no-unused-modules`]: avoid crash when using `ignoreExports`-option ([#1331], [#1323], thanks [@rfermann]) -- [`no-unused-modules`]: make sure that rule with no options will not fail ([#1330], [#1334], thanks [@kiwka]) - -## [2.17.1] - 2019-04-13 - -### Fixed -- require v2.4 of `eslint-module-utils` ([#1322]) - -## [2.17.0] - 2019-04-13 - -### Added -- [`no-useless-path-segments`]: Add `noUselessIndex` option ([#1290], thanks [@timkraut]) -- [`no-duplicates`]: Add autofix ([#1312], thanks [@lydell]) -- Add [`no-unused-modules`] rule ([#1142], thanks [@rfermann]) -- support export type named exports from TypeScript ([#1304], thanks [@bradennapier] and [@schmod]) - -### Fixed -- [`order`]: Fix interpreting some external modules being interpreted as internal modules ([#793], [#794] thanks [@ephys]) -- allow aliases that start with @ to be "internal" ([#1293], [#1294], thanks [@jeffshaver]) -- aliased internal modules that look like core modules ([#1297], thanks [@echenley]) -- [`namespace`]: add check for null ExportMap ([#1235], [#1144], thanks [@ljqx]) -- [ExportMap] fix condition for checking if block comment ([#1234], [#1233], thanks [@ljqx]) -- Fix overwriting of dynamic import() CallExpression ([`no-cycle`], [`no-relative-parent-imports`], [`no-unresolved`], [`no-useless-path-segments`]) ([#1218], [#1166], [#1035], thanks [@vikr01]) -- [`export`]: false positives for TypeScript type + value export ([#1319], thanks [@bradzacher]) -- [`export`]: Support TypeScript namespaces ([#1320], [#1300], thanks [@bradzacher]) - -### Docs -- Update readme for TypeScript ([#1256], [#1277], thanks [@kirill-konshin]) -- make rule names consistent ([#1112], thanks [@feychenie]) - -### Tests -- fix broken tests on master ([#1295], thanks [@jeffshaver] and [@ljharb]) -- [`no-commonjs`]: add tests that show corner cases ([#1308], thanks [@TakeScoop]) - -## [2.16.0] - 2019-01-29 - -### Added -- `typescript` config ([#1257], thanks [@kirill-konshin]) - -### Fixed -- Memory leak of `SourceCode` objects for all parsed dependencies, resolved. (issue [#1266], thanks [@asapach] and [@sergei-startsev] for digging in) - -## [2.15.0] - 2019-01-22 - -### Added -- new rule: [`no-named-export`] ([#1157], thanks [@fsmaia]) - -### Fixed -- [`no-extraneous-dependencies`]: `packageDir` option with array value was clobbering package deps instead of merging them ([#1175]/[#1176], thanks [@aravindet] & [@pzhine]) -- [`dynamic-import-chunkname`]: Add proper webpack comment parsing ([#1163], thanks [@st-sloth]) -- [`named`]: fix destructuring assignment ([#1232], thanks [@ljqx]) - -## [2.14.0] - 2018-08-13 - -### Added -- [`no-useless-path-segments`]: add commonJS (CJS) support ([#1128], thanks [@1pete]) -- [`namespace`]: add JSX check ([#1151], thanks [@jf248]) - -### Fixed -- [`no-cycle`]: ignore Flow imports ([#1126], thanks [@gajus]) -- fix Flow type imports ([#1106], thanks [@syymza]) -- [`no-relative-parent-imports`]: resolve paths ([#1135], thanks [@chrislloyd]) -- [`order`]: fix autofixer when using typescript-eslint-parser ([#1137], thanks [@justinanastos]) -- repeat fix from [#797] for [#717], in another place (thanks [@ljharb]) - -### Refactors -- add explicit support for RestElement alongside ExperimentalRestProperty (thanks [@ljharb]) - -## [2.13.0] - 2018-06-24 - -### Added -- Add ESLint 5 support ([#1122], thanks [@ai] and [@ljharb]) -- Add [`no-relative-parent-imports`] rule: disallow relative imports from parent directories ([#1093], thanks [@chrislloyd]) - -### Fixed -- `namespace` rule: ensure it works in eslint 5/ecmaVersion 2018 (thanks [@ljharb]) - -## [2.12.0] - 2018-05-17 - -### Added -- Ignore type imports for [`named`] rule ([#931], thanks [@mattijsbliek]) -- Add documentation for [`no-useless-path-segments`] rule ([#1068], thanks [@manovotny]) -- `packageDir` option for [`no-extraneous-dependencies`] can be array-valued ([#1085], thanks [@hulkish]) - -## [2.11.0] - 2018-04-09 - -### Added -- Fixer for [`first`] ([#1046], thanks [@fengkfengk]) -- `allow-require` option for [`no-commonjs`] rule ([#880], thanks [@futpib]) - -### Fixed -- memory/CPU regression where ASTs were held in memory ([#1058], thanks [@klimashkin]/[@lukeapage]) - -## [2.10.0] - 2018-03-29 - -### Added -- Autofixer for [`order`] rule ([#908], thanks [@tihonove]) -- Add [`no-cycle`] rule: reports import cycles. - -## [2.9.0] - 2018-02-21 - -### Added -- Add [`group-exports`] rule: style-guide rule to report use of multiple named exports ([#721], thanks [@robertrossmann]) -- Add [`no-self-import`] rule: forbids a module from importing itself. ([#727], [#449], [#447], thanks [@giodamelio]). -- Add [`no-default-export`] rule ([#889], thanks [@isiahmeadows]) -- Add [`no-useless-path-segments`] rule ([#912], thanks [@graingert] and [@danny-andrews]) -- ... and more! check the commits for v[2.9.0] - -## [2.8.0] - 2017-10-18 -### Added -- [`exports-last`] rule ([#620] + [#632], thanks [@k15a]) - -### Changed -- Case-sensitivity checking ignores working directory and ancestors. ([#720] + [#858], thanks [@laysent]) - -### Fixed -- support scoped modules containing hyphens ([#744], thanks [@rosswarren]) -- core-modules now resolves files inside declared modules ([#886] / [#891], thanks [@mplewis]) -- TypeError for missing AST fields from TypeScript ([#842] / [#944], thanks [@alexgorbatchev]) - -## [2.7.0] - 2017-07-06 - -### Changed -- [`no-absolute-path`] picks up speed boost, optional AMD support ([#843], thanks [@jseminck]) - -## [2.6.1] - 2017-06-29 - -### Fixed -- update bundled node resolver dependency to latest version - -## [2.6.0] - 2017-06-23 - -### Changed -- update tests / peerDeps for ESLint 4.0 compatibility ([#871], thanks [@mastilver]) -- [`memo-parser`] updated to require `filePath` on parser options as it melts - down if it's not there, now that this plugin always provides it. (see [#863]) - -## [2.5.0] - 2017-06-22 - -Re-releasing v[2.4.0] after discovering that the memory leak is isolated to the [`memo-parser`], -which is more or less experimental anyway. - -### Added -- Autofixer for newline-after-import. ([#686] + [#696], thanks [@eelyafi]) - -## [2.4.0] - 2017-06-02 [YANKED] - -Yanked due to critical issue in eslint-module-utils with cache key resulting from [#839]. - -### Added -- Add `filePath` into `parserOptions` passed to `parser` ([#839], thanks [@sompylasar]) -- Add `allow` option to [`no-unassigned-import`] to allow for files that match the globs ([#671], [#737], thanks [@kevin940726]). - -## [2.3.0] - 2017-05-18 - -### Added -- [`no-anonymous-default-export`] rule: report anonymous default exports ([#712], thanks [@duncanbeevers]). -- Add new value to [`order`]'s `newlines-between` option to allow newlines inside import groups ([#627], [#628], thanks [@giodamelio]) -- Add `count` option to the [`newline-after-import`] rule to allow configuration of number of newlines expected ([#742], thanks [@ntdb]) - -### Changed -- [`no-extraneous-dependencies`]: use `read-pkg-up` to simplify finding + loading `package.json` ([#680], thanks [@wtgtybhertgeghgtwtg]) -- Add support to specify the package.json [`no-extraneous-dependencies`] ([#685], thanks [@ramasilveyra]) - -### Fixed -- attempt to fix crash in [`no-mutable-exports`]. ([#660]) -- "default is a reserved keyword" in no-maned-default tests by locking down babylon to 6.15.0 (#756, thanks @gmathieu) -- support scoped modules containing non word characters - -## [2.2.0] - 2016-11-07 - -### Fixed -- Corrected a few gaffs in the auto-ignore logic to fix major performance issues - with projects that did not explicitly ignore `node_modules`. ([#654]) -- [`import/ignore` setting] was only being respected if the ignored module didn't start with - an `import` or `export` JS statement -- [`prefer-default-export`]: fixed crash on export extensions ([#653]) - -## [2.1.0] - 2016-11-02 - -### Added -- Add [`no-named-default`] rule: style-guide rule to report use of unnecessarily named default imports ([#596], thanks [@ntdb]) -- [`no-extraneous-dependencies`]: check globs against CWD + absolute path ([#602] + [#630], thanks [@ljharb]) - -### Fixed -- [`prefer-default-export`] handles flow `export type` ([#484] + [#639], thanks [@jakubsta]) -- [`prefer-default-export`] handles re-exported default exports ([#609]) -- Fix crash when using [`newline-after-import`] with decorators ([#592]) -- Properly report [`newline-after-import`] when next line is a decorator -- Fixed documentation for the default values for the [`order`] rule ([#601]) - -## [2.0.1] - 2016-10-06 - -### Fixed -- Fixed code that relied on removed dependencies. ([#604]) - -## [2.0.0]! - 2016-09-30 - -### Added -- [`unambiguous`] rule: report modules that are not unambiguously ES modules. -- `recommended` shared config. Roughly `errors` and `warnings` mixed together, - with some `parserOptions` in the mix. ([#402]) -- `react` shared config: added `jsx: true` to `parserOptions.ecmaFeatures`. -- Added [`no-webpack-loader-syntax`] rule: forbid custom Webpack loader syntax in imports. ([#586], thanks [@fson]!) -- Add option `newlines-between: "ignore"` to [`order`] ([#519]) -- Added [`no-unassigned-import`] rule ([#529]) - -### Breaking -- [`import/extensions` setting] defaults to `['.js']`. ([#306]) -- [`import/ignore` setting] defaults to nothing, and ambiguous modules are ignored natively. This means importing from CommonJS modules will no longer be reported by [`default`], [`named`], or [`namespace`], regardless of `import/ignore`. ([#270]) -- [`newline-after-import`]: Removed need for an empty line after an inline `require` call ([#570]) -- [`order`]: Default value for `newlines-between` option is now `ignore` ([#519]) - -### Changed -- `imports-first` is renamed to [`first`]. `imports-first` alias will continue to - exist, but may be removed in a future major release. -- Case-sensitivity: now specifically (and optionally) reported by [`no-unresolved`]. - Other rules will ignore case-mismatches on paths on case-insensitive filesystems. ([#311]) - -### Fixed -- [`no-internal-modules`]: support `@`-scoped packages ([#577]+[#578], thanks [@spalger]) - -## [1.16.0] - 2016-09-22 - -### Added -- Added [`no-dynamic-require`] rule: forbid `require()` calls with expressions. ([#567], [#568]) -- Added [`no-internal-modules`] rule: restrict deep package imports to specific folders. ([#485], thanks [@spalger]!) -- [`extensions`]: allow override of a chosen default with options object ([#555], thanks [@ljharb]!) - -### Fixed -- [`no-named-as-default`] no longer false-positives on `export default from '...'` ([#566], thanks [@preco21]) -- [`default`]: allow re-export of values from ignored files as default ([#545], thanks [@skyrpex]) - -## [1.15.0] - 2016-09-12 - -### Added -- Added an `allow` option to [`no-nodejs-modules`] to allow exceptions ([#452], [#509]). -- Added [`no-absolute-path`] rule ([#530], [#538]) -- [`max-dependencies`] for specifying the maximum number of dependencies (both `import` and `require`) a module can have. (see [#489], thanks [@tizmagik]) -- Added glob option to config for [`no-extraneous-dependencies`], after much bikeshedding. Thanks, [@knpwrs]! ([#527]) - -### Fixed -- [`no-named-as-default-member`] Allow default import to have a property named "default" ([#507], [#508], thanks [@jquense] for both!) - -## [1.14.0] - 2016-08-22 - -### Added -- [`import/parsers` setting]: parse some dependencies (i.e. TypeScript!) with a different parser than the ESLint-configured parser. ([#503]) - -### Fixed -- [`namespace`] exception for get property from `namespace` import, which are re-export from commonjs module ([#499] fixes [#416], thanks [@wKich]) - -## [1.13.0] - 2016-08-11 - -### Added -- `allowComputed` option for [`namespace`] rule. If set to `true`, won't report - computed member references to namespaces. (see [#456]) - -### Changed -- Modified [`no-nodejs-modules`] error message to include the module's name ([#453], [#461]) - -### Fixed -- [`import/extensions` setting] is respected in spite of the appearance of imports - in an imported file. (fixes [#478], thanks [@rhys-vdw]) - -## [1.12.0] - 2016-07-26 - -### Added -- [`import/external-module-folders` setting]: a possibility to configure folders for "external" modules ([#444], thanks [@zloirock]) - -## [1.11.1] - 2016-07-20 - -### Fixed -- [`newline-after-import`] exception for `switch` branches with `require`s iff parsed as `sourceType:'module'`. - (still [#441], thanks again [@ljharb]) - -## [1.11.0] - 2016-07-17 - -### Added -- Added an `peerDependencies` option to [`no-extraneous-dependencies`] to allow/forbid peer dependencies ([#423], [#428], thanks [@jfmengels]!). - -### Fixed -- [`newline-after-import`] exception for multiple `require`s in an arrow - function expression (e.g. `() => require('a') || require('b')`). ([#441], thanks [@ljharb]) - -## [1.10.3] - 2016-07-08 - -### Fixed -- removing `Symbol` dependencies (i.e. `for-of` loops) due to Node 0.10 polyfill - issue (see [#415]). Should not make any discernible semantic difference. - -## [1.10.2] - 2016-07-04 - -### Fixed -- Something horrible happened during `npm prepublish` of 1.10.1. - Several `rm -rf node_modules && npm i` and `gulp clean && npm prepublish`s later, it is rebuilt and republished as 1.10.2. Thanks [@rhettlivingston] for noticing and reporting! - -## [1.10.1] - 2016-07-02 [YANKED] - -### Added -- Officially support ESLint 3.x. (peerDependencies updated to `2.x - 3.x`) - -## [1.10.0] - 2016-06-30 - -### Added -- Added new rule [`no-restricted-paths`]. ([#155]/[#371], thanks [@lo1tuma]) -- [`import/core-modules` setting]: allow configuration of additional module names, - to be treated as builtin modules (a la `path`, etc. in Node). ([#275] + [#365], thanks [@sindresorhus] for driving) -- React Native shared config (based on comment from [#283]) - -### Fixed -- Fixed crash with `newline-after-import` related to the use of switch cases. (fixes [#386], thanks [@ljharb] for reporting) ([#395]) - -## [1.9.2] - 2016-06-21 - -### Fixed -- Issues with ignored/CJS files in [`export`] and [`no-deprecated`] rules. ([#348], [#370]) - -## [1.9.1] - 2016-06-16 - -### Fixed -- Reordered precedence for loading resolvers. ([#373]) - -## [1.9.0] - 2016-06-10 - -### Added -- Added support TomDoc comments to [`no-deprecated`]. ([#321], thanks [@josh]) -- Added support for loading custom resolvers ([#314], thanks [@le0nik]) - -### Fixed -- [`prefer-default-export`] handles `export function` and `export const` in same file ([#359], thanks [@scottnonnenberg]) - -## [1.8.1] - 2016-05-23 - -### Fixed -- `export * from 'foo'` now properly ignores a `default` export from `foo`, if any. ([#328]/[#332], thanks [@jkimbo]) - This impacts all static analysis of imported names. ([`default`], [`named`], [`namespace`], [`export`]) -- Make [`order`]'s `newline-between` option handle multiline import statements ([#313], thanks [@singles]) -- Make [`order`]'s `newline-between` option handle not assigned import statements ([#313], thanks [@singles]) -- Make [`order`]'s `newline-between` option ignore `require` statements inside object literals ([#313], thanks [@singles]) -- [`prefer-default-export`] properly handles deep destructuring, `export * from ...`, and files with no exports. ([#342]+[#343], thanks [@scottnonnenberg]) - -## [1.8.0] - 2016-05-11 - -### Added -- [`prefer-default-export`], new rule. ([#308], thanks [@gavriguy]) - -### Fixed -- Ignore namespace / ES7 re-exports in [`no-mutable-exports`]. ([#317], fixed by [#322]. thanks [@borisyankov] + [@jfmengels]) -- Make [`no-extraneous-dependencies`] handle scoped packages ([#316], thanks [@jfmengels]) - -## [1.7.0] - 2016-05-06 - -### Added -- [`newline-after-import`], new rule. ([#245], thanks [@singles]) -- Added an `optionalDependencies` option to [`no-extraneous-dependencies`] to allow/forbid optional dependencies ([#266], thanks [@jfmengels]). -- Added `newlines-between` option to [`order`] rule ([#298], thanks [@singles]) -- add [`no-mutable-exports`] rule ([#290], thanks [@josh]) -- [`import/extensions` setting]: a list of file extensions to parse as modules - and search for `export`s. If unspecified, all extensions are considered valid (for now). - In v2, this will likely default to `['.js', MODULE_EXT]`. ([#297], to fix [#267]) - -### Fixed -- [`extensions`]: fallback to source path for extension enforcement if imported - module is not resolved. Also, never report for builtins (i.e. `path`). ([#296]) - -## [1.6.1] - 2016-04-28 - -### Fixed -- [`no-named-as-default-member`]: don't crash on rest props. ([#281], thanks [@SimenB]) -- support for Node 6: don't pass `null` to `path` functions. - Thanks to [@strawbrary] for bringing this up ([#272]) and adding OSX support to the Travis - config ([#288]). - -## [1.6.0] - 2016-04-25 - -### Added -- add [`no-named-as-default-member`] to `warnings` canned config -- add [`no-extraneous-dependencies`] rule ([#241], thanks [@jfmengels]) -- add [`extensions`] rule ([#250], thanks [@lo1tuma]) -- add [`no-nodejs-modules`] rule ([#261], thanks [@jfmengels]) -- add [`order`] rule ([#247], thanks [@jfmengels]) -- consider `resolve.fallback` config option in the webpack resolver ([#254]) - -### Changed -- [`imports-first`] now allows directives (i.e. `'use strict'`) strictly before - any imports ([#256], thanks [@lemonmade]) - -### Fixed -- [`named`] now properly ignores the source module if a name is re-exported from - an ignored file (i.e. `node_modules`). Also improved the reported error. (thanks to [@jimbolla] for reporting) -- [`no-named-as-default-member`] had a crash on destructuring in loops (thanks for heads up from [@lemonmade]) - -## [1.5.0] - 2016-04-18 - -### Added -- report resolver errors at the top of the linted file -- add [`no-namespace`] rule ([#239], thanks [@singles]) -- add [`no-named-as-default-member`] rule ([#243], thanks [@dmnd]) - -### Changed -- Rearranged rule groups in README in preparation for more style guide rules - -### Removed -- support for Node 0.10, via `es6-*` ponyfills. Using native Map/Set/Symbol. - -## [1.4.0] - 2016-03-25 - -### Added -- Resolver plugin interface v2: more explicit response format that more clearly covers the found-but-core-module case, where there is no path. - Still backwards-compatible with the original version of the resolver spec. -- [Resolver documentation](./resolvers/README.md) - -### Changed -- using `package.json/files` instead of `.npmignore` for package file inclusion ([#228], thanks [@mathieudutour]) -- using `es6-*` ponyfills instead of `babel-runtime` - -## [1.3.0] - 2016-03-20 - -Major perf improvements. Between parsing only once and ignoring gigantic, non-module `node_modules`, -there is very little added time. - -My test project takes 17s to lint completely, down from 55s, when using the -memoizing parser, and takes only 27s with naked `babel-eslint` (thus, reparsing local modules). - -### Added -- This change log ([#216]) -- Experimental memoizing [parser](./memo-parser/README.md) - -### Fixed -- Huge reduction in execution time by _only_ ignoring [`import/ignore` setting] if - something that looks like an `export` is detected in the module content. - -## [1.2.0] - 2016-03-19 - -Thanks [@lencioni] for identifying a huge amount of rework in resolve and kicking -off a bunch of memoization. - -I'm seeing 62% improvement over my normal test codebase when executing only -[`no-unresolved`] in isolation, and ~35% total reduction in lint time. - -### Changed -- added caching to core/resolve via [#214], configured via [`import/cache` setting] - -## [1.1.0] - 2016-03-15 - -### Added -- Added an [`ignore`](./docs/rules/no-unresolved.md#ignore) option to [`no-unresolved`] for those pesky files that no resolver can find. (still prefer enhancing the Webpack and Node resolvers to using it, though). See [#89] for details. - -## [1.0.4] - 2016-03-11 - -### Changed -- respect hoisting for deep namespaces ([`namespace`]/[`no-deprecated`]) ([#211]) - -### Fixed -- don't crash on self references ([#210]) -- correct cache behavior in `eslint_d` for deep namespaces ([#200]) - -## [1.0.3] - 2016-02-26 - -### Changed -- no-deprecated follows deep namespaces ([#191]) - -### Fixed -- [`namespace`] no longer flags modules with only a default export as having no names. (ns.default is valid ES6) - -## [1.0.2] - 2016-02-26 - -### Fixed -- don't parse imports with no specifiers ([#192]) - -## [1.0.1] - 2016-02-25 - -### Fixed -- export `stage-0` shared config -- documented [`no-deprecated`] -- deep namespaces are traversed regardless of how they get imported ([#189]) - -## [1.0.0] - 2016-02-24 - -### Added -- [`no-deprecated`]: WIP rule to let you know at lint time if you're using deprecated functions, constants, classes, or modules. - -### Changed -- [`namespace`]: support deep namespaces ([#119] via [#157]) - -## [1.0.0-beta.0] - 2016-02-13 - -### Changed -- support for (only) ESLint 2.x -- no longer needs/refers to `import/parser` or `import/parse-options`. Instead, ESLint provides the configured parser + options to the rules, and they use that to parse dependencies. - -### Removed - -- `babylon` as default import parser (see Breaking) - -## [0.13.0] - 2016-02-08 - -### Added -- [`no-commonjs`] rule -- [`no-amd`] rule - -### Removed -- Removed vestigial `no-require` rule. [`no-commonjs`] is more complete. - -## [0.12.2] - 2016-02-06 [YANKED] - -Unpublished from npm and re-released as 0.13.0. See [#170]. - -## [0.12.1] - 2015-12-17 - -### Changed -- Broke docs for rules out into individual files. - -## [0.12.0] - 2015-12-14 - -### Changed -- Ignore [`import/ignore` setting] if exports are actually found in the parsed module. Does this to support use of `jsnext:main` in `node_modules` without the pain of managing an allow list or a nuanced deny list. - -## [0.11.0] - 2015-11-27 - -### Added -- Resolver plugins. Now the linter can read Webpack config, properly follow aliases and ignore externals, dismisses inline loaders, etc. etc.! - -## Earlier releases (0.10.1 and younger) -See [GitHub release notes](https://github.com/import-js/eslint-plugin-import/releases?after=v0.11.0) -for info on changes for earlier releases. - - -[`import/cache` setting]: ./README.md#importcache -[`import/ignore` setting]: ./README.md#importignore -[`import/extensions` setting]: ./README.md#importextensions -[`import/parsers` setting]: ./README.md#importparsers -[`import/core-modules` setting]: ./README.md#importcore-modules -[`import/external-module-folders` setting]: ./README.md#importexternal-module-folders -[`internal-regex` setting]: ./README.md#importinternal-regex - -[`consistent-type-specifier-style`]: ./docs/rules/consistent-type-specifier-style.md -[`default`]: ./docs/rules/default.md -[`dynamic-import-chunkname`]: ./docs/rules/dynamic-import-chunkname.md -[`export`]: ./docs/rules/export.md -[`exports-last`]: ./docs/rules/exports-last.md -[`extensions`]: ./docs/rules/extensions.md -[`first`]: ./docs/rules/first.md -[`group-exports`]: ./docs/rules/group-exports.md -[`imports-first`]: ./docs/rules/first.md -[`max-dependencies`]: ./docs/rules/max-dependencies.md -[`named`]: ./docs/rules/named.md -[`namespace`]: ./docs/rules/namespace.md -[`newline-after-import`]: ./docs/rules/newline-after-import.md -[`no-absolute-path`]: ./docs/rules/no-absolute-path.md -[`no-amd`]: ./docs/rules/no-amd.md -[`no-anonymous-default-export`]: ./docs/rules/no-anonymous-default-export.md -[`no-commonjs`]: ./docs/rules/no-commonjs.md -[`no-cycle`]: ./docs/rules/no-cycle.md -[`no-default-export`]: ./docs/rules/no-default-export.md -[`no-deprecated`]: ./docs/rules/no-deprecated.md -[`no-duplicates`]: ./docs/rules/no-duplicates.md -[`no-dynamic-require`]: ./docs/rules/no-dynamic-require.md -[`no-empty-named-blocks`]: ./docs/rules/no-empty-named-blocks.md -[`no-extraneous-dependencies`]: ./docs/rules/no-extraneous-dependencies.md -[`no-import-module-exports`]: ./docs/rules/no-import-module-exports.md -[`no-internal-modules`]: ./docs/rules/no-internal-modules.md -[`no-mutable-exports`]: ./docs/rules/no-mutable-exports.md -[`no-named-as-default-member`]: ./docs/rules/no-named-as-default-member.md -[`no-named-as-default`]: ./docs/rules/no-named-as-default.md -[`no-named-default`]: ./docs/rules/no-named-default.md -[`no-named-export`]: ./docs/rules/no-named-export.md -[`no-namespace`]: ./docs/rules/no-namespace.md -[`no-nodejs-modules`]: ./docs/rules/no-nodejs-modules.md -[`no-relative-packages`]: ./docs/rules/no-relative-packages.md -[`no-relative-parent-imports`]: ./docs/rules/no-relative-parent-imports.md -[`no-restricted-paths`]: ./docs/rules/no-restricted-paths.md -[`no-self-import`]: ./docs/rules/no-self-import.md -[`no-unassigned-import`]: ./docs/rules/no-unassigned-import.md -[`no-unresolved`]: ./docs/rules/no-unresolved.md -[`no-unused-modules`]: ./docs/rules/no-unused-modules.md -[`no-useless-path-segments`]: ./docs/rules/no-useless-path-segments.md -[`no-webpack-loader-syntax`]: ./docs/rules/no-webpack-loader-syntax.md -[`order`]: ./docs/rules/order.md -[`prefer-default-export`]: ./docs/rules/prefer-default-export.md -[`unambiguous`]: ./docs/rules/unambiguous.md - -[`memo-parser`]: ./memo-parser/README.md - -[#2944]: https://github.com/import-js/eslint-plugin-import/pull/2944 -[#2942]: https://github.com/import-js/eslint-plugin-import/pull/2942 -[#2919]: https://github.com/import-js/eslint-plugin-import/pull/2919 -[#2884]: https://github.com/import-js/eslint-plugin-import/pull/2884 -[#2866]: https://github.com/import-js/eslint-plugin-import/pull/2866 -[#2854]: https://github.com/import-js/eslint-plugin-import/pull/2854 -[#2851]: https://github.com/import-js/eslint-plugin-import/pull/2851 -[#2850]: https://github.com/import-js/eslint-plugin-import/pull/2850 -[#2842]: https://github.com/import-js/eslint-plugin-import/pull/2842 -[#2835]: https://github.com/import-js/eslint-plugin-import/pull/2835 -[#2832]: https://github.com/import-js/eslint-plugin-import/pull/2832 -[#2778]: https://github.com/import-js/eslint-plugin-import/pull/2778 -[#2756]: https://github.com/import-js/eslint-plugin-import/pull/2756 -[#2754]: https://github.com/import-js/eslint-plugin-import/pull/2754 -[#2748]: https://github.com/import-js/eslint-plugin-import/pull/2748 -[#2735]: https://github.com/import-js/eslint-plugin-import/pull/2735 -[#2699]: https://github.com/import-js/eslint-plugin-import/pull/2699 -[#2664]: https://github.com/import-js/eslint-plugin-import/pull/2664 -[#2613]: https://github.com/import-js/eslint-plugin-import/pull/2613 -[#2608]: https://github.com/import-js/eslint-plugin-import/pull/2608 -[#2605]: https://github.com/import-js/eslint-plugin-import/pull/2605 -[#2602]: https://github.com/import-js/eslint-plugin-import/pull/2602 -[#2598]: https://github.com/import-js/eslint-plugin-import/pull/2598 -[#2589]: https://github.com/import-js/eslint-plugin-import/pull/2589 -[#2588]: https://github.com/import-js/eslint-plugin-import/pull/2588 -[#2582]: https://github.com/import-js/eslint-plugin-import/pull/2582 -[#2570]: https://github.com/import-js/eslint-plugin-import/pull/2570 -[#2568]: https://github.com/import-js/eslint-plugin-import/pull/2568 -[#2546]: https://github.com/import-js/eslint-plugin-import/pull/2546 -[#2541]: https://github.com/import-js/eslint-plugin-import/pull/2541 -[#2531]: https://github.com/import-js/eslint-plugin-import/pull/2531 -[#2511]: https://github.com/import-js/eslint-plugin-import/pull/2511 -[#2506]: https://github.com/import-js/eslint-plugin-import/pull/2506 -[#2503]: https://github.com/import-js/eslint-plugin-import/pull/2503 -[#2490]: https://github.com/import-js/eslint-plugin-import/pull/2490 -[#2475]: https://github.com/import-js/eslint-plugin-import/pull/2475 -[#2473]: https://github.com/import-js/eslint-plugin-import/pull/2473 -[#2466]: https://github.com/import-js/eslint-plugin-import/pull/2466 -[#2459]: https://github.com/import-js/eslint-plugin-import/pull/2459 -[#2440]: https://github.com/import-js/eslint-plugin-import/pull/2440 -[#2438]: https://github.com/import-js/eslint-plugin-import/pull/2438 -[#2436]: https://github.com/import-js/eslint-plugin-import/pull/2436 -[#2427]: https://github.com/import-js/eslint-plugin-import/pull/2427 -[#2424]: https://github.com/import-js/eslint-plugin-import/pull/2424 -[#2419]: https://github.com/import-js/eslint-plugin-import/pull/2419 -[#2417]: https://github.com/import-js/eslint-plugin-import/pull/2417 -[#2411]: https://github.com/import-js/eslint-plugin-import/pull/2411 -[#2399]: https://github.com/import-js/eslint-plugin-import/pull/2399 -[#2396]: https://github.com/import-js/eslint-plugin-import/pull/2396 -[#2395]: https://github.com/import-js/eslint-plugin-import/pull/2395 -[#2393]: https://github.com/import-js/eslint-plugin-import/pull/2393 -[#2388]: https://github.com/import-js/eslint-plugin-import/pull/2388 -[#2387]: https://github.com/import-js/eslint-plugin-import/pull/2387 -[#2381]: https://github.com/import-js/eslint-plugin-import/pull/2381 -[#2378]: https://github.com/import-js/eslint-plugin-import/pull/2378 -[#2374]: https://github.com/import-js/eslint-plugin-import/pull/2374 -[#2371]: https://github.com/import-js/eslint-plugin-import/pull/2371 -[#2367]: https://github.com/import-js/eslint-plugin-import/pull/2367 -[#2358]: https://github.com/import-js/eslint-plugin-import/pull/2358 -[#2341]: https://github.com/import-js/eslint-plugin-import/pull/2341 -[#2332]: https://github.com/import-js/eslint-plugin-import/pull/2332 -[#2334]: https://github.com/import-js/eslint-plugin-import/pull/2334 -[#2330]: https://github.com/import-js/eslint-plugin-import/pull/2330 -[#2315]: https://github.com/import-js/eslint-plugin-import/pull/2315 -[#2305]: https://github.com/import-js/eslint-plugin-import/pull/2305 -[#2299]: https://github.com/import-js/eslint-plugin-import/pull/2299 -[#2297]: https://github.com/import-js/eslint-plugin-import/pull/2297 -[#2287]: https://github.com/import-js/eslint-plugin-import/pull/2287 -[#2282]: https://github.com/import-js/eslint-plugin-import/pull/2282 -[#2280]: https://github.com/import-js/eslint-plugin-import/pull/2280 -[#2279]: https://github.com/import-js/eslint-plugin-import/pull/2279 -[#2272]: https://github.com/import-js/eslint-plugin-import/pull/2272 -[#2271]: https://github.com/import-js/eslint-plugin-import/pull/2271 -[#2270]: https://github.com/import-js/eslint-plugin-import/pull/2270 -[#2240]: https://github.com/import-js/eslint-plugin-import/pull/2240 -[#2233]: https://github.com/import-js/eslint-plugin-import/pull/2233 -[#2226]: https://github.com/import-js/eslint-plugin-import/pull/2226 -[#2220]: https://github.com/import-js/eslint-plugin-import/pull/2220 -[#2219]: https://github.com/import-js/eslint-plugin-import/pull/2219 -[#2212]: https://github.com/import-js/eslint-plugin-import/pull/2212 -[#2196]: https://github.com/import-js/eslint-plugin-import/pull/2196 -[#2194]: https://github.com/import-js/eslint-plugin-import/pull/2194 -[#2191]: https://github.com/import-js/eslint-plugin-import/pull/2191 -[#2184]: https://github.com/import-js/eslint-plugin-import/pull/2184 -[#2179]: https://github.com/import-js/eslint-plugin-import/pull/2179 -[#2160]: https://github.com/import-js/eslint-plugin-import/pull/2160 -[#2158]: https://github.com/import-js/eslint-plugin-import/pull/2158 -[#2156]: https://github.com/import-js/eslint-plugin-import/pull/2156 -[#2149]: https://github.com/import-js/eslint-plugin-import/pull/2149 -[#2146]: https://github.com/import-js/eslint-plugin-import/pull/2146 -[#2140]: https://github.com/import-js/eslint-plugin-import/pull/2140 -[#2138]: https://github.com/import-js/eslint-plugin-import/pull/2138 -[#2121]: https://github.com/import-js/eslint-plugin-import/pull/2121 -[#2112]: https://github.com/import-js/eslint-plugin-import/pull/2112 -[#2099]: https://github.com/import-js/eslint-plugin-import/pull/2099 -[#2097]: https://github.com/import-js/eslint-plugin-import/pull/2097 -[#2090]: https://github.com/import-js/eslint-plugin-import/pull/2090 -[#2087]: https://github.com/import-js/eslint-plugin-import/pull/2087 -[#2083]: https://github.com/import-js/eslint-plugin-import/pull/2083 -[#2075]: https://github.com/import-js/eslint-plugin-import/pull/2075 -[#2071]: https://github.com/import-js/eslint-plugin-import/pull/2071 -[#2047]: https://github.com/import-js/eslint-plugin-import/pull/2047 -[#2034]: https://github.com/import-js/eslint-plugin-import/pull/2034 -[#2028]: https://github.com/import-js/eslint-plugin-import/pull/2028 -[#2026]: https://github.com/import-js/eslint-plugin-import/pull/2026 -[#2022]: https://github.com/import-js/eslint-plugin-import/pull/2022 -[#2021]: https://github.com/import-js/eslint-plugin-import/pull/2021 -[#2012]: https://github.com/import-js/eslint-plugin-import/pull/2012 -[#1997]: https://github.com/import-js/eslint-plugin-import/pull/1997 -[#1993]: https://github.com/import-js/eslint-plugin-import/pull/1993 -[#1990]: https://github.com/import-js/eslint-plugin-import/pull/1990 -[#1985]: https://github.com/import-js/eslint-plugin-import/pull/1985 -[#1983]: https://github.com/import-js/eslint-plugin-import/pull/1983 -[#1974]: https://github.com/import-js/eslint-plugin-import/pull/1974 -[#1958]: https://github.com/import-js/eslint-plugin-import/pull/1958 -[#1948]: https://github.com/import-js/eslint-plugin-import/pull/1948 -[#1947]: https://github.com/import-js/eslint-plugin-import/pull/1947 -[#1944]: https://github.com/import-js/eslint-plugin-import/pull/1944 -[#1940]: https://github.com/import-js/eslint-plugin-import/pull/1940 -[#1897]: https://github.com/import-js/eslint-plugin-import/pull/1897 -[#1889]: https://github.com/import-js/eslint-plugin-import/pull/1889 -[#1878]: https://github.com/import-js/eslint-plugin-import/pull/1878 -[#1860]: https://github.com/import-js/eslint-plugin-import/pull/1860 -[#1848]: https://github.com/import-js/eslint-plugin-import/pull/1848 -[#1847]: https://github.com/import-js/eslint-plugin-import/pull/1847 -[#1846]: https://github.com/import-js/eslint-plugin-import/pull/1846 -[#1836]: https://github.com/import-js/eslint-plugin-import/pull/1836 -[#1835]: https://github.com/import-js/eslint-plugin-import/pull/1835 -[#1833]: https://github.com/import-js/eslint-plugin-import/pull/1833 -[#1831]: https://github.com/import-js/eslint-plugin-import/pull/1831 -[#1830]: https://github.com/import-js/eslint-plugin-import/pull/1830 -[#1824]: https://github.com/import-js/eslint-plugin-import/pull/1824 -[#1823]: https://github.com/import-js/eslint-plugin-import/pull/1823 -[#1822]: https://github.com/import-js/eslint-plugin-import/pull/1822 -[#1820]: https://github.com/import-js/eslint-plugin-import/pull/1820 -[#1819]: https://github.com/import-js/eslint-plugin-import/pull/1819 -[#1802]: https://github.com/import-js/eslint-plugin-import/pull/1802 -[#1788]: https://github.com/import-js/eslint-plugin-import/pull/1788 -[#1786]: https://github.com/import-js/eslint-plugin-import/pull/1786 -[#1785]: https://github.com/import-js/eslint-plugin-import/pull/1785 -[#1776]: https://github.com/import-js/eslint-plugin-import/pull/1776 -[#1770]: https://github.com/import-js/eslint-plugin-import/pull/1770 -[#1764]: https://github.com/import-js/eslint-plugin-import/pull/1764 -[#1763]: https://github.com/import-js/eslint-plugin-import/pull/1763 -[#1751]: https://github.com/import-js/eslint-plugin-import/pull/1751 -[#1744]: https://github.com/import-js/eslint-plugin-import/pull/1744 -[#1736]: https://github.com/import-js/eslint-plugin-import/pull/1736 -[#1735]: https://github.com/import-js/eslint-plugin-import/pull/1735 -[#1726]: https://github.com/import-js/eslint-plugin-import/pull/1726 -[#1724]: https://github.com/import-js/eslint-plugin-import/pull/1724 -[#1719]: https://github.com/import-js/eslint-plugin-import/pull/1719 -[#1696]: https://github.com/import-js/eslint-plugin-import/pull/1696 -[#1691]: https://github.com/import-js/eslint-plugin-import/pull/1691 -[#1690]: https://github.com/import-js/eslint-plugin-import/pull/1690 -[#1689]: https://github.com/import-js/eslint-plugin-import/pull/1689 -[#1681]: https://github.com/import-js/eslint-plugin-import/pull/1681 -[#1676]: https://github.com/import-js/eslint-plugin-import/pull/1676 -[#1666]: https://github.com/import-js/eslint-plugin-import/pull/1666 -[#1664]: https://github.com/import-js/eslint-plugin-import/pull/1664 -[#1660]: https://github.com/import-js/eslint-plugin-import/pull/1660 -[#1658]: https://github.com/import-js/eslint-plugin-import/pull/1658 -[#1651]: https://github.com/import-js/eslint-plugin-import/pull/1651 -[#1626]: https://github.com/import-js/eslint-plugin-import/pull/1626 -[#1620]: https://github.com/import-js/eslint-plugin-import/pull/1620 -[#1619]: https://github.com/import-js/eslint-plugin-import/pull/1619 -[#1612]: https://github.com/import-js/eslint-plugin-import/pull/1612 -[#1611]: https://github.com/import-js/eslint-plugin-import/pull/1611 -[#1605]: https://github.com/import-js/eslint-plugin-import/pull/1605 -[#1586]: https://github.com/import-js/eslint-plugin-import/pull/1586 -[#1572]: https://github.com/import-js/eslint-plugin-import/pull/1572 -[#1569]: https://github.com/import-js/eslint-plugin-import/pull/1569 -[#1563]: https://github.com/import-js/eslint-plugin-import/pull/1563 -[#1560]: https://github.com/import-js/eslint-plugin-import/pull/1560 -[#1551]: https://github.com/import-js/eslint-plugin-import/pull/1551 -[#1542]: https://github.com/import-js/eslint-plugin-import/pull/1542 -[#1534]: https://github.com/import-js/eslint-plugin-import/pull/1534 -[#1528]: https://github.com/import-js/eslint-plugin-import/pull/1528 -[#1526]: https://github.com/import-js/eslint-plugin-import/pull/1526 -[#1521]: https://github.com/import-js/eslint-plugin-import/pull/1521 -[#1519]: https://github.com/import-js/eslint-plugin-import/pull/1519 -[#1517]: https://github.com/import-js/eslint-plugin-import/pull/1517 -[#1507]: https://github.com/import-js/eslint-plugin-import/pull/1507 -[#1506]: https://github.com/import-js/eslint-plugin-import/pull/1506 -[#1496]: https://github.com/import-js/eslint-plugin-import/pull/1496 -[#1495]: https://github.com/import-js/eslint-plugin-import/pull/1495 -[#1494]: https://github.com/import-js/eslint-plugin-import/pull/1494 -[#1493]: https://github.com/import-js/eslint-plugin-import/pull/1493 -[#1491]: https://github.com/import-js/eslint-plugin-import/pull/1491 -[#1472]: https://github.com/import-js/eslint-plugin-import/pull/1472 -[#1470]: https://github.com/import-js/eslint-plugin-import/pull/1470 -[#1447]: https://github.com/import-js/eslint-plugin-import/pull/1447 -[#1439]: https://github.com/import-js/eslint-plugin-import/pull/1439 -[#1436]: https://github.com/import-js/eslint-plugin-import/pull/1436 -[#1435]: https://github.com/import-js/eslint-plugin-import/pull/1435 -[#1425]: https://github.com/import-js/eslint-plugin-import/pull/1425 -[#1419]: https://github.com/import-js/eslint-plugin-import/pull/1419 -[#1412]: https://github.com/import-js/eslint-plugin-import/pull/1412 -[#1409]: https://github.com/import-js/eslint-plugin-import/pull/1409 -[#1404]: https://github.com/import-js/eslint-plugin-import/pull/1404 -[#1401]: https://github.com/import-js/eslint-plugin-import/pull/1401 -[#1393]: https://github.com/import-js/eslint-plugin-import/pull/1393 -[#1389]: https://github.com/import-js/eslint-plugin-import/pull/1389 -[#1386]: https://github.com/import-js/eslint-plugin-import/pull/1386 -[#1377]: https://github.com/import-js/eslint-plugin-import/pull/1377 -[#1375]: https://github.com/import-js/eslint-plugin-import/pull/1375 -[#1372]: https://github.com/import-js/eslint-plugin-import/pull/1372 -[#1371]: https://github.com/import-js/eslint-plugin-import/pull/1371 -[#1370]: https://github.com/import-js/eslint-plugin-import/pull/1370 -[#1363]: https://github.com/import-js/eslint-plugin-import/pull/1363 -[#1360]: https://github.com/import-js/eslint-plugin-import/pull/1360 -[#1358]: https://github.com/import-js/eslint-plugin-import/pull/1358 -[#1356]: https://github.com/import-js/eslint-plugin-import/pull/1356 -[#1354]: https://github.com/import-js/eslint-plugin-import/pull/1354 -[#1352]: https://github.com/import-js/eslint-plugin-import/pull/1352 -[#1347]: https://github.com/import-js/eslint-plugin-import/pull/1347 -[#1345]: https://github.com/import-js/eslint-plugin-import/pull/1345 -[#1342]: https://github.com/import-js/eslint-plugin-import/pull/1342 -[#1340]: https://github.com/import-js/eslint-plugin-import/pull/1340 -[#1333]: https://github.com/import-js/eslint-plugin-import/pull/1333 -[#1331]: https://github.com/import-js/eslint-plugin-import/pull/1331 -[#1330]: https://github.com/import-js/eslint-plugin-import/pull/1330 -[#1320]: https://github.com/import-js/eslint-plugin-import/pull/1320 -[#1319]: https://github.com/import-js/eslint-plugin-import/pull/1319 -[#1312]: https://github.com/import-js/eslint-plugin-import/pull/1312 -[#1308]: https://github.com/import-js/eslint-plugin-import/pull/1308 -[#1304]: https://github.com/import-js/eslint-plugin-import/pull/1304 -[#1297]: https://github.com/import-js/eslint-plugin-import/pull/1297 -[#1295]: https://github.com/import-js/eslint-plugin-import/pull/1295 -[#1294]: https://github.com/import-js/eslint-plugin-import/pull/1294 -[#1290]: https://github.com/import-js/eslint-plugin-import/pull/1290 -[#1277]: https://github.com/import-js/eslint-plugin-import/pull/1277 -[#1262]: https://github.com/import-js/eslint-plugin-import/pull/1262 -[#1257]: https://github.com/import-js/eslint-plugin-import/pull/1257 -[#1253]: https://github.com/import-js/eslint-plugin-import/pull/1253 -[#1248]: https://github.com/import-js/eslint-plugin-import/pull/1248 -[#1238]: https://github.com/import-js/eslint-plugin-import/pull/1238 -[#1237]: https://github.com/import-js/eslint-plugin-import/pull/1237 -[#1235]: https://github.com/import-js/eslint-plugin-import/pull/1235 -[#1234]: https://github.com/import-js/eslint-plugin-import/pull/1234 -[#1232]: https://github.com/import-js/eslint-plugin-import/pull/1232 -[#1223]: https://github.com/import-js/eslint-plugin-import/pull/1223 -[#1222]: https://github.com/import-js/eslint-plugin-import/pull/1222 -[#1218]: https://github.com/import-js/eslint-plugin-import/pull/1218 -[#1176]: https://github.com/import-js/eslint-plugin-import/pull/1176 -[#1163]: https://github.com/import-js/eslint-plugin-import/pull/1163 -[#1157]: https://github.com/import-js/eslint-plugin-import/pull/1157 -[#1151]: https://github.com/import-js/eslint-plugin-import/pull/1151 -[#1142]: https://github.com/import-js/eslint-plugin-import/pull/1142 -[#1139]: https://github.com/import-js/eslint-plugin-import/pull/1139 -[#1137]: https://github.com/import-js/eslint-plugin-import/pull/1137 -[#1135]: https://github.com/import-js/eslint-plugin-import/pull/1135 -[#1128]: https://github.com/import-js/eslint-plugin-import/pull/1128 -[#1126]: https://github.com/import-js/eslint-plugin-import/pull/1126 -[#1122]: https://github.com/import-js/eslint-plugin-import/pull/1122 -[#1112]: https://github.com/import-js/eslint-plugin-import/pull/1112 -[#1107]: https://github.com/import-js/eslint-plugin-import/pull/1107 -[#1106]: https://github.com/import-js/eslint-plugin-import/pull/1106 -[#1105]: https://github.com/import-js/eslint-plugin-import/pull/1105 -[#1093]: https://github.com/import-js/eslint-plugin-import/pull/1093 -[#1085]: https://github.com/import-js/eslint-plugin-import/pull/1085 -[#1068]: https://github.com/import-js/eslint-plugin-import/pull/1068 -[#1049]: https://github.com/import-js/eslint-plugin-import/pull/1049 -[#1046]: https://github.com/import-js/eslint-plugin-import/pull/1046 -[#966]: https://github.com/import-js/eslint-plugin-import/pull/966 -[#944]: https://github.com/import-js/eslint-plugin-import/pull/944 -[#912]: https://github.com/import-js/eslint-plugin-import/pull/912 -[#908]: https://github.com/import-js/eslint-plugin-import/pull/908 -[#891]: https://github.com/import-js/eslint-plugin-import/pull/891 -[#889]: https://github.com/import-js/eslint-plugin-import/pull/889 -[#880]: https://github.com/import-js/eslint-plugin-import/pull/880 -[#871]: https://github.com/import-js/eslint-plugin-import/pull/871 -[#858]: https://github.com/import-js/eslint-plugin-import/pull/858 -[#843]: https://github.com/import-js/eslint-plugin-import/pull/843 -[#804]: https://github.com/import-js/eslint-plugin-import/pull/804 -[#797]: https://github.com/import-js/eslint-plugin-import/pull/797 -[#794]: https://github.com/import-js/eslint-plugin-import/pull/794 -[#744]: https://github.com/import-js/eslint-plugin-import/pull/744 -[#742]: https://github.com/import-js/eslint-plugin-import/pull/742 -[#737]: https://github.com/import-js/eslint-plugin-import/pull/737 -[#727]: https://github.com/import-js/eslint-plugin-import/pull/727 -[#721]: https://github.com/import-js/eslint-plugin-import/pull/721 -[#712]: https://github.com/import-js/eslint-plugin-import/pull/712 -[#696]: https://github.com/import-js/eslint-plugin-import/pull/696 -[#685]: https://github.com/import-js/eslint-plugin-import/pull/685 -[#680]: https://github.com/import-js/eslint-plugin-import/pull/680 -[#654]: https://github.com/import-js/eslint-plugin-import/pull/654 -[#639]: https://github.com/import-js/eslint-plugin-import/pull/639 -[#632]: https://github.com/import-js/eslint-plugin-import/pull/632 -[#630]: https://github.com/import-js/eslint-plugin-import/pull/630 -[#629]: https://github.com/import-js/eslint-plugin-import/pull/629 -[#628]: https://github.com/import-js/eslint-plugin-import/pull/628 -[#596]: https://github.com/import-js/eslint-plugin-import/pull/596 -[#586]: https://github.com/import-js/eslint-plugin-import/pull/586 -[#578]: https://github.com/import-js/eslint-plugin-import/pull/578 -[#568]: https://github.com/import-js/eslint-plugin-import/pull/568 -[#555]: https://github.com/import-js/eslint-plugin-import/pull/555 -[#538]: https://github.com/import-js/eslint-plugin-import/pull/538 -[#527]: https://github.com/import-js/eslint-plugin-import/pull/527 -[#518]: https://github.com/import-js/eslint-plugin-import/pull/518 -[#509]: https://github.com/import-js/eslint-plugin-import/pull/509 -[#508]: https://github.com/import-js/eslint-plugin-import/pull/508 -[#503]: https://github.com/import-js/eslint-plugin-import/pull/503 -[#499]: https://github.com/import-js/eslint-plugin-import/pull/499 -[#489]: https://github.com/import-js/eslint-plugin-import/pull/489 -[#485]: https://github.com/import-js/eslint-plugin-import/pull/485 -[#461]: https://github.com/import-js/eslint-plugin-import/pull/461 -[#449]: https://github.com/import-js/eslint-plugin-import/pull/449 -[#444]: https://github.com/import-js/eslint-plugin-import/pull/444 -[#428]: https://github.com/import-js/eslint-plugin-import/pull/428 -[#395]: https://github.com/import-js/eslint-plugin-import/pull/395 -[#371]: https://github.com/import-js/eslint-plugin-import/pull/371 -[#365]: https://github.com/import-js/eslint-plugin-import/pull/365 -[#359]: https://github.com/import-js/eslint-plugin-import/pull/359 -[#343]: https://github.com/import-js/eslint-plugin-import/pull/343 -[#332]: https://github.com/import-js/eslint-plugin-import/pull/332 -[#322]: https://github.com/import-js/eslint-plugin-import/pull/322 -[#321]: https://github.com/import-js/eslint-plugin-import/pull/321 -[#316]: https://github.com/import-js/eslint-plugin-import/pull/316 -[#314]: https://github.com/import-js/eslint-plugin-import/pull/314 -[#308]: https://github.com/import-js/eslint-plugin-import/pull/308 -[#298]: https://github.com/import-js/eslint-plugin-import/pull/298 -[#297]: https://github.com/import-js/eslint-plugin-import/pull/297 -[#296]: https://github.com/import-js/eslint-plugin-import/pull/296 -[#290]: https://github.com/import-js/eslint-plugin-import/pull/290 -[#289]: https://github.com/import-js/eslint-plugin-import/pull/289 -[#288]: https://github.com/import-js/eslint-plugin-import/pull/288 -[#287]: https://github.com/import-js/eslint-plugin-import/pull/287 -[#278]: https://github.com/import-js/eslint-plugin-import/pull/278 -[#261]: https://github.com/import-js/eslint-plugin-import/pull/261 -[#256]: https://github.com/import-js/eslint-plugin-import/pull/256 -[#254]: https://github.com/import-js/eslint-plugin-import/pull/254 -[#250]: https://github.com/import-js/eslint-plugin-import/pull/250 -[#247]: https://github.com/import-js/eslint-plugin-import/pull/247 -[#245]: https://github.com/import-js/eslint-plugin-import/pull/245 -[#243]: https://github.com/import-js/eslint-plugin-import/pull/243 -[#241]: https://github.com/import-js/eslint-plugin-import/pull/241 -[#239]: https://github.com/import-js/eslint-plugin-import/pull/239 -[#228]: https://github.com/import-js/eslint-plugin-import/pull/228 -[#211]: https://github.com/import-js/eslint-plugin-import/pull/211 -[#164]: https://github.com/import-js/eslint-plugin-import/pull/164 -[#157]: https://github.com/import-js/eslint-plugin-import/pull/157 - -[#2930]: https://github.com/import-js/eslint-plugin-import/issues/2930 -[#2687]: https://github.com/import-js/eslint-plugin-import/issues/2687 -[#2684]: https://github.com/import-js/eslint-plugin-import/issues/2684 -[#2674]: https://github.com/import-js/eslint-plugin-import/issues/2674 -[#2668]: https://github.com/import-js/eslint-plugin-import/issues/2668 -[#2666]: https://github.com/import-js/eslint-plugin-import/issues/2666 -[#2665]: https://github.com/import-js/eslint-plugin-import/issues/2665 -[#2577]: https://github.com/import-js/eslint-plugin-import/issues/2577 -[#2447]: https://github.com/import-js/eslint-plugin-import/issues/2447 -[#2444]: https://github.com/import-js/eslint-plugin-import/issues/2444 -[#2412]: https://github.com/import-js/eslint-plugin-import/issues/2412 -[#2392]: https://github.com/import-js/eslint-plugin-import/issues/2392 -[#2340]: https://github.com/import-js/eslint-plugin-import/issues/2340 -[#2255]: https://github.com/import-js/eslint-plugin-import/issues/2255 -[#2210]: https://github.com/import-js/eslint-plugin-import/issues/2210 -[#2201]: https://github.com/import-js/eslint-plugin-import/issues/2201 -[#2199]: https://github.com/import-js/eslint-plugin-import/issues/2199 -[#2161]: https://github.com/import-js/eslint-plugin-import/issues/2161 -[#2118]: https://github.com/import-js/eslint-plugin-import/issues/2118 -[#2067]: https://github.com/import-js/eslint-plugin-import/issues/2067 -[#2063]: https://github.com/import-js/eslint-plugin-import/issues/2063 -[#2056]: https://github.com/import-js/eslint-plugin-import/issues/2056 -[#1998]: https://github.com/import-js/eslint-plugin-import/issues/1998 -[#1965]: https://github.com/import-js/eslint-plugin-import/issues/1965 -[#1924]: https://github.com/import-js/eslint-plugin-import/issues/1924 -[#1854]: https://github.com/import-js/eslint-plugin-import/issues/1854 -[#1841]: https://github.com/import-js/eslint-plugin-import/issues/1841 -[#1834]: https://github.com/import-js/eslint-plugin-import/issues/1834 -[#1814]: https://github.com/import-js/eslint-plugin-import/issues/1814 -[#1811]: https://github.com/import-js/eslint-plugin-import/issues/1811 -[#1808]: https://github.com/import-js/eslint-plugin-import/issues/1808 -[#1805]: https://github.com/import-js/eslint-plugin-import/issues/1805 -[#1801]: https://github.com/import-js/eslint-plugin-import/issues/1801 -[#1722]: https://github.com/import-js/eslint-plugin-import/issues/1722 -[#1704]: https://github.com/import-js/eslint-plugin-import/issues/1704 -[#1702]: https://github.com/import-js/eslint-plugin-import/issues/1702 -[#1635]: https://github.com/import-js/eslint-plugin-import/issues/1635 -[#1631]: https://github.com/import-js/eslint-plugin-import/issues/1631 -[#1616]: https://github.com/import-js/eslint-plugin-import/issues/1616 -[#1613]: https://github.com/import-js/eslint-plugin-import/issues/1613 -[#1590]: https://github.com/import-js/eslint-plugin-import/issues/1590 -[#1589]: https://github.com/import-js/eslint-plugin-import/issues/1589 -[#1565]: https://github.com/import-js/eslint-plugin-import/issues/1565 -[#1366]: https://github.com/import-js/eslint-plugin-import/issues/1366 -[#1334]: https://github.com/import-js/eslint-plugin-import/issues/1334 -[#1323]: https://github.com/import-js/eslint-plugin-import/issues/1323 -[#1322]: https://github.com/import-js/eslint-plugin-import/issues/1322 -[#1300]: https://github.com/import-js/eslint-plugin-import/issues/1300 -[#1293]: https://github.com/import-js/eslint-plugin-import/issues/1293 -[#1266]: https://github.com/import-js/eslint-plugin-import/issues/1266 -[#1256]: https://github.com/import-js/eslint-plugin-import/issues/1256 -[#1233]: https://github.com/import-js/eslint-plugin-import/issues/1233 -[#1175]: https://github.com/import-js/eslint-plugin-import/issues/1175 -[#1166]: https://github.com/import-js/eslint-plugin-import/issues/1166 -[#1144]: https://github.com/import-js/eslint-plugin-import/issues/1144 -[#1058]: https://github.com/import-js/eslint-plugin-import/issues/1058 -[#1035]: https://github.com/import-js/eslint-plugin-import/issues/1035 -[#931]: https://github.com/import-js/eslint-plugin-import/issues/931 -[#886]: https://github.com/import-js/eslint-plugin-import/issues/886 -[#863]: https://github.com/import-js/eslint-plugin-import/issues/863 -[#842]: https://github.com/import-js/eslint-plugin-import/issues/842 -[#839]: https://github.com/import-js/eslint-plugin-import/issues/839 -[#795]: https://github.com/import-js/eslint-plugin-import/issues/795 -[#793]: https://github.com/import-js/eslint-plugin-import/issues/793 -[#720]: https://github.com/import-js/eslint-plugin-import/issues/720 -[#717]: https://github.com/import-js/eslint-plugin-import/issues/717 -[#686]: https://github.com/import-js/eslint-plugin-import/issues/686 -[#671]: https://github.com/import-js/eslint-plugin-import/issues/671 -[#660]: https://github.com/import-js/eslint-plugin-import/issues/660 -[#653]: https://github.com/import-js/eslint-plugin-import/issues/653 -[#627]: https://github.com/import-js/eslint-plugin-import/issues/627 -[#620]: https://github.com/import-js/eslint-plugin-import/issues/620 -[#609]: https://github.com/import-js/eslint-plugin-import/issues/609 -[#604]: https://github.com/import-js/eslint-plugin-import/issues/604 -[#602]: https://github.com/import-js/eslint-plugin-import/issues/602 -[#601]: https://github.com/import-js/eslint-plugin-import/issues/601 -[#592]: https://github.com/import-js/eslint-plugin-import/issues/592 -[#577]: https://github.com/import-js/eslint-plugin-import/issues/577 -[#570]: https://github.com/import-js/eslint-plugin-import/issues/570 -[#567]: https://github.com/import-js/eslint-plugin-import/issues/567 -[#566]: https://github.com/import-js/eslint-plugin-import/issues/566 -[#545]: https://github.com/import-js/eslint-plugin-import/issues/545 -[#530]: https://github.com/import-js/eslint-plugin-import/issues/530 -[#529]: https://github.com/import-js/eslint-plugin-import/issues/529 -[#519]: https://github.com/import-js/eslint-plugin-import/issues/519 -[#507]: https://github.com/import-js/eslint-plugin-import/issues/507 -[#484]: https://github.com/import-js/eslint-plugin-import/issues/484 -[#478]: https://github.com/import-js/eslint-plugin-import/issues/478 -[#456]: https://github.com/import-js/eslint-plugin-import/issues/456 -[#453]: https://github.com/import-js/eslint-plugin-import/issues/453 -[#452]: https://github.com/import-js/eslint-plugin-import/issues/452 -[#447]: https://github.com/import-js/eslint-plugin-import/issues/447 -[#441]: https://github.com/import-js/eslint-plugin-import/issues/441 -[#423]: https://github.com/import-js/eslint-plugin-import/issues/423 -[#416]: https://github.com/import-js/eslint-plugin-import/issues/416 -[#415]: https://github.com/import-js/eslint-plugin-import/issues/415 -[#402]: https://github.com/import-js/eslint-plugin-import/issues/402 -[#386]: https://github.com/import-js/eslint-plugin-import/issues/386 -[#373]: https://github.com/import-js/eslint-plugin-import/issues/373 -[#370]: https://github.com/import-js/eslint-plugin-import/issues/370 -[#348]: https://github.com/import-js/eslint-plugin-import/issues/348 -[#342]: https://github.com/import-js/eslint-plugin-import/issues/342 -[#328]: https://github.com/import-js/eslint-plugin-import/issues/328 -[#317]: https://github.com/import-js/eslint-plugin-import/issues/317 -[#313]: https://github.com/import-js/eslint-plugin-import/issues/313 -[#311]: https://github.com/import-js/eslint-plugin-import/issues/311 -[#306]: https://github.com/import-js/eslint-plugin-import/issues/306 -[#286]: https://github.com/import-js/eslint-plugin-import/issues/286 -[#283]: https://github.com/import-js/eslint-plugin-import/issues/283 -[#281]: https://github.com/import-js/eslint-plugin-import/issues/281 -[#275]: https://github.com/import-js/eslint-plugin-import/issues/275 -[#272]: https://github.com/import-js/eslint-plugin-import/issues/272 -[#270]: https://github.com/import-js/eslint-plugin-import/issues/270 -[#267]: https://github.com/import-js/eslint-plugin-import/issues/267 -[#266]: https://github.com/import-js/eslint-plugin-import/issues/266 -[#216]: https://github.com/import-js/eslint-plugin-import/issues/216 -[#214]: https://github.com/import-js/eslint-plugin-import/issues/214 -[#210]: https://github.com/import-js/eslint-plugin-import/issues/210 -[#200]: https://github.com/import-js/eslint-plugin-import/issues/200 -[#192]: https://github.com/import-js/eslint-plugin-import/issues/192 -[#191]: https://github.com/import-js/eslint-plugin-import/issues/191 -[#189]: https://github.com/import-js/eslint-plugin-import/issues/189 -[#170]: https://github.com/import-js/eslint-plugin-import/issues/170 -[#155]: https://github.com/import-js/eslint-plugin-import/issues/155 -[#119]: https://github.com/import-js/eslint-plugin-import/issues/119 -[#89]: https://github.com/import-js/eslint-plugin-import/issues/89 - -[Unreleased]: https://github.com/import-js/eslint-plugin-import/compare/v2.29.1...HEAD -[2.29.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.29.0...v2.29.1 -[2.29.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.28.1...v2.29.0 -[2.28.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.28.0...v2.28.1 -[2.28.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.27.5...v2.28.0 -[2.27.5]: https://github.com/import-js/eslint-plugin-import/compare/v2.27.4...v2.27.5 -[2.27.4]: https://github.com/import-js/eslint-plugin-import/compare/v2.27.3...v2.27.4 -[2.27.3]: https://github.com/import-js/eslint-plugin-import/compare/v2.27.2...v2.27.3 -[2.27.2]: https://github.com/import-js/eslint-plugin-import/compare/v2.27.1...v2.27.2 -[2.27.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.27.0...v2.27.1 -[2.27.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.26.0...v2.27.0 -[2.26.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.25.4...v2.26.0 -[2.25.4]: https://github.com/import-js/eslint-plugin-import/compare/v2.25.3...v2.25.4 -[2.25.3]: https://github.com/import-js/eslint-plugin-import/compare/v2.25.2...v2.25.3 -[2.25.2]: https://github.com/import-js/eslint-plugin-import/compare/v2.25.1...v2.25.2 -[2.25.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.25.0...v2.25.1 -[2.25.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.24.2...v2.25.0 -[2.24.2]: https://github.com/import-js/eslint-plugin-import/compare/v2.24.1...v2.24.2 -[2.24.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.24.0...v2.24.1 -[2.24.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.23.4...v2.24.0 -[2.23.4]: https://github.com/import-js/eslint-plugin-import/compare/v2.23.3...v2.23.4 -[2.23.3]: https://github.com/import-js/eslint-plugin-import/compare/v2.23.2...v2.23.3 -[2.23.2]: https://github.com/import-js/eslint-plugin-import/compare/v2.23.1...v2.23.2 -[2.23.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.23.0...v2.23.1 -[2.23.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.22.1...v2.23.0 -[2.22.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.22.0...v2.22.1 -[2.22.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.21.1...v2.22.0 -[2.21.2]: https://github.com/import-js/eslint-plugin-import/compare/v2.21.1...v2.21.2 -[2.21.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.21.0...v2.21.1 -[2.21.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.20.2...v2.21.0 -[2.20.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.20.1...v2.20.2 -[2.20.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.20.0...v2.20.1 -[2.19.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.19.1...v2.20.0 -[2.19.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.19.0...v2.19.1 -[2.19.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.18.2...v2.19.0 -[2.18.2]: https://github.com/import-js/eslint-plugin-import/compare/v2.18.1...v2.18.2 -[2.18.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.18.0...v2.18.1 -[2.18.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.17.3...v2.18.0 -[2.17.3]: https://github.com/import-js/eslint-plugin-import/compare/v2.17.2...v2.17.3 -[2.17.2]: https://github.com/import-js/eslint-plugin-import/compare/v2.17.1...v2.17.2 -[2.17.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.17.0...v2.17.1 -[2.17.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.16.0...v2.17.0 -[2.16.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.15.0...v2.16.0 -[2.15.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.14.0...v2.15.0 -[2.14.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.13.0...v2.14.0 -[2.13.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.12.0...v2.13.0 -[2.12.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.11.0...v2.12.0 -[2.11.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.10.0...v2.11.0 -[2.10.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.9.0...v2.10.0 -[2.9.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.8.0...v2.9.0 -[2.8.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.7.0...v2.8.0 -[2.7.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.6.1...v2.7.0 -[2.6.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.6.0...v2.6.1 -[2.6.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.5.0...v2.6.0 -[2.5.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.4.0...v2.5.0 -[2.4.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.3.0...v2.4.0 -[2.3.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.2.0...v2.3.0 -[2.2.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.1.0...v2.2.0 -[2.1.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.0.1...v2.1.0 -[2.0.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.0.0...v2.0.1 -[2.0.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.16.0...v2.0.0 -[1.16.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.15.0...v1.16.0 -[1.15.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.14.0...v1.15.0 -[1.14.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.13.0...v1.14.0 -[1.13.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.12.0...v1.13.0 -[1.12.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.11.1...v1.12.0 -[1.11.1]: https://github.com/import-js/eslint-plugin-import/compare/v1.11.0...v1.11.1 -[1.11.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.10.3...v1.11.0 -[1.10.3]: https://github.com/import-js/eslint-plugin-import/compare/v1.10.2...v1.10.3 -[1.10.2]: https://github.com/import-js/eslint-plugin-import/compare/v1.10.1...v1.10.2 -[1.10.1]: https://github.com/import-js/eslint-plugin-import/compare/v1.10.0...v1.10.1 -[1.10.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.9.2...v1.10.0 -[1.9.2]: https://github.com/import-js/eslint-plugin-import/compare/v1.9.1...v1.9.2 -[1.9.1]: https://github.com/import-js/eslint-plugin-import/compare/v1.9.0...v1.9.1 -[1.9.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.8.1...v1.9.0 -[1.8.1]: https://github.com/import-js/eslint-plugin-import/compare/v1.8.0...v1.8.1 -[1.8.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.7.0...v1.8.0 -[1.7.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.6.1...v1.7.0 -[1.6.1]: https://github.com/import-js/eslint-plugin-import/compare/v1.6.0...v1.6.1 -[1.6.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.5.0...1.6.0 -[1.5.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.4.0...v1.5.0 -[1.4.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.3.0...v1.4.0 -[1.3.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.2.0...v1.3.0 -[1.2.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.1.0...v1.2.0 -[1.1.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.0.4...v1.1.0 -[1.0.4]: https://github.com/import-js/eslint-plugin-import/compare/v1.0.3...v1.0.4 -[1.0.3]: https://github.com/import-js/eslint-plugin-import/compare/v1.0.2...v1.0.3 -[1.0.2]: https://github.com/import-js/eslint-plugin-import/compare/v1.0.1...v1.0.2 -[1.0.1]: https://github.com/import-js/eslint-plugin-import/compare/v1.0.0...v1.0.1 -[1.0.0]: https://github.com/import-js/eslint-plugin-import/compare/v1.0.0-beta.0...v1.0.0 -[1.0.0-beta.0]: https://github.com/import-js/eslint-plugin-import/compare/v0.13.0...v1.0.0-beta.0 -[0.13.0]: https://github.com/import-js/eslint-plugin-import/compare/v0.12.1...v0.13.0 -[0.12.2]: https://github.com/import-js/eslint-plugin-import/compare/v0.12.1...v0.12.2 -[0.12.1]: https://github.com/import-js/eslint-plugin-import/compare/v0.12.0...v0.12.1 -[0.12.0]: https://github.com/import-js/eslint-plugin-import/compare/v0.11.0...v0.12.0 -[0.11.0]: https://github.com/import-js/eslint-plugin-import/compare/v0.10.1...v0.11.0 - -[@1pete]: https://github.com/1pete -[@3nuc]: https://github.com/3nuc -[@aamulumi]: https://github.com/aamulumi -[@aberezkin]: https://github.com/aberezkin -[@adamborowski]: https://github.com/adamborowski -[@adjerbetian]: https://github.com/adjerbetian -[@AdriAt360]: https://github.com/AdriAt360 -[@ai]: https://github.com/ai -[@aladdin-add]: https://github.com/aladdin-add -[@alex-page]: https://github.com/alex-page -[@alexgorbatchev]: https://github.com/alexgorbatchev -[@andreubotella]: https://github.com/andreubotella -[@AndrewLeedham]: https://github.com/AndrewLeedham -[@andyogo]: https://github.com/andyogo -[@aravindet]: https://github.com/aravindet -[@arvigeus]: https://github.com/arvigeus -[@asapach]: https://github.com/asapach -[@astorije]: https://github.com/astorije -[@atav32]: https://github.com/atav32 -[@atikenny]: https://github.com/atikenny -[@atos1990]: https://github.com/atos1990 -[@azyzz228]: https://github.com/azyzz228 -[@barbogast]: https://github.com/barbogast -[@BarryThePenguin]: https://github.com/BarryThePenguin -[@be5invis]: https://github.com/be5invis -[@beatrizrezener]: https://github.com/beatrizrezener -[@benasher44]: https://github.com/benasher44 -[@benkrejci]: https://github.com/benkrejci -[@benmosher]: https://github.com/benmosher -[@benmunro]: https://github.com/benmunro -[@BenoitZugmeyer]: https://github.com/BenoitZugmeyer -[@bertyhell]: https://github.com/bertyhell -[@bicstone]: https://github.com/bicstone -[@Blasz]: https://github.com/Blasz -[@bmish]: https://github.com/bmish -[@borisyankov]: https://github.com/borisyankov -[@bradennapier]: https://github.com/bradennapier -[@bradzacher]: https://github.com/bradzacher -[@brendo]: https://github.com/brendo -[@brettz9]: https://github.com/brettz9 -[@Chamion]: https://github.com/Chamion -[@charlessuh]: https://github.com/charlessuh -[@charpeni]: https://github.com/charpeni -[@cherryblossom000]: https://github.com/cherryblossom000 -[@chrislloyd]: https://github.com/chrislloyd -[@christianvuerings]: https://github.com/christianvuerings -[@christophercurrie]: https://github.com/christophercurrie -[@cristobal]: https://github.com/cristobal -[@DamienCassou]: https://github.com/DamienCassou -[@danny-andrews]: https://github.com/dany-andrews -[@darkartur]: https://github.com/darkartur -[@davidbonnet]: https://github.com/davidbonnet -[@dbrewer5]: https://github.com/dbrewer5 -[@devinrhode2]: https://github.com/devinrhode2 -[@devongovett]: https://github.com/devongovett -[@dmnd]: https://github.com/dmnd -[@domdomegg]: https://github.com/domdomegg -[@duncanbeevers]: https://github.com/duncanbeevers -[@dwardu]: https://github.com/dwardu -[@echenley]: https://github.com/echenley -[@edemaine]: https://github.com/edemaine -[@eelyafi]: https://github.com/eelyafi -[@Ephem]: https://github.com/Ephem -[@ephys]: https://github.com/ephys -[@eps1lon]: https://github.com/eps1lon -[@ernestostifano]: https://github.com/ernestostifano -[@ertrzyiks]: https://github.com/ertrzyiks -[@fa93hws]: https://github.com/fa93hws -[@Fdawgs]: https://github.com/Fdawgs -[@fengkfengk]: https://github.com/fengkfengk -[@fernandopasik]: https://github.com/fernandopasik -[@feychenie]: https://github.com/feychenie -[@fisker]: https://github.com/fisker -[@FloEdelmann]: https://github.com/FloEdelmann -[@fooloomanzoo]: https://github.com/fooloomanzoo -[@foray1010]: https://github.com/foray1010 -[@forivall]: https://github.com/forivall -[@fsmaia]: https://github.com/fsmaia -[@fson]: https://github.com/fson -[@futpib]: https://github.com/futpib -[@gajus]: https://github.com/gajus -[@gausie]: https://github.com/gausie -[@gavriguy]: https://github.com/gavriguy -[@georeith]: https://github.com/georeith -[@giodamelio]: https://github.com/giodamelio -[@gnprice]: https://github.com/gnprice -[@golergka]: https://github.com/golergka -[@golopot]: https://github.com/golopot -[@GoodForOneFare]: https://github.com/GoodForOneFare -[@graingert]: https://github.com/graingert -[@grit96]: https://github.com/grit96 -[@guilhermelimak]: https://github.com/guilhermelimak -[@guillaumewuip]: https://github.com/guillaumewuip -[@hayes]: https://github.com/hayes -[@himynameisdave]: https://github.com/himynameisdave -[@hulkish]: https://github.com/hulkish -[@hyperupcall]: https://github.com/hyperupcall -[@Hypnosphi]: https://github.com/Hypnosphi -[@isiahmeadows]: https://github.com/isiahmeadows -[@IvanGoncharov]: https://github.com/IvanGoncharov -[@ivo-stefchev]: https://github.com/ivo-stefchev -[@jablko]: https://github.com/jablko -[@jakubsta]: https://github.com/jakubsta -[@jeffshaver]: https://github.com/jeffshaver -[@jf248]: https://github.com/jf248 -[@jfmengels]: https://github.com/jfmengels -[@JiangWeixian]: https://github.com/JiangWeixian -[@jimbolla]: https://github.com/jimbolla -[@jkimbo]: https://github.com/jkimbo -[@joaovieira]: https://github.com/joaovieira -[@joe-matsec]: https://github.com/joe-matsec -[@johndevedu]: https://github.com/johndevedu -[@johnthagen]: https://github.com/johnthagen -[@jonboiser]: https://github.com/jonboiser -[@josh]: https://github.com/josh -[@JounQin]: https://github.com/JounQin -[@jquense]: https://github.com/jquense -[@jseminck]: https://github.com/jseminck -[@julien1619]: https://github.com/julien1619 -[@justinanastos]: https://github.com/justinanastos -[@k15a]: https://github.com/k15a -[@kentcdodds]: https://github.com/kentcdodds -[@kevin940726]: https://github.com/kevin940726 -[@kgregory]: https://github.com/kgregory -[@kinland]: https://github.com/kinland -[@kirill-konshin]: https://github.com/kirill-konshin -[@kiwka]: https://github.com/kiwka -[@klimashkin]: https://github.com/klimashkin -[@kmui2]: https://github.com/kmui2 -[@knpwrs]: https://github.com/knpwrs -[@KostyaZgara]: https://github.com/KostyaZgara -[@kylemh]: https://github.com/kylemh -[@laurens-dg]: https://github.com/laurens-dg -[@laysent]: https://github.com/laysent -[@le0nik]: https://github.com/le0nik -[@leipert]: https://github.com/leipert -[@lemonmade]: https://github.com/lemonmade -[@lencioni]: https://github.com/lencioni -[@leonardodino]: https://github.com/leonardodino -[@Librazy]: https://github.com/Librazy -[@liby]: https://github.com/liby -[@lilling]: https://github.com/lilling -[@ljharb]: https://github.com/ljharb -[@ljqx]: https://github.com/ljqx -[@lo1tuma]: https://github.com/lo1tuma -[@loganfsmyth]: https://github.com/loganfsmyth -[@luczsoma]: https://github.com/luczsoma -[@ludofischer]: https://github.com/ludofischer -[@Lukas-Kullmann]: https://github.com/Lukas-Kullmann -[@lukeapage]: https://github.com/lukeapage -[@lydell]: https://github.com/lydell -[@magarcia]: https://github.com/magarcia -[@Mairu]: https://github.com/Mairu -[@malykhinvi]: https://github.com/malykhinvi -[@manovotny]: https://github.com/manovotny -[@manuth]: https://github.com/manuth -[@marcusdarmstrong]: https://github.com/marcusdarmstrong -[@mastilver]: https://github.com/mastilver -[@mathieudutour]: https://github.com/mathieudutour -[@MatthiasKunnen]: https://github.com/MatthiasKunnen -[@mattijsbliek]: https://github.com/mattijsbliek -[@Maxim-Mazurok]: https://github.com/Maxim-Mazurok -[@maxkomarychev]: https://github.com/maxkomarychev -[@maxmalov]: https://github.com/maxmalov -[@meowtec]: https://github.com/meowtec -[@mgwalker]: https://github.com/mgwalker -[@mhmadhamster]: https://github.com/MhMadHamster -[@MikeyBeLike]: https://github.com/MikeyBeLike -[@mpint]: https://github.com/mpint -[@mplewis]: https://github.com/mplewis -[@mrmckeb]: https://github.com/mrmckeb -[@msvab]: https://github.com/msvab -[@mulztob]: https://github.com/mulztob -[@mx-bernhard]: https://github.com/mx-bernhard -[@Nfinished]: https://github.com/Nfinished -[@nickofthyme]: https://github.com/nickofthyme -[@nicolashenry]: https://github.com/nicolashenry -[@noelebrun]: https://github.com/noelebrun -[@ntdb]: https://github.com/ntdb -[@nwalters512]: https://github.com/nwalters512 -[@ombene]: https://github.com/ombene -[@ota-meshi]: https://github.com/ota-meshi -[@OutdatedVersion]: https://github.com/OutdatedVersion -[@Pandemic1617]: https://github.com/Pandemic1617 -[@panrafal]: https://github.com/panrafal -[@paztis]: https://github.com/paztis -[@pcorpet]: https://github.com/pcorpet -[@Pearce-Ropion]: https://github.com/Pearce-Ropion -[@Pessimistress]: https://github.com/Pessimistress -[@pmcelhaney]: https://github.com/pmcelhaney -[@preco21]: https://github.com/preco21 -[@pri1311]: https://github.com/pri1311 -[@ProdigySim]: https://github.com/ProdigySim -[@pzhine]: https://github.com/pzhine -[@ramasilveyra]: https://github.com/ramasilveyra -[@randallreedjr]: https://github.com/randallreedjr -[@redbugz]: https://github.com/redbugz -[@remcohaszing]: https://github.com/remcohaszing -[@rfermann]: https://github.com/rfermann -[@rhettlivingston]: https://github.com/rhettlivingston -[@rhys-vdw]: https://github.com/rhys-vdw -[@richardxia]: https://github.com/richardxia -[@robertrossmann]: https://github.com/robertrossmann -[@rosswarren]: https://github.com/rosswarren -[@rperello]: https://github.com/rperello -[@rsolomon]: https://github.com/rsolomon -[@s-h-a-d-o-w]: https://github.com/s-h-a-d-o-w -[@saschanaz]: https://github.com/saschanaz -[@schmidsi]: https://github.com/schmidsi -[@schmod]: https://github.com/schmod -[@Schweinepriester]: https://github.com/Schweinepriester -[@scottnonnenberg]: https://github.com/scottnonnenberg -[@sergei-startsev]: https://github.com/sergei-startsev -[@sharmilajesupaul]: https://github.com/sharmilajesupaul -[@sheepsteak]: https://github.com/sheepsteak -[@silviogutierrez]: https://github.com/silviogutierrez -[@SimenB]: https://github.com/SimenB -[@simmo]: https://github.com/simmo -[@sindresorhus]: https://github.com/sindresorhus -[@singles]: https://github.com/singles -[@skozin]: https://github.com/skozin -[@skyrpex]: https://github.com/skyrpex -[@snewcomer]: https://github.com/snewcomer -[@sompylasar]: https://github.com/sompylasar -[@soryy708]: https://github.com/soryy708 -[@sosukesuzuki]: https://github.com/sosukesuzuki -[@spalger]: https://github.com/spalger -[@st-sloth]: https://github.com/st-sloth -[@stekycz]: https://github.com/stekycz -[@stenin-nikita]: https://github.com/stenin-nikita -[@stephtr]: https://github.com/stephtr -[@straub]: https://github.com/straub -[@strawbrary]: https://github.com/strawbrary -[@stropho]: https://github.com/stropho -[@sveyret]: https://github.com/sveyret -[@swernerx]: https://github.com/swernerx -[@syymza]: https://github.com/syymza -[@taion]: https://github.com/taion -[@TakeScoop]: https://github.com/TakeScoop -[@tapayne88]: https://github.com/tapayne88 -[@Taranys]: https://github.com/Taranys -[@taye]: https://github.com/taye -[@TheCrueltySage]: https://github.com/TheCrueltySage -[@TheJaredWilcurt]: https://github.com/TheJaredWilcurt -[@tihonove]: https://github.com/tihonove -[@timkraut]: https://github.com/timkraut -[@tizmagik]: https://github.com/tizmagik -[@tomprats]: https://github.com/tomprats -[@TrevorBurnham]: https://github.com/TrevorBurnham -[@ttmarek]: https://github.com/ttmarek -[@vikr01]: https://github.com/vikr01 -[@wenfangdu]: https://github.com/wenfangdu -[@wKich]: https://github.com/wKich -[@wschurman]: https://github.com/wschurman -[@wtgtybhertgeghgtwtg]: https://github.com/wtgtybhertgeghgtwtg -[@xM8WVqaG]: https://github.com/xM8WVqaG -[@xpl]: https://github.com/xpl -[@yndajas]: https://github.com/yndajas -[@yordis]: https://github.com/yordis -[@Zamiell]: https://github.com/Zamiell -[@zloirock]: https://github.com/zloirock diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 871e90ad8..4c3d4f95c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,15 +6,15 @@ Thanks for your interest in helping out! Here are a **few** _weird_ tricks to ~~ When opening an [issue](#issues): - - [ ] search open/closed issues - - [ ] discuss bug/enhancement in new or old issue +- [ ] search open/closed issues +- [ ] discuss bug/enhancement in new or old issue [PR](#prs) time: - - [ ] write tests - - [ ] implement feature/fix bug - - [ ] update docs - - [ ] make a note in change log +- [ ] write tests +- [ ] implement feature/fix bug +- [ ] update docs +- [ ] make a note in change log Remember, you don't need to do it all yourself; any of these are helpful! 😎 @@ -22,27 +22,27 @@ Remember, you don't need to do it all yourself; any of these are helpful! 😎 If you are new to `eslint`, below are a few resources that will help you to familiarize yourself with the project. - - Watch [this presentation](https://www.youtube.com/watch?v=2W9tUnALrLg) to learn the fundamental concept of Abstract Syntax Trees (AST) and the way `eslint` works under the hood. - - Familiarize yourself with the [AST explorer](https://astexplorer.net/) tool. Look into rules in `docs/rules`, create patterns in the rules, then analyze its AST. - - Explore the blog posts on how to create a custom rule. [One blog post](https://blog.yonatan.dev/writing-a-custom-eslint-rule-to-spot-undeclared-props/). [Second blog post](https://betterprogramming.pub/creating-custom-eslint-rules-cdc579694608). - - Read the official `eslint` [developer guide](https://eslint.org/docs/latest/developer-guide/architecture/). +- Watch [this presentation](https://www.youtube.com/watch?v=2W9tUnALrLg) to learn the fundamental concept of Abstract Syntax Trees (AST) and the way `eslint` works under the hood. +- Familiarize yourself with the [AST explorer](https://astexplorer.net/) tool. Look into rules in `docs/rules`, create patterns in the rules, then analyze its AST. +- Explore the blog posts on how to create a custom rule. [One blog post](https://blog.yonatan.dev/writing-a-custom-eslint-rule-to-spot-undeclared-props/). [Second blog post](https://betterprogramming.pub/creating-custom-eslint-rules-cdc579694608). +- Read the official `eslint` [developer guide](https://eslint.org/docs/latest/developer-guide/architecture/). ## Issues ### Search open + closed issues for similar cases - You may find an open issue that closely matches what you are thinking. You may also find a closed issue with discussion that either solves your problem or explains why we are unlikely to solve it in the near future. +You may find an open issue that closely matches what you are thinking. You may also find a closed issue with discussion that either solves your problem or explains why we are unlikely to solve it in the near future. - If you find a matching issue that is open, and marked `accepted` and/or `help wanted`, you might want to [open a PR](#prs). +If you find a matching issue that is open, and marked `accepted` and/or `help wanted`, you might want to [open a PR](#prs). ### Open an issue - Let's discuss your issue. Could be as simple as unclear documentation or a wonky config file. - If you're suggesting a feature, it might exist and need better documentation, or it might be in process. Even given those, some discussion might be warranted to ensure the enhancement is clear. +Let's discuss your issue. Could be as simple as unclear documentation or a wonky config file. +If you're suggesting a feature, it might exist and need better documentation, or it might be in process. Even given those, some discussion might be warranted to ensure the enhancement is clear. - You're welcome to jump right to a PR, but without a discussion, can't make any guarantees about merging. +You're welcome to jump right to a PR, but without a discussion, can't make any guarantees about merging. - That said: sometimes seeing the code makes the discussion clearer.😄 +That said: sometimes seeing the code makes the discussion clearer.😄 This is a helpful contribution all by itself. Thanks! diff --git a/README.md b/README.md index d6f107d1c..3ea588979 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# eslint-plugin-import +# eslint-plugin-import-x [![github actions][actions-image]][actions-url] [![travis-ci](https://travis-ci.org/import-js/eslint-plugin-import.svg?branch=main)](https://travis-ci.org/import-js/eslint-plugin-import) @@ -28,68 +28,68 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a ### Helpful warnings -| Name                       | Description | 💼 | ⚠️ | 🚫 | 🔧 | 💡 | ❌ | -| :--------------------------------------------------------------------- | :------------------------------------------------------------------------------------ | :--- | :---- | :- | :- | :- | :- | -| [export](docs/rules/export.md) | Forbid any invalid exports, i.e. re-export of the same name. | ❗ ☑️ | | | | | | -| [no-deprecated](docs/rules/no-deprecated.md) | Forbid imported names marked with `@deprecated` documentation tag. | | | | | | | -| [no-empty-named-blocks](docs/rules/no-empty-named-blocks.md) | Forbid empty named import blocks. | | | | 🔧 | 💡 | | -| [no-extraneous-dependencies](docs/rules/no-extraneous-dependencies.md) | Forbid the use of extraneous packages. | | | | | | | -| [no-mutable-exports](docs/rules/no-mutable-exports.md) | Forbid the use of mutable exports with `var` or `let`. | | | | | | | -| [no-named-as-default](docs/rules/no-named-as-default.md) | Forbid use of exported name as identifier of default export. | | ☑️ 🚸 | | | | | -| [no-named-as-default-member](docs/rules/no-named-as-default-member.md) | Forbid use of exported name as property of default export. | | ☑️ 🚸 | | | | | -| [no-unused-modules](docs/rules/no-unused-modules.md) | Forbid modules without exports, or exports without matching import in another module. | | | | | | | +| Name                       | Description | 💼 | ⚠️ | 🚫 | 🔧 | 💡 | ❌ | +| :--------------------------------------------------------------------- | :------------------------------------------------------------------------------------ | :---- | :---- | :-- | :-- | :-- | :-- | +| [export](docs/rules/export.md) | Forbid any invalid exports, i.e. re-export of the same name. | ❗ ☑️ | | | | | | +| [no-deprecated](docs/rules/no-deprecated.md) | Forbid imported names marked with `@deprecated` documentation tag. | | | | | | | +| [no-empty-named-blocks](docs/rules/no-empty-named-blocks.md) | Forbid empty named import blocks. | | | | 🔧 | 💡 | | +| [no-extraneous-dependencies](docs/rules/no-extraneous-dependencies.md) | Forbid the use of extraneous packages. | | | | | | | +| [no-mutable-exports](docs/rules/no-mutable-exports.md) | Forbid the use of mutable exports with `var` or `let`. | | | | | | | +| [no-named-as-default](docs/rules/no-named-as-default.md) | Forbid use of exported name as identifier of default export. | | ☑️ 🚸 | | | | | +| [no-named-as-default-member](docs/rules/no-named-as-default-member.md) | Forbid use of exported name as property of default export. | | ☑️ 🚸 | | | | | +| [no-unused-modules](docs/rules/no-unused-modules.md) | Forbid modules without exports, or exports without matching import in another module. | | | | | | | ### Module systems -| Name                     | Description | 💼 | ⚠️ | 🚫 | 🔧 | 💡 | ❌ | -| :----------------------------------------------------------------- | :------------------------------------------------------------------- | :- | :- | :- | :- | :- | :- | -| [no-amd](docs/rules/no-amd.md) | Forbid AMD `require` and `define` calls. | | | | | | | -| [no-commonjs](docs/rules/no-commonjs.md) | Forbid CommonJS `require` calls and `module.exports` or `exports.*`. | | | | | | | -| [no-import-module-exports](docs/rules/no-import-module-exports.md) | Forbid import statements with CommonJS module.exports. | | | | 🔧 | | | -| [no-nodejs-modules](docs/rules/no-nodejs-modules.md) | Forbid Node.js builtin modules. | | | | | | | -| [unambiguous](docs/rules/unambiguous.md) | Forbid potentially ambiguous parse goal (`script` vs. `module`). | | | | | | | +| Name                     | Description | 💼 | ⚠️ | 🚫 | 🔧 | 💡 | ❌ | +| :----------------------------------------------------------------- | :------------------------------------------------------------------- | :-- | :-- | :-- | :-- | :-- | :-- | +| [no-amd](docs/rules/no-amd.md) | Forbid AMD `require` and `define` calls. | | | | | | | +| [no-commonjs](docs/rules/no-commonjs.md) | Forbid CommonJS `require` calls and `module.exports` or `exports.*`. | | | | | | | +| [no-import-module-exports](docs/rules/no-import-module-exports.md) | Forbid import statements with CommonJS module.exports. | | | | 🔧 | | | +| [no-nodejs-modules](docs/rules/no-nodejs-modules.md) | Forbid Node.js builtin modules. | | | | | | | +| [unambiguous](docs/rules/unambiguous.md) | Forbid potentially ambiguous parse goal (`script` vs. `module`). | | | | | | | ### Static analysis -| Name                       | Description | 💼 | ⚠️ | 🚫 | 🔧 | 💡 | ❌ | -| :--------------------------------------------------------------------- | :----------------------------------------------------------------------------------- | :--- | :- | :- | :- | :- | :- | -| [default](docs/rules/default.md) | Ensure a default export is present, given a default import. | ❗ ☑️ | | | | | | -| [named](docs/rules/named.md) | Ensure named imports correspond to a named export in the remote file. | ❗ ☑️ | | ⌨️ | | | | -| [namespace](docs/rules/namespace.md) | Ensure imported namespaces contain dereferenced properties as they are dereferenced. | ❗ ☑️ | | | | | | -| [no-absolute-path](docs/rules/no-absolute-path.md) | Forbid import of modules using absolute paths. | | | | 🔧 | | | -| [no-cycle](docs/rules/no-cycle.md) | Forbid a module from importing a module with a dependency path back to itself. | | | | | | | -| [no-dynamic-require](docs/rules/no-dynamic-require.md) | Forbid `require()` calls with expressions. | | | | | | | -| [no-internal-modules](docs/rules/no-internal-modules.md) | Forbid importing the submodules of other modules. | | | | | | | -| [no-relative-packages](docs/rules/no-relative-packages.md) | Forbid importing packages through relative paths. | | | | 🔧 | | | -| [no-relative-parent-imports](docs/rules/no-relative-parent-imports.md) | Forbid importing modules from parent directories. | | | | | | | -| [no-restricted-paths](docs/rules/no-restricted-paths.md) | Enforce which files can be imported in a given folder. | | | | | | | -| [no-self-import](docs/rules/no-self-import.md) | Forbid a module from importing itself. | | | | | | | -| [no-unresolved](docs/rules/no-unresolved.md) | Ensure imports point to a file/module that can be resolved. | ❗ ☑️ | | | | | | -| [no-useless-path-segments](docs/rules/no-useless-path-segments.md) | Forbid unnecessary path segments in import and require statements. | | | | 🔧 | | | -| [no-webpack-loader-syntax](docs/rules/no-webpack-loader-syntax.md) | Forbid webpack loader syntax in imports. | | | | | | | +| Name                       | Description | 💼 | ⚠️ | 🚫 | 🔧 | 💡 | ❌ | +| :--------------------------------------------------------------------- | :----------------------------------------------------------------------------------- | :---- | :-- | :-- | :-- | :-- | :-- | +| [default](docs/rules/default.md) | Ensure a default export is present, given a default import. | ❗ ☑️ | | | | | | +| [named](docs/rules/named.md) | Ensure named imports correspond to a named export in the remote file. | ❗ ☑️ | | ⌨️ | | | | +| [namespace](docs/rules/namespace.md) | Ensure imported namespaces contain dereferenced properties as they are dereferenced. | ❗ ☑️ | | | | | | +| [no-absolute-path](docs/rules/no-absolute-path.md) | Forbid import of modules using absolute paths. | | | | 🔧 | | | +| [no-cycle](docs/rules/no-cycle.md) | Forbid a module from importing a module with a dependency path back to itself. | | | | | | | +| [no-dynamic-require](docs/rules/no-dynamic-require.md) | Forbid `require()` calls with expressions. | | | | | | | +| [no-internal-modules](docs/rules/no-internal-modules.md) | Forbid importing the submodules of other modules. | | | | | | | +| [no-relative-packages](docs/rules/no-relative-packages.md) | Forbid importing packages through relative paths. | | | | 🔧 | | | +| [no-relative-parent-imports](docs/rules/no-relative-parent-imports.md) | Forbid importing modules from parent directories. | | | | | | | +| [no-restricted-paths](docs/rules/no-restricted-paths.md) | Enforce which files can be imported in a given folder. | | | | | | | +| [no-self-import](docs/rules/no-self-import.md) | Forbid a module from importing itself. | | | | | | | +| [no-unresolved](docs/rules/no-unresolved.md) | Ensure imports point to a file/module that can be resolved. | ❗ ☑️ | | | | | | +| [no-useless-path-segments](docs/rules/no-useless-path-segments.md) | Forbid unnecessary path segments in import and require statements. | | | | 🔧 | | | +| [no-webpack-loader-syntax](docs/rules/no-webpack-loader-syntax.md) | Forbid webpack loader syntax in imports. | | | | | | | ### Style guide -| Name                            | Description | 💼 | ⚠️ | 🚫 | 🔧 | 💡 | ❌ | -| :------------------------------------------------------------------------------- | :------------------------------------------------------------------------- | :- | :---- | :- | :- | :- | :- | -| [consistent-type-specifier-style](docs/rules/consistent-type-specifier-style.md) | Enforce or ban the use of inline type-only markers for named imports. | | | | 🔧 | | | -| [dynamic-import-chunkname](docs/rules/dynamic-import-chunkname.md) | Enforce a leading comment with the webpackChunkName for dynamic imports. | | | | | | | -| [exports-last](docs/rules/exports-last.md) | Ensure all exports appear after other statements. | | | | | | | -| [extensions](docs/rules/extensions.md) | Ensure consistent use of file extension within the import path. | | | | | | | -| [first](docs/rules/first.md) | Ensure all imports appear before other statements. | | | | 🔧 | | | -| [group-exports](docs/rules/group-exports.md) | Prefer named exports to be grouped together in a single export declaration | | | | | | | -| [imports-first](docs/rules/imports-first.md) | Replaced by `import/first`. | | | | 🔧 | | ❌ | -| [max-dependencies](docs/rules/max-dependencies.md) | Enforce the maximum number of dependencies a module can have. | | | | | | | -| [newline-after-import](docs/rules/newline-after-import.md) | Enforce a newline after import statements. | | | | 🔧 | | | -| [no-anonymous-default-export](docs/rules/no-anonymous-default-export.md) | Forbid anonymous values as default exports. | | | | | | | -| [no-default-export](docs/rules/no-default-export.md) | Forbid default exports. | | | | | | | -| [no-duplicates](docs/rules/no-duplicates.md) | Forbid repeated import of the same module in multiple places. | | ☑️ 🚸 | | 🔧 | | | -| [no-named-default](docs/rules/no-named-default.md) | Forbid named default exports. | | | | | | | -| [no-named-export](docs/rules/no-named-export.md) | Forbid named exports. | | | | | | | -| [no-namespace](docs/rules/no-namespace.md) | Forbid namespace (a.k.a. "wildcard" `*`) imports. | | | | 🔧 | | | -| [no-unassigned-import](docs/rules/no-unassigned-import.md) | Forbid unassigned imports | | | | | | | -| [order](docs/rules/order.md) | Enforce a convention in module import order. | | | | 🔧 | | | -| [prefer-default-export](docs/rules/prefer-default-export.md) | Prefer a default export if module exports a single name or multiple names. | | | | | | | +| Name                            | Description | 💼 | ⚠️ | 🚫 | 🔧 | 💡 | ❌ | +| :------------------------------------------------------------------------------- | :------------------------------------------------------------------------- | :-- | :---- | :-- | :-- | :-- | :-- | +| [consistent-type-specifier-style](docs/rules/consistent-type-specifier-style.md) | Enforce or ban the use of inline type-only markers for named imports. | | | | 🔧 | | | +| [dynamic-import-chunkname](docs/rules/dynamic-import-chunkname.md) | Enforce a leading comment with the webpackChunkName for dynamic imports. | | | | | | | +| [exports-last](docs/rules/exports-last.md) | Ensure all exports appear after other statements. | | | | | | | +| [extensions](docs/rules/extensions.md) | Ensure consistent use of file extension within the import path. | | | | | | | +| [first](docs/rules/first.md) | Ensure all imports appear before other statements. | | | | 🔧 | | | +| [group-exports](docs/rules/group-exports.md) | Prefer named exports to be grouped together in a single export declaration | | | | | | | +| [imports-first](docs/rules/imports-first.md) | Replaced by `import-x/first`. | | | | 🔧 | | ❌ | +| [max-dependencies](docs/rules/max-dependencies.md) | Enforce the maximum number of dependencies a module can have. | | | | | | | +| [newline-after-import](docs/rules/newline-after-import.md) | Enforce a newline after import statements. | | | | 🔧 | | | +| [no-anonymous-default-export](docs/rules/no-anonymous-default-export.md) | Forbid anonymous values as default exports. | | | | | | | +| [no-default-export](docs/rules/no-default-export.md) | Forbid default exports. | | | | | | | +| [no-duplicates](docs/rules/no-duplicates.md) | Forbid repeated import of the same module in multiple places. | | ☑️ 🚸 | | 🔧 | | | +| [no-named-default](docs/rules/no-named-default.md) | Forbid named default exports. | | | | | | | +| [no-named-export](docs/rules/no-named-export.md) | Forbid named exports. | | | | | | | +| [no-namespace](docs/rules/no-namespace.md) | Forbid namespace (a.k.a. "wildcard" `*`) imports. | | | | 🔧 | | | +| [no-unassigned-import](docs/rules/no-unassigned-import.md) | Forbid unassigned imports | | | | | | | +| [order](docs/rules/order.md) | Enforce a convention in module import order. | | | | 🔧 | | | +| [prefer-default-export](docs/rules/prefer-default-export.md) | Prefer a default export if module exports a single name or multiple names. | | | | | | | @@ -113,21 +113,21 @@ in your `.eslintrc.(yml|json|js)`, or extend one of the canned configs: --- extends: - eslint:recommended - - plugin:import/recommended + - plugin:import-x/recommended # alternatively, 'recommended' is the combination of these two rule sets: - - plugin:import/errors - - plugin:import/warnings + - plugin:import-x/errors + - plugin:import-x/warnings # or configure manually: plugins: - import rules: - import/no-unresolved: [2, {commonjs: true, amd: true}] - import/named: 2 - import/namespace: 2 - import/default: 2 - import/export: 2 + import-x/no-unresolved: [2, { commonjs: true, amd: true }] + import-x/named: 2 + import-x/namespace: 2 + import-x/default: 2 + import-x/export: 2 # etc... ``` @@ -140,11 +140,11 @@ Make sure you have installed [`@typescript-eslint/parser`] and [`eslint-import-r ```yaml extends: - eslint:recommended - - plugin:import/recommended -# the following lines do the trick - - plugin:import/typescript + - plugin:import-x/recommended + # the following lines do the trick + - plugin:import-x/typescript settings: - import/resolver: + import-x/resolver: # You will also need to install and configure the TypeScript resolver # See also https://github.com/import-js/eslint-import-resolver-typescript#configuration typescript: true @@ -175,55 +175,55 @@ resolvers are just npm packages, so [third party packages are supported](https:/ You can reference resolvers in several ways (in order of precedence): - - as a conventional `eslint-import-resolver` name, like `eslint-import-resolver-foo`: +- as a conventional `eslint-import-resolver` name, like `eslint-import-resolver-foo`: ```yaml # .eslintrc.yml settings: # uses 'eslint-import-resolver-foo': - import/resolver: foo + import-x/resolver: foo ``` ```js // .eslintrc.js module.exports = { settings: { - 'import/resolver': { - foo: { someConfig: value } - } - } + 'import-x/resolver': { + foo: { someConfig: value }, + }, + }, } ``` - - with a full npm module name, like `my-awesome-npm-module`: +- with a full npm module name, like `my-awesome-npm-module`: ```yaml # .eslintrc.yml settings: - import/resolver: 'my-awesome-npm-module' + import-x/resolver: 'my-awesome-npm-module' ``` ```js // .eslintrc.js module.exports = { settings: { - 'import/resolver': { - 'my-awesome-npm-module': { someConfig: value } - } - } + 'import-x/resolver': { + 'my-awesome-npm-module': { someConfig: value }, + }, + }, } ``` - - with a filesystem path to resolver, defined in this example as a `computed property` name: +- with a filesystem path to resolver, defined in this example as a `computed property` name: ```js // .eslintrc.js module.exports = { settings: { - 'import/resolver': { - [path.resolve('../../../my-resolver')]: { someConfig: value } - } - } + 'import-x/resolver': { + [path.resolve('../../../my-resolver')]: { someConfig: value }, + }, + }, } ``` @@ -234,7 +234,6 @@ If you are interesting in writing a resolver, see the [spec](./resolvers/README. [`resolve`]: https://www.npmjs.com/package/resolve [`externals`]: https://webpack.github.io/docs/library-and-externals.html - [Node]: https://www.npmjs.com/package/eslint-import-resolver-node [webpack]: https://www.npmjs.com/package/eslint-import-resolver-webpack @@ -242,20 +241,20 @@ If you are interesting in writing a resolver, see the [spec](./resolvers/README. You may set the following settings in your `.eslintrc`: -### `import/extensions` +### `import-x/extensions` A list of file extensions that will be parsed as modules and inspected for `export`s. This defaults to `['.js']`, unless you are using the `react` shared config, in which case it is specified as `['.js', '.jsx']`. Despite the default, -if you are using TypeScript (without the `plugin:import/typescript` config +if you are using TypeScript (without the `plugin:import-x/typescript` config described above) you must specify the new extensions (`.ts`, and also `.tsx` if using React). ```js "settings": { - "import/extensions": [ + "import-x/extensions": [ ".js", ".jsx" ] @@ -266,7 +265,7 @@ If you require more granular extension definitions, you can use: ```js "settings": { - "import/resolver": { + "import-x/resolver": { "node": { "extensions": [ ".js", @@ -277,13 +276,13 @@ If you require more granular extension definitions, you can use: } ``` -Note that this is different from (and likely a subset of) any `import/resolver` +Note that this is different from (and likely a subset of) any `import-x/resolver` extensions settings, which may include `.json`, `.coffee`, etc. which will still factor into the `no-unresolved` rule. -Also, the following `import/ignore` patterns will overrule this list. +Also, the following `import-x/ignore` patterns will overrule this list. -### `import/ignore` +### `import-x/ignore` A list of regex strings that, if matched by a path, will not report the matching module if no `export`s are found. @@ -294,12 +293,12 @@ In practice, this means rules other than [`no-unresolved`](./docs/rules/no-unres ```yaml settings: - import/ignore: - - \.coffee$ # fraught with parse errors + import-x/ignore: + - \.coffee$ # fraught with parse errors - \.(scss|less|css)$ # can't parse unprocessed CSS modules, either ``` -### `import/core-modules` +### `import-x/core-modules` An array of additional modules to consider as "core" modules--modules that should be considered resolved but have no path on the filesystem. Your resolver may @@ -309,7 +308,7 @@ already define some of these (for example, the Node resolver knows about `fs` an For example, Electron exposes an `electron` module: ```js -import 'electron' // without extra config, will be flagged as unresolved! +import 'electron' // without extra config, will be flagged as unresolved! ``` that would otherwise be unresolved. To avoid this, you may provide `electron` as a @@ -318,7 +317,7 @@ core module: ```yaml # .eslintrc.yml settings: - import/core-modules: [ electron ] + import-x/core-modules: [electron] ``` In Electron's specific case, there is a shared config named `electron` @@ -326,7 +325,7 @@ that specifies this for you. Contribution of more such shared configs for other platforms are welcome! -### `import/external-module-folders` +### `import-x/external-module-folders` An array of folders. Resolved modules only from those folders will be considered as "external". By default - `["node_modules"]`. Makes sense if you have configured your path or webpack to handle your internal paths differently and want to consider modules from some folders, for example `bower_components` or `jspm_modules`, as "external". @@ -336,15 +335,15 @@ If you are using `yarn` PnP as your package manager, add the `.yarn` folder and Each item in this array is either a folder's name, its subpath, or its absolute prefix path: - - `jspm_modules` will match any file or folder named `jspm_modules` or which has a direct or non-direct parent named `jspm_modules`, e.g. `/home/me/project/jspm_modules` or `/home/me/project/jspm_modules/some-pkg/index.js`. +- `jspm_modules` will match any file or folder named `jspm_modules` or which has a direct or non-direct parent named `jspm_modules`, e.g. `/home/me/project/jspm_modules` or `/home/me/project/jspm_modules/some-pkg/index.js`. - - `packages/core` will match any path that contains these two segments, for example `/home/me/project/packages/core/src/utils.js`. +- `packages/core` will match any path that contains these two segments, for example `/home/me/project/packages/core/src/utils.js`. - - `/home/me/project/packages` will only match files and directories inside this directory, and the directory itself. +- `/home/me/project/packages` will only match files and directories inside this directory, and the directory itself. Please note that incomplete names are not allowed here so `components` won't match `bower_components` and `packages/ui` won't match `packages/ui-utils` (but will match `packages/ui/utils`). -### `import/parsers` +### `import-x/parsers` A map from parsers to file extension arrays. If a file extension is matched, the dependency parser will require and use the map key as the parser instead of the @@ -354,8 +353,8 @@ directly using webpack, for example: ```yaml # .eslintrc.yml settings: - import/parsers: - "@typescript-eslint/parser": [ .ts, .tsx ] + import-x/parsers: + '@typescript-eslint/parser': [.ts, .tsx] ``` In this case, [`@typescript-eslint/parser`](https://www.npmjs.com/package/@typescript-eslint/parser) @@ -371,11 +370,11 @@ depending on how far down the rabbit hole goes. Submit an issue if you find stra behavior beyond here, but steel your heart against the likely outcome of closing with `wontfix`. -### `import/resolver` +### `import-x/resolver` See [resolvers](#resolvers). -### `import/cache` +### `import-x/cache` Settings for cache behavior. Memoization is used at various levels to avoid the copious amount of `fs.statSync`/module parse calls required to correctly report errors. @@ -388,8 +387,8 @@ If you never use [`eslint_d`] or [`eslint-loader`], you may set the cache lifeti ```yaml # .eslintrc.yml settings: - import/cache: - lifetime: ∞ # or Infinity + import-x/cache: + lifetime: ∞ # or Infinity ``` Otherwise, set some integer, and cache entries will be evicted after that many seconds have elapsed: @@ -397,25 +396,25 @@ Otherwise, set some integer, and cache entries will be evicted after that many s ```yaml # .eslintrc.yml settings: - import/cache: - lifetime: 5 # 30 is the default + import-x/cache: + lifetime: 5 # 30 is the default ``` [`eslint_d`]: https://www.npmjs.com/package/eslint_d [`eslint-loader`]: https://www.npmjs.com/package/eslint-loader -### `import/internal-regex` +### `import-x/internal-regex` A regex for packages should be treated as internal. Useful when you are utilizing a monorepo setup or developing a set of packages that depend on each other. -By default, any package referenced from [`import/external-module-folders`](#importexternal-module-folders) will be considered as "external", including packages in a monorepo like yarn workspace or lerna environment. If you want to mark these packages as "internal" this will be useful. +By default, any package referenced from [`import-x/external-module-folders`](#importexternal-module-folders) will be considered as "external", including packages in a monorepo like yarn workspace or lerna environment. If you want to mark these packages as "internal" this will be useful. -For example, if your packages in a monorepo are all in `@scope`, you can configure `import/internal-regex` like this +For example, if your packages in a monorepo are all in `@scope`, you can configure `import-x/internal-regex` like this ```yaml # .eslintrc.yml settings: - import/internal-regex: ^@scope/ + import-x/internal-regex: ^@scope/ ``` ## SublimeLinter-eslint @@ -436,22 +435,18 @@ for more details and discussion, but essentially, you may find you need to add t ```json { - "folders": - [ - { - "path": "code" - } - ], - "SublimeLinter": + "folders": [ { - "linters": - { - "eslint": - { - "chdir": "${project}/code" - } - } + "path": "code" } + ], + "SublimeLinter": { + "linters": { + "eslint": { + "chdir": "${project}/code" + } + } + } } ``` diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index 6c048dc09..000000000 --- a/RELEASE.md +++ /dev/null @@ -1,54 +0,0 @@ -# Release steps - -1. create a `release-[x.y.z]` branch from tip of `main` (or whatever release commit) - - ```bash - git checkout main && git pull && git checkout -b release-2.1.0 - ``` - -2. bump `package.json` + update CHANGELOG version links for all releasing packages (i.e., root + any resolvers) - - In changelog for core plugin, normally leave [Unreleased] but update its link at the bottom - to be rooted at the new version's tag, and add a link for the new version rooted - at last version's tag. - - ```markdown - [Unreleased]: https://github.com/import-js/eslint-plugin-import/compare/v2.0.1...HEAD - [2.0.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.0.0...v2.0.1 - ``` - - becomes - - ```markdown - [Unreleased]: https://github.com/import-js/eslint-plugin-import/compare/v2.1.0...HEAD - [2.1.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.0.1...v2.1.0 - [2.0.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.0.0...v2.0.1 - ``` - - Generally, don't use `npm version` for this because it creates a tag, which I normally - wait until signoff from contributors and actually `npm publish`-ing to snap the tag. - -3. create pull request from `release-[x.y.z]` into `release` branch - - I like this because it - - lists all commits in the release - - provides a commentary location to discuss the release - - builds in CI and provides test results - -4. iterate on feedback - - handle other issues - - merge more PRs - - fix issues in changelog/docs - -5. `npm publish` from `release-[x.y.z]` branch - - don't forget resolvers! - -6. tag commit (`v[x.y.z]`) - - again, not forgetting resolvers, if needed (`resolvers/[name]/v[t.u.v]`) - -7. merge `release-[x.y.z]` into `release` ( - - ideally fast-forward, probably with Git CLI instead of Github - -8. merge `release` into `main` - -Done! diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index b155f54d5..000000000 --- a/SECURITY.md +++ /dev/null @@ -1,11 +0,0 @@ -# Security Policy - -## Supported Versions - -Latest major/minor version is supported only for security updates. - -## Reporting a Vulnerability - -To report a security vulnerability, please use the -[Tidelift security contact](https://tidelift.com/security). -Tidelift will coordinate the fix and disclosure. diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index e50ab87d2..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,165 +0,0 @@ -configuration: - - Native - - WSL - -# Test against this version of Node.js -environment: - matrix: - - nodejs_version: "16" - - nodejs_version: "14" - - nodejs_version: "12" - - nodejs_version: "10" - - nodejs_version: "8" - # - nodejs_version: "6" - # - nodejs_version: "4" - -image: Visual Studio 2019 -matrix: - fast_finish: false - exclude: - - configuration: WSL - nodejs_version: "8" - - configuration: WSL - nodejs_version: "6" - - configuration: WSL - nodejs_version: "4" - - allow_failures: - - nodejs_version: "4" # for eslint 5 - - configuration: WSL - -platform: - - x86 - - x64 - -# Initialization scripts. (runs before repo cloning) -init: - # Declare version-numbers of packages to install - - ps: >- - if ($env:nodejs_version -eq "4") { - $env:NPM_VERSION="3" - } - if ($env:nodejs_version -in @("8")) { - $env:NPM_VERSION="6" - } - if ($env:nodejs_version -in @("10", "12", "14", "16")) { - $env:NPM_VERSION="6" # TODO: use npm 7 - $env:NPM_CONFIG_LEGACY_PEER_DEPS="true" - } - - ps: >- - $env:ESLINT_VERSION="7"; - if ([int]$env:nodejs_version -le 8) { - $env:ESLINT_VERSION="6" - } - if ([int]$env:nodejs_version -le 7) { - $env:ESLINT_VERSION="5" - } - if ([int]$env:nodejs_version -le 6) { - $env:ESLINT_VERSION="4" - } - - ps: $env:WINDOWS_NYC_VERSION = "15.0.1" - - ps: $env:TRAVIS_NODE_VERSION = $env:nodejs_version - - # Add `ci`-command to `PATH` for running commands either using cmd or wsl depending on the configuration - - ps: $env:PATH += ";$(Join-Path $(pwd) "scripts")" - -# Install scripts. (runs after repo cloning) -before_build: - # Install propert `npm`-version - - IF DEFINED NPM_VERSION ci sudo npm install -g npm@%NPM_VERSION% - - # Install dependencies - - ci npm install - - ci npm run copy-metafiles - - bash ./tests/dep-time-travel.sh 2>&1 - - # fix symlinks - - git config core.symlinks true - - git reset --hard - - ci git reset --hard - - # Install dependencies of resolvers - - ps: >- - $resolverDir = "./resolvers"; - $resolvers = @(); - Get-ChildItem -Directory $resolverDir | - ForEach { - $resolvers += "$(Resolve-Path $(Join-Path $resolverDir $_))"; - } - $env:RESOLVERS = [string]::Join(";", $resolvers); - - FOR %%G in ("%RESOLVERS:;=";"%") do ( pushd %%~G & ci npm install & popd ) - - # Install proper `eslint`-version - - IF DEFINED ESLINT_VERSION ci npm install --no-save eslint@%ESLINT_VERSION% - -# Build scripts (project isn't actually built) -build_script: - - ps: "# This Project isn't actually built" - -# Test scripts -test_script: - # Output useful info for debugging. - - ci node --version - - ci npm --version - - # Run core tests - - ci npm run pretest - - ci npm run tests-only - - # Run resolver tests - - ps: >- - $resolverDir = "./resolvers"; - $resolvers = @(); - Get-ChildItem -Directory $resolverDir | - ForEach { - $resolvers += "$(Resolve-Path $(Join-Path $resolverDir $_))"; - } - $env:RESOLVERS = [string]::Join(";", $resolvers); - - FOR %%G in ("%RESOLVERS:;=";"%") do ( pushd %%~G & ci npm test & popd ) - -# Configuration-specific steps -for: - - matrix: - except: - - configuration: WSL - install: - # Get the latest stable version of Node.js or io.js - - ps: Install-Product node $env:nodejs_version - before_test: - # Upgrade nyc - - ci npm i --no-save nyc@%WINDOWS_NYC_VERSION% - - ps: >- - $resolverDir = "./resolvers"; - $resolvers = @(); - Get-ChildItem -Directory $resolverDir | - ForEach { - Push-Location $(Resolve-Path $(Join-Path $resolverDir $_)); - ci npm ls nyc > $null; - if ($?) { - $resolvers += "$(pwd)"; - } - Pop-Location; - } - $env:RESOLVERS = [string]::Join(";", $resolvers); - - IF DEFINED RESOLVERS FOR %%G in ("%RESOLVERS:;=";"%") do ( pushd %%~G & ci npm install --no-save nyc@%WINDOWS_NYC_VERSION% & popd ) - # TODO: enable codecov for native windows builds - #on_success: - #- ci $ProgressPreference = 'SilentlyContinue' - #- ci Invoke-WebRequest -Uri https://uploader.codecov.io/latest/windows/codecov.exe - #- ci -Outfile codecov.exe - #- ci .\codecov.exe - - matrix: - only: - - configuration: WSL - # Install scripts. (runs after repo cloning) - install: - # Get a specific version of Node.js - - ps: $env:WSLENV += ":nodejs_version" - - ps: wsl curl -sL 'https://deb.nodesource.com/setup_${nodejs_version}.x' `| sudo APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=1 -E bash - - - wsl sudo DEBIAN_FRONTEND=noninteractive apt install -y nodejs - on_success: - - ci curl -Os https://uploader.codecov.io/latest/linux/codecov - - ci chmod +x codecov - - ci ./codecov - -build: on diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 000000000..0ebcb629d --- /dev/null +++ b/babel.config.js @@ -0,0 +1,25 @@ +module.exports = { + presets: [ + [ + '@babel/env', + { + targets: { + node: 4, + }, + }, + ], + '@babel/flow', + '@babel/react', + ], + plugins: [ + [ + '@babel/proposal-decorators', + { + version: 'legacy', + }, + ], + '@babel/proposal-export-default-from', + ], + sourceMaps: 'inline', + retainLines: true, +} diff --git a/config/electron.js b/config/electron.js index f98ff0614..e1d9833db 100644 --- a/config/electron.js +++ b/config/electron.js @@ -3,6 +3,6 @@ */ module.exports = { settings: { - 'import/core-modules': ['electron'], + 'import-x/core-modules': ['electron'], }, -}; +} diff --git a/config/errors.js b/config/errors.js index 127c29a0c..3bdc7399f 100644 --- a/config/errors.js +++ b/config/errors.js @@ -4,11 +4,12 @@ * @type {Object} */ module.exports = { - plugins: ['import'], - rules: { 'import/no-unresolved': 2, - 'import/named': 2, - 'import/namespace': 2, - 'import/default': 2, - 'import/export': 2, + plugins: ['import-x'], + rules: { + 'import-x/no-unresolved': 2, + 'import-x/named': 2, + 'import-x/namespace': 2, + 'import-x/default': 2, + 'import-x/export': 2, }, -}; +} diff --git a/config/react-native.js b/config/react-native.js index a1aa0ee56..ce5535223 100644 --- a/config/react-native.js +++ b/config/react-native.js @@ -3,11 +3,11 @@ */ module.exports = { settings: { - 'import/resolver': { + 'import-x/resolver': { node: { // Note: will not complain if only _one_ of these files exists. extensions: ['.js', '.web.js', '.ios.js', '.android.js'], }, }, }, -}; +} diff --git a/config/react.js b/config/react.js index 68555512d..ed7376dd8 100644 --- a/config/react.js +++ b/config/react.js @@ -6,13 +6,11 @@ * if you don't enable these settings at the top level. */ module.exports = { - settings: { - 'import/extensions': ['.js', '.jsx'], + 'import-x/extensions': ['.js', '.jsx'], }, parserOptions: { ecmaFeatures: { jsx: true }, }, - -}; +} diff --git a/config/recommended.js b/config/recommended.js index 8e7ca9fd0..b20d943ac 100644 --- a/config/recommended.js +++ b/config/recommended.js @@ -3,20 +3,20 @@ * @type {Object} */ module.exports = { - plugins: ['import'], + plugins: ['import-x'], rules: { // analysis/correctness - 'import/no-unresolved': 'error', - 'import/named': 'error', - 'import/namespace': 'error', - 'import/default': 'error', - 'import/export': 'error', + 'import-x/no-unresolved': 'error', + 'import-x/named': 'error', + 'import-x/namespace': 'error', + 'import-x/default': 'error', + 'import-x/export': 'error', // red flags (thus, warnings) - 'import/no-named-as-default': 'warn', - 'import/no-named-as-default-member': 'warn', - 'import/no-duplicates': 'warn', + 'import-x/no-named-as-default': 'warn', + 'import-x/no-named-as-default-member': 'warn', + 'import-x/no-duplicates': 'warn', }, // need all these for parsing dependencies (even if _your_ code doesn't need @@ -25,4 +25,4 @@ module.exports = { sourceType: 'module', ecmaVersion: 2018, }, -}; +} diff --git a/config/stage-0.js b/config/stage-0.js index 42419123f..a02945158 100644 --- a/config/stage-0.js +++ b/config/stage-0.js @@ -5,8 +5,8 @@ * @type {Object} */ module.exports = { - plugins: ['import'], + plugins: ['import-x'], rules: { - 'import/no-deprecated': 1, + 'import-x/no-deprecated': 1, }, -}; +} diff --git a/config/typescript.js b/config/typescript.js index ff7d0795c..f1ca42609 100644 --- a/config/typescript.js +++ b/config/typescript.js @@ -7,18 +7,18 @@ // Omit `.d.ts` because 1) TypeScript compilation already confirms that // types are resolved, and 2) it would mask an unresolved // `.ts`/`.tsx`/`.js`/`.jsx` implementation. -const typeScriptExtensions = ['.ts', '.cts', '.mts', '.tsx']; +const typeScriptExtensions = ['.ts', '.cts', '.mts', '.tsx'] -const allExtensions = [...typeScriptExtensions, '.js', '.jsx']; +const allExtensions = [...typeScriptExtensions, '.js', '.jsx'] module.exports = { settings: { - 'import/extensions': allExtensions, - 'import/external-module-folders': ['node_modules', 'node_modules/@types'], - 'import/parsers': { + 'import-x/extensions': allExtensions, + 'import-x/external-module-folders': ['node_modules', 'node_modules/@types'], + 'import-x/parsers': { '@typescript-eslint/parser': typeScriptExtensions, }, - 'import/resolver': { + 'import-x/resolver': { node: { extensions: allExtensions, }, @@ -29,6 +29,6 @@ module.exports = { // analysis/correctness // TypeScript compilation already ensures that named imports exist in the referenced module - 'import/named': 'off', + 'import-x/named': 'off', }, -}; +} diff --git a/config/warnings.js b/config/warnings.js index 5d74143b2..b323885c7 100644 --- a/config/warnings.js +++ b/config/warnings.js @@ -3,10 +3,10 @@ * @type {Object} */ module.exports = { - plugins: ['import'], + plugins: ['import-x'], rules: { - 'import/no-named-as-default': 1, - 'import/no-named-as-default-member': 1, - 'import/no-duplicates': 1, + 'import-x/no-named-as-default': 1, + 'import-x/no-named-as-default-member': 1, + 'import-x/no-duplicates': 1, }, -}; +} diff --git a/docs/rules/consistent-type-specifier-style.md b/docs/rules/consistent-type-specifier-style.md index 41d98e4e1..11beda2eb 100644 --- a/docs/rules/consistent-type-specifier-style.md +++ b/docs/rules/consistent-type-specifier-style.md @@ -1,4 +1,4 @@ -# import/consistent-type-specifier-style +# import-x/consistent-type-specifier-style 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). @@ -29,16 +29,16 @@ import {typeof Bar} from 'Bar'; This rule either enforces or bans the use of inline type-only markers for named imports. -This rule includes a fixer that will automatically convert your specifiers to the correct form - however the fixer will not respect your preferences around de-duplicating imports. If this is important to you, consider using the [`import/no-duplicates`] rule. +This rule includes a fixer that will automatically convert your specifiers to the correct form - however the fixer will not respect your preferences around de-duplicating imports. If this is important to you, consider using the [`import-x/no-duplicates`] rule. -[`import/no-duplicates`]: ./no-duplicates.md +[`import-x/no-duplicates`]: ./no-duplicates.md ## Options The rule accepts a single string option which may be one of: - - `'prefer-inline'` - enforces that named type-only specifiers are only ever written with an inline marker; and never as part of a top-level, type-only import. - - `'prefer-top-level'` - enforces that named type-only specifiers only ever written as part of a top-level, type-only import; and never with an inline marker. +- `'prefer-inline'` - enforces that named type-only specifiers are only ever written with an inline marker; and never as part of a top-level, type-only import. +- `'prefer-top-level'` - enforces that named type-only specifiers only ever written as part of a top-level, type-only import; and never with an inline marker. By default the rule will use the `prefer-inline` option. diff --git a/docs/rules/default.md b/docs/rules/default.md index 9f8c919bf..bd6eb7f4c 100644 --- a/docs/rules/default.md +++ b/docs/rules/default.md @@ -1,4 +1,4 @@ -# import/default +# import-x/default 💼 This rule is enabled in the following configs: ❗ `errors`, ☑️ `recommended`. @@ -25,16 +25,24 @@ Given: ```js // ./foo.js -export default function () { return 42 } +export default function () { + return 42 +} // ./bar.js -export function bar() { return null } +export function bar() { + return null +} // ./baz.js -module.exports = function () { /* ... */ } +module.exports = function () { + /* ... */ +} // node_modules/some-module/index.js -exports.sharedFunction = function shared() { /* ... */ } +exports.sharedFunction = function shared() { + /* ... */ +} ``` The following is considered valid: @@ -63,10 +71,10 @@ either, so such a situation will be reported in the importing module. ## Further Reading - - Lee Byron's [ES7] export proposal - - [`import/ignore`] setting - - [`jsnext:main`] (Rollup) +- Lee Byron's [ES7] export proposal +- [`import-x/ignore`] setting +- [`jsnext:main`] (Rollup) [ES7]: https://github.com/leebyron/ecmascript-more-export-from -[`import/ignore`]: ../../README.md#importignore +[`import-x/ignore`]: ../../README.md#importignore [`jsnext:main`]: https://github.com/rollup/rollup/wiki/jsnext:main diff --git a/docs/rules/dynamic-import-chunkname.md b/docs/rules/dynamic-import-chunkname.md index dd526c891..401938d2b 100644 --- a/docs/rules/dynamic-import-chunkname.md +++ b/docs/rules/dynamic-import-chunkname.md @@ -1,4 +1,4 @@ -# import/dynamic-import-chunkname +# import-x/dynamic-import-chunkname @@ -11,13 +11,13 @@ This rule enforces naming of webpack chunks in dynamic imports. When you don't e This rule runs against `import()` by default, but can be configured to also run against an alternative dynamic-import function, e.g. 'dynamicImport.' You can also configure the regex format you'd like to accept for the webpackChunkName - for example, if we don't want the number 6 to show up in our chunk names: - ```javascript +```javascript { - "dynamic-import-chunkname": [2, { - importFunctions: ["dynamicImport"], - webpackChunknameFormat: "[a-zA-Z0-57-9-/_]+", - allowEmpty: false - }] + "dynamic-import-chunkname": [2, { + importFunctions: ["dynamicImport"], + webpackChunknameFormat: "[a-zA-Z0-57-9-/_]+", + allowEmpty: false + }] } ``` @@ -27,35 +27,35 @@ The following patterns are invalid: ```javascript // no leading comment -import('someModule'); +import('someModule') // incorrectly formatted comment import( /*webpackChunkName:"someModule"*/ - 'someModule', -); + 'someModule' +) import( /* webpackChunkName : "someModule" */ - 'someModule', -); + 'someModule' +) // chunkname contains a 6 (forbidden by rule config) import( /* webpackChunkName: "someModule6" */ - 'someModule', -); + 'someModule' +) // invalid syntax for webpack comment import( /* totally not webpackChunkName: "someModule" */ - 'someModule', -); + 'someModule' +) // single-line comment, not a block-style comment import( // webpackChunkName: "someModule" - 'someModule', -); + 'someModule' +) ``` ### valid @@ -63,29 +63,29 @@ import( The following patterns are valid: ```javascript - import( - /* webpackChunkName: "someModule" */ - 'someModule', - ); - import( - /* webpackChunkName: "someOtherModule12345789" */ - 'someModule', - ); - import( - /* webpackChunkName: "someModule" */ - /* webpackPrefetch: true */ - 'someModule', - ); - import( - /* webpackChunkName: "someModule", webpackPrefetch: true */ - 'someModule', - ); - - // using single quotes instead of double quotes - import( - /* webpackChunkName: 'someModule' */ - 'someModule', - ); +import( + /* webpackChunkName: "someModule" */ + 'someModule' +) +import( + /* webpackChunkName: "someOtherModule12345789" */ + 'someModule' +) +import( + /* webpackChunkName: "someModule" */ + /* webpackPrefetch: true */ + 'someModule' +) +import( + /* webpackChunkName: "someModule", webpackPrefetch: true */ + 'someModule' +) + +// using single quotes instead of double quotes +import( + /* webpackChunkName: 'someModule' */ + 'someModule' +) ``` ### `allowEmpty: true` @@ -95,19 +95,22 @@ If you want to allow dynamic imports without a webpackChunkName, you can set `al Given `{ "allowEmpty": true }`: + ### valid The following patterns are valid: ```javascript -import('someModule'); +import('someModule') import( /* webpackChunkName: "someModule" */ - 'someModule', -); + 'someModule' +) ``` + + ### invalid The following patterns are invalid: @@ -116,8 +119,8 @@ The following patterns are invalid: // incorrectly formatted comment import( /*webpackChunkName:"someModule"*/ - 'someModule', -); + 'someModule' +) ``` ## When Not To Use It diff --git a/docs/rules/export.md b/docs/rules/export.md index 54a8a39cf..afce8af7c 100644 --- a/docs/rules/export.md +++ b/docs/rules/export.md @@ -1,4 +1,4 @@ -# import/export +# import-x/export 💼 This rule is enabled in the following configs: ❗ `errors`, ☑️ `recommended`. @@ -19,9 +19,13 @@ export default makeClass // Multiple default exports. or ```js -export const foo = function () { /*...*/ } // Multiple exports of name 'foo'. +export const foo = function () { + /*...*/ +} // Multiple exports of name 'foo'. -function bar() { /*...*/ } +function bar() { + /*...*/ +} export { bar as foo } // Multiple exports of name 'foo'. ``` @@ -32,6 +36,6 @@ intent to rename, etc. ## Further Reading - - Lee Byron's [ES7] export proposal +- Lee Byron's [ES7] export proposal [ES7]: https://github.com/leebyron/ecmascript-more-export-from diff --git a/docs/rules/exports-last.md b/docs/rules/exports-last.md index 56e947e94..085c6d56d 100644 --- a/docs/rules/exports-last.md +++ b/docs/rules/exports-last.md @@ -1,4 +1,4 @@ -# import/exports-last +# import-x/exports-last diff --git a/docs/rules/extensions.md b/docs/rules/extensions.md index 946ccb7bf..2242a57c6 100644 --- a/docs/rules/extensions.md +++ b/docs/rules/extensions.md @@ -1,4 +1,4 @@ -# import/extensions +# import-x/extensions @@ -11,23 +11,23 @@ In order to provide a consistent use of file extensions across your code base, t This rule either takes one string option, one object option, or a string and an object option. If it is the string `"never"` (the default value), then the rule forbids the use for any extension. If it is the string `"always"`, then the rule enforces the use of extensions for all import statements. If it is the string `"ignorePackages"`, then the rule enforces the use of extensions for all import statements except package imports. ```json -"import/extensions": [, "never" | "always" | "ignorePackages"] +"import-x/extensions": [, "never" | "always" | "ignorePackages"] ``` By providing an object you can configure each extension separately. ```json -"import/extensions": [, { +"import-x/extensions": [, { : "never" | "always" | "ignorePackages" }] ``` - For example `{ "js": "always", "json": "never" }` would always enforce the use of the `.js` extension but never allow the use of the `.json` extension. +For example `{ "js": "always", "json": "never" }` would always enforce the use of the `.js` extension but never allow the use of the `.json` extension. By providing both a string and an object, the string will set the default setting for all extensions, and the object can be used to set granular overrides for specific extensions. ```json -"import/extensions": [ +"import-x/extensions": [ , "never" | "always" | "ignorePackages", { @@ -41,7 +41,7 @@ For example, `["error", "never", { "svg": "always" }]` would require that all ex `ignorePackages` can be set as a separate boolean option like this: ```json -"import/extensions": [ +"import-x/extensions": [ , "never" | "always" | "ignorePackages", { @@ -71,7 +71,7 @@ For example, given the following folder structure: and this import statement: ```js -import bar from './foo/bar.json'; +import bar from './foo/bar.json' ``` then the extension can’t be omitted because it would then resolve to `./foo/bar.js`. @@ -81,76 +81,75 @@ then the extension can’t be omitted because it would then resolve to `./foo/ba The following patterns are considered problems when configuration set to "never": ```js -import foo from './foo.js'; +import foo from './foo.js' -import bar from './bar.json'; +import bar from './bar.json' -import Component from './Component.jsx'; +import Component from './Component.jsx' -import express from 'express/index.js'; +import express from 'express/index.js' ``` The following patterns are not considered problems when configuration set to "never": ```js -import foo from './foo'; +import foo from './foo' -import bar from './bar'; +import bar from './bar' -import Component from './Component'; +import Component from './Component' -import express from 'express/index'; +import express from 'express/index' -import * as path from 'path'; +import * as path from 'path' ``` The following patterns are considered problems when configuration set to "always": ```js -import foo from './foo'; +import foo from './foo' -import bar from './bar'; +import bar from './bar' -import Component from './Component'; +import Component from './Component' -import foo from '@/foo'; +import foo from '@/foo' ``` The following patterns are not considered problems when configuration set to "always": ```js -import foo from './foo.js'; +import foo from './foo.js' -import bar from './bar.json'; +import bar from './bar.json' -import Component from './Component.jsx'; +import Component from './Component.jsx' -import * as path from 'path'; +import * as path from 'path' -import foo from '@/foo.js'; +import foo from '@/foo.js' ``` The following patterns are considered problems when configuration set to "ignorePackages": ```js -import foo from './foo'; - -import bar from './bar'; +import foo from './foo' -import Component from './Component'; +import bar from './bar' +import Component from './Component' ``` The following patterns are not considered problems when configuration set to "ignorePackages": ```js -import foo from './foo.js'; +import foo from './foo.js' -import bar from './bar.json'; +import bar from './bar.json' -import Component from './Component.jsx'; +import Component from './Component.jsx' -import express from 'express'; +import express from 'express' import foo from '@/foo' ``` @@ -158,13 +157,13 @@ import foo from '@/foo' The following patterns are not considered problems when configuration set to `['error', 'always', {ignorePackages: true} ]`: ```js -import Component from './Component.jsx'; +import Component from './Component.jsx' -import baz from 'foo/baz.js'; +import baz from 'foo/baz.js' -import express from 'express'; +import express from 'express' -import foo from '@/foo'; +import foo from '@/foo' ``` ## When Not To Use It diff --git a/docs/rules/first.md b/docs/rules/first.md index c765a2973..2e1f020cf 100644 --- a/docs/rules/first.md +++ b/docs/rules/first.md @@ -1,4 +1,4 @@ -# import/first +# import-x/first 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). @@ -28,7 +28,7 @@ import bar from './bar' import * as _ from 'lodash' // <- reported ``` -If you really want import type ordering, check out [`import/order`]. +If you really want import type ordering, check out [`import-x/order`]. Notably, `import`s are hoisted, which means the imported modules will be evaluated before any of the statements interspersed between them. Keeping all `import`s together @@ -42,7 +42,7 @@ as follows: ```js 'use super-mega-strict' -import { suchFoo } from 'lame-fake-module-name' // no report here +import { suchFoo } from 'lame-fake-module-name' // no report here ``` A directive in this case is assumed to be a single statement that contains only @@ -59,7 +59,7 @@ Given that, see [#255] for the reasoning. This rule contains a fixer to reorder in-body import to top, the following criteria applied: 1. Never re-order relative to each other, even if `absolute-first` is set. -2. If an import creates an identifier, and that identifier is referenced at module level *before* the import itself, that won't be re-ordered. +2. If an import creates an identifier, and that identifier is referenced at module level _before_ the import itself, that won't be re-ordered. ## When Not To Use It @@ -68,8 +68,8 @@ enable this rule. ## Further Reading - - [`import/order`]: a major step up from `absolute-first` - - Issue [#255] +- [`import-x/order`]: a major step up from `absolute-first` +- Issue [#255] -[`import/order`]: ./order.md +[`import-x/order`]: ./order.md [#255]: https://github.com/import-js/eslint-plugin-import/issues/255 diff --git a/docs/rules/group-exports.md b/docs/rules/group-exports.md index 9fb212de6..ee9677826 100644 --- a/docs/rules/group-exports.md +++ b/docs/rules/group-exports.md @@ -1,4 +1,4 @@ -# import/group-exports +# import-x/group-exports @@ -22,10 +22,7 @@ const first = true const second = true // A single named export declaration -> ok -export { - first, - second, -} +export { first, second } ``` ```js @@ -38,7 +35,7 @@ export { default as module2 } from 'module-2' // A single exports assignment -> ok module.exports = { first: true, - second: true + second: true, } ``` @@ -63,12 +60,12 @@ module.exports = test ``` ```ts -const first = true; +const first = true type firstType = boolean // A single named export declaration (type exports handled separately) -> ok -export {first} -export type {firstType} +export { first } +export type { firstType } ``` ### Invalid @@ -109,8 +106,8 @@ type firstType = boolean type secondType = any // Multiple named type export statements -> not ok! -export type {firstType} -export type {secondType} +export type { firstType } +export type { secondType } ``` ## When Not To Use It diff --git a/docs/rules/imports-first.md b/docs/rules/imports-first.md index 278e4c472..eede18d8e 100644 --- a/docs/rules/imports-first.md +++ b/docs/rules/imports-first.md @@ -1,4 +1,4 @@ -# import/imports-first +# import-x/imports-first ❌ This rule is deprecated. diff --git a/docs/rules/max-dependencies.md b/docs/rules/max-dependencies.md index 1ecbca64d..779c9292e 100644 --- a/docs/rules/max-dependencies.md +++ b/docs/rules/max-dependencies.md @@ -1,4 +1,4 @@ -# import/max-dependencies +# import-x/max-dependencies @@ -13,7 +13,7 @@ Importing multiple named exports from a single module will only count once (e.g. This rule has the following options, with these defaults: ```js -"import/max-dependencies": ["error", { +"import-x/max-dependencies": ["error", { "max": 10, "ignoreTypeImports": false, }] @@ -28,17 +28,17 @@ Given a max value of `{"max": 2}`: ### Fail ```js -import a from './a'; // 1 -const b = require('./b'); // 2 -import c from './c'; // 3 - exceeds max! +import a from './a' // 1 +const b = require('./b') // 2 +import c from './c' // 3 - exceeds max! ``` ### Pass ```js -import a from './a'; // 1 -const anotherA = require('./a'); // still 1 -import {x, y, z} from './foo'; // 2 +import a from './a' // 1 +const anotherA = require('./a') // still 1 +import { x, y, z } from './foo' // 2 ``` ### `ignoreTypeImports` @@ -48,21 +48,23 @@ Ignores `type` imports. Type imports are a feature released in TypeScript 3.8, y Given `{"max": 2, "ignoreTypeImports": true}`: + ### Fail ```ts -import a from './a'; -import b from './b'; -import c from './c'; +import a from './a' +import b from './b' +import c from './c' ``` + ### Pass ```ts -import a from './a'; -import b from './b'; -import type c from './c'; // Doesn't count against max +import a from './a' +import b from './b' +import type c from './c' // Doesn't count against max ``` ## When Not To Use It diff --git a/docs/rules/named.md b/docs/rules/named.md index 44f8dc658..487bdc887 100644 --- a/docs/rules/named.md +++ b/docs/rules/named.md @@ -1,4 +1,4 @@ -# import/named +# import-x/named 💼🚫 This rule is enabled in the following configs: ❗ `errors`, ☑️ `recommended`. This rule is _disabled_ in the ⌨️ `typescript` config. @@ -37,7 +37,7 @@ import { foo } from './foo' export { foo as bar } from './foo' // node_modules without jsnext:main are not analyzed by default -// (import/ignore setting) +// (import-x/ignore setting) import { SomeNonsenseThatDoesntExist } from 'react' ``` @@ -56,7 +56,7 @@ import { dontCreateStore } from 'redux' ### Settings -[`import/ignore`] can be provided as a setting to ignore certain modules (node_modules, +[`import-x/ignore`] can be provided as a setting to ignore certain modules (node_modules, CoffeeScript, CSS if using Webpack, etc.). Given: @@ -65,7 +65,7 @@ Given: # .eslintrc (YAML) --- settings: - import/ignore: + import-x/ignore: - node_modules # included by default, but replaced if explicitly configured - *.coffee$ # can't parse CoffeeScript (unless a custom polyglot parser was configured) ``` @@ -93,10 +93,10 @@ runtime, you will likely see false positives with this rule. ## Further Reading - - [`import/ignore`] setting - - [`jsnext:main`] deprecation - - [`pkg.module`] (Rollup) +- [`import-x/ignore`] setting +- [`jsnext:main`] deprecation +- [`pkg.module`] (Rollup) [`jsnext:main`]: https://github.com/jsforum/jsforum/issues/5 [`pkg.module`]: https://github.com/rollup/rollup/wiki/pkg.module -[`import/ignore`]: ../../README.md#importignore +[`import-x/ignore`]: ../../README.md#importignore diff --git a/docs/rules/namespace.md b/docs/rules/namespace.md index 1a177f581..ac21ab66f 100644 --- a/docs/rules/namespace.md +++ b/docs/rules/namespace.md @@ -1,4 +1,4 @@ -# import/namespace +# import-x/namespace 💼 This rule is enabled in the following configs: ❗ `errors`, ☑️ `recommended`. @@ -40,7 +40,7 @@ export { b } const c = 3 export { c as d } -export class ExportedClass { } +export class ExportedClass {} // ES7 export * as deep from './deep' @@ -50,7 +50,7 @@ and: ```js // @module ./deep -export const e = "MC2" +export const e = 'MC2' ``` See what is valid and reported: @@ -60,7 +60,7 @@ See what is valid and reported: import * as names from './named-exports' function great() { - return names.a + names.b // so great https://youtu.be/ei7mb8UxEl8 + return names.a + names.b // so great https://youtu.be/ei7mb8UxEl8 } function notGreat() { @@ -74,7 +74,6 @@ function deepTrouble() { doSomethingWith(names.deep.e) // fine doSomethingWith(names.deep.f) // Reported: 'f' not found in deeply imported namespace 'names.deep'. } - ``` ### Options @@ -84,7 +83,7 @@ function deepTrouble() { Defaults to `false`. When false, will report the following: ```js -/*eslint import/namespace: [2, { allowComputed: false }]*/ +/*eslint import-x/namespace: [2, { allowComputed: false }]*/ import * as a from './a' function f(x) { @@ -97,10 +96,10 @@ still can't be statically analyzed any further. ## Further Reading - - Lee Byron's [ES7] export proposal - - [`import/ignore`] setting - - [`jsnext:main`](Rollup) +- Lee Byron's [ES7] export proposal +- [`import-x/ignore`] setting +- [`jsnext:main`](Rollup) [ES7]: https://github.com/leebyron/ecmascript-more-export-from -[`import/ignore`]: ../../README.md#importignore +[`import-x/ignore`]: ../../README.md#importignore [`jsnext:main`]: https://github.com/rollup/rollup/wiki/jsnext:main diff --git a/docs/rules/newline-after-import.md b/docs/rules/newline-after-import.md index ef5aeed76..7d87ed342 100644 --- a/docs/rules/newline-after-import.md +++ b/docs/rules/newline-after-import.md @@ -1,4 +1,4 @@ -# import/newline-after-import +# import-x/newline-after-import 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). @@ -10,121 +10,112 @@ Enforces having one or more empty lines after the last top-level import statemen This rule supports the following options: - - `count` which sets the number of newlines that are enforced after the last top-level import statement or require call. This option defaults to `1`. +- `count` which sets the number of newlines that are enforced after the last top-level import statement or require call. This option defaults to `1`. - - `exactCount` which enforce the exact numbers of newlines that is mentioned in `count`. This option defaults to `false`. +- `exactCount` which enforce the exact numbers of newlines that is mentioned in `count`. This option defaults to `false`. - - `considerComments` which enforces the rule on comments after the last import-statement as well when set to true. This option defaults to `false`. +- `considerComments` which enforces the rule on comments after the last import-statement as well when set to true. This option defaults to `false`. Valid: ```js -import defaultExport from './foo'; +import defaultExport from './foo' -const FOO = 'BAR'; +const FOO = 'BAR' ``` ```js -import defaultExport from './foo'; -import { bar } from 'bar-lib'; +import defaultExport from './foo' +import { bar } from 'bar-lib' -const FOO = 'BAR'; +const FOO = 'BAR' ``` ```js -const FOO = require('./foo'); -const BAR = require('./bar'); +const FOO = require('./foo') +const BAR = require('./bar') -const BAZ = 1; +const BAZ = 1 ``` Invalid: ```js import * as foo from 'foo' -const FOO = 'BAR'; +const FOO = 'BAR' ``` ```js -import * as foo from 'foo'; -const FOO = 'BAR'; +import * as foo from 'foo' +const FOO = 'BAR' -import { bar } from 'bar-lib'; +import { bar } from 'bar-lib' ``` ```js -const FOO = require('./foo'); -const BAZ = 1; -const BAR = require('./bar'); +const FOO = require('./foo') +const BAZ = 1 +const BAR = require('./bar') ``` With `count` set to `2` this will be considered valid: ```js -import defaultExport from './foo'; - +import defaultExport from './foo' -const FOO = 'BAR'; +const FOO = 'BAR' ``` ```js -import defaultExport from './foo'; - - +import defaultExport from './foo' -const FOO = 'BAR'; +const FOO = 'BAR' ``` With `count` set to `2` these will be considered invalid: ```js -import defaultExport from './foo'; -const FOO = 'BAR'; +import defaultExport from './foo' +const FOO = 'BAR' ``` ```js -import defaultExport from './foo'; +import defaultExport from './foo' -const FOO = 'BAR'; +const FOO = 'BAR' ``` With `count` set to `2` and `exactCount` set to `true` this will be considered valid: ```js -import defaultExport from './foo'; - +import defaultExport from './foo' -const FOO = 'BAR'; +const FOO = 'BAR' ``` With `count` set to `2` and `exactCount` set to `true` these will be considered invalid: ```js -import defaultExport from './foo'; -const FOO = 'BAR'; +import defaultExport from './foo' +const FOO = 'BAR' ``` ```js -import defaultExport from './foo'; +import defaultExport from './foo' -const FOO = 'BAR'; +const FOO = 'BAR' ``` ```js -import defaultExport from './foo'; - - +import defaultExport from './foo' -const FOO = 'BAR'; +const FOO = 'BAR' ``` ```js -import defaultExport from './foo'; - - - +import defaultExport from './foo' -const FOO = 'BAR'; +const FOO = 'BAR' ``` With `considerComments` set to `false` this will be considered valid: @@ -157,7 +148,7 @@ const FOO = 'BAR' ```json { "rules": { - "import/newline-after-import": ["error", { "count": 2 }] + "import-x/newline-after-import": ["error", { "count": 2 }] } } ``` diff --git a/docs/rules/no-absolute-path.md b/docs/rules/no-absolute-path.md index 48fb9532b..d7cbb1968 100644 --- a/docs/rules/no-absolute-path.md +++ b/docs/rules/no-absolute-path.md @@ -1,4 +1,4 @@ -# import/no-absolute-path +# import-x/no-absolute-path 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). @@ -13,23 +13,23 @@ This rule forbids the import of modules using absolute paths. ### Fail ```js -import f from '/foo'; -import f from '/some/path'; +import f from '/foo' +import f from '/some/path' -var f = require('/foo'); -var f = require('/some/path'); +var f = require('/foo') +var f = require('/some/path') ``` ### Pass ```js -import _ from 'lodash'; -import foo from 'foo'; -import foo from './foo'; +import _ from 'lodash' +import foo from 'foo' +import foo from './foo' -var _ = require('lodash'); -var foo = require('foo'); -var foo = require('./foo'); +var _ = require('lodash') +var foo = require('foo') +var foo = require('./foo') ``` ### Options @@ -38,17 +38,21 @@ By default, only ES6 imports and CommonJS `require` calls will have this rule en You may provide an options object providing true/false for any of - - `esmodule`: defaults to `true` - - `commonjs`: defaults to `true` - - `amd`: defaults to `false` +- `esmodule`: defaults to `true` +- `commonjs`: defaults to `true` +- `amd`: defaults to `false` If `{ amd: true }` is provided, dependency paths for AMD-style `define` and `require` calls will be resolved: ```js -/*eslint import/no-absolute-path: [2, { commonjs: false, amd: true }]*/ -define(['/foo'], function (foo) { /*...*/ }) // reported -require(['/foo'], function (foo) { /*...*/ }) // reported +/*eslint import-x/no-absolute-path: [2, { commonjs: false, amd: true }]*/ +define(['/foo'], function (foo) { + /*...*/ +}) // reported +require(['/foo'], function (foo) { + /*...*/ +}) // reported const foo = require('/foo') // ignored because of explicit `commonjs: false` ``` diff --git a/docs/rules/no-amd.md b/docs/rules/no-amd.md index 6e592ba75..fe51227f1 100644 --- a/docs/rules/no-amd.md +++ b/docs/rules/no-amd.md @@ -1,4 +1,4 @@ -# import/no-amd +# import-x/no-amd @@ -12,9 +12,13 @@ Intended for temporary use when migrating to pure ES6 modules. This will be reported: ```js -define(["a", "b"], function (a, b) { /* ... */ }) +define(['a', 'b'], function (a, b) { + /* ... */ +}) -require(["b", "c"], function (b, c) { /* ... */ }) +require(['b', 'c'], function (b, c) { + /* ... */ +}) ``` CommonJS `require` is still valid. @@ -33,5 +37,5 @@ Special thanks to @xjamundx for donating his no-define rule as a start to this. ## Further Reading - - [`no-commonjs`](./no-commonjs.md): report CommonJS `require` and `exports` - - Source: +- [`no-commonjs`](./no-commonjs.md): report CommonJS `require` and `exports` +- Source: diff --git a/docs/rules/no-anonymous-default-export.md b/docs/rules/no-anonymous-default-export.md index 70efb8450..c3f9ff13a 100644 --- a/docs/rules/no-anonymous-default-export.md +++ b/docs/rules/no-anonymous-default-export.md @@ -1,4 +1,4 @@ -# import/no-anonymous-default-export +# import-x/no-anonymous-default-export @@ -13,7 +13,7 @@ By default, all types of anonymous default exports are forbidden, but any types The complete default configuration looks like this. ```js -"import/no-anonymous-default-export": ["error", { +"import-x/no-anonymous-default-export": ["error", { "allowArray": false, "allowArrowFunction": false, "allowAnonymousClass": false, @@ -38,7 +38,7 @@ export default class {} export default function () {} -/* eslint import/no-anonymous-default-export: [2, {"allowCallExpression": false}] */ +/* eslint import-x/no-anonymous-default-export: [2, {"allowCallExpression": false}] */ export default foo(bar) export default 123 @@ -58,26 +58,26 @@ export default class MyClass() {} export default function foo() {} -/* eslint import/no-anonymous-default-export: [2, {"allowArray": true}] */ +/* eslint import-x/no-anonymous-default-export: [2, {"allowArray": true}] */ export default [] -/* eslint import/no-anonymous-default-export: [2, {"allowArrowFunction": true}] */ +/* eslint import-x/no-anonymous-default-export: [2, {"allowArrowFunction": true}] */ export default () => {} -/* eslint import/no-anonymous-default-export: [2, {"allowAnonymousClass": true}] */ +/* eslint import-x/no-anonymous-default-export: [2, {"allowAnonymousClass": true}] */ export default class {} -/* eslint import/no-anonymous-default-export: [2, {"allowAnonymousFunction": true}] */ +/* eslint import-x/no-anonymous-default-export: [2, {"allowAnonymousFunction": true}] */ export default function () {} export default foo(bar) -/* eslint import/no-anonymous-default-export: [2, {"allowLiteral": true}] */ +/* eslint import-x/no-anonymous-default-export: [2, {"allowLiteral": true}] */ export default 123 -/* eslint import/no-anonymous-default-export: [2, {"allowObject": true}] */ +/* eslint import-x/no-anonymous-default-export: [2, {"allowObject": true}] */ export default {} -/* eslint import/no-anonymous-default-export: [2, {"allowNew": true}] */ +/* eslint import-x/no-anonymous-default-export: [2, {"allowNew": true}] */ export default new Foo() ``` diff --git a/docs/rules/no-commonjs.md b/docs/rules/no-commonjs.md index 4dc9c8c5d..2243baff3 100644 --- a/docs/rules/no-commonjs.md +++ b/docs/rules/no-commonjs.md @@ -1,4 +1,4 @@ -# import/no-commonjs +# import-x/no-commonjs @@ -14,13 +14,13 @@ Intended for temporary use when migrating to pure ES6 modules. This will be reported: ```js -var mod = require('./mod') - , common = require('./common') - , fs = require('fs') - , whateverModule = require('./not-found') +var mod = require('./mod'), + common = require('./common'), + fs = require('fs'), + whateverModule = require('./not-found') -module.exports = { a: "b" } -exports.c = "d" +module.exports = { a: 'b' } +exports.c = 'd' ``` ### Allow require @@ -29,7 +29,7 @@ If `allowRequire` option is set to `true`, `require` calls are valid: ```js /*eslint no-commonjs: [2, { allowRequire: true }]*/ -var mod = require('./mod'); +var mod = require('./mod') ``` but `module.exports` is reported as usual. @@ -39,15 +39,15 @@ but `module.exports` is reported as usual. By default, conditional requires are allowed: ```js -var a = b && require("c") +var a = b && require('c') -if (typeof window !== "undefined") { - require('that-ugly-thing'); +if (typeof window !== 'undefined') { + require('that-ugly-thing') } -var fs = null; +var fs = null try { - fs = require("fs") + fs = require('fs') } catch (error) {} ``` @@ -62,8 +62,12 @@ If `allowPrimitiveModules` option is set to `true`, the following is valid: ```js /*eslint no-commonjs: [2, { allowPrimitiveModules: true }]*/ -module.exports = "foo" -module.exports = function rule(context) { return { /* ... */ } } +module.exports = 'foo' +module.exports = function rule(context) { + return { + /* ... */ + } +} ``` but this is still reported: @@ -71,8 +75,10 @@ but this is still reported: ```js /*eslint no-commonjs: [2, { allowPrimitiveModules: true }]*/ -module.exports = { x: "y" } -exports.z = function boop() { /* ... */ } +module.exports = { x: 'y' } +exports.z = function boop() { + /* ... */ +} ``` This is useful for things like ESLint rule modules, which must export a function as @@ -88,9 +94,9 @@ from CommonJS to ES6 modules. ## Contributors -Special thanks to @xjamundx for donating the module.exports and exports.* bits. +Special thanks to @xjamundx for donating the module.exports and exports.\* bits. ## Further Reading - - [`no-amd`](./no-amd.md): report on AMD `require`, `define` - - Source: +- [`no-amd`](./no-amd.md): report on AMD `require`, `define` +- Source: diff --git a/docs/rules/no-cycle.md b/docs/rules/no-cycle.md index 76e96f95f..b57a6da78 100644 --- a/docs/rules/no-cycle.md +++ b/docs/rules/no-cycle.md @@ -1,4 +1,4 @@ -# import/no-cycle +# import-x/no-cycle @@ -11,7 +11,9 @@ This includes cycles of depth 1 (imported module imports me) to `"∞"` (or `Inf // dep-b.js import './dep-a.js' -export function b() { /* ... */ } +export function b() { + /* ... */ +} ``` ```js @@ -29,7 +31,7 @@ This rule ignores type-only imports in Flow and TypeScript syntax (`import type` ### Options By default, this rule only detects cycles for ES6 imports, but see the [`no-unresolved` options](./no-unresolved.md#options) as this rule also supports the same `commonjs` and `amd` flags. However, these flags only impact which import types are _linted_; the -import/export infrastructure only registers `import` statements in dependencies, so +import-x/export infrastructure only registers `import` statements in dependencies, so cycles created by `require` within imported modules may not be detected. #### `maxDepth` @@ -37,7 +39,7 @@ cycles created by `require` within imported modules may not be detected. There is a `maxDepth` option available to prevent full expansion of very deep dependency trees: ```js -/*eslint import/no-cycle: [2, { maxDepth: 1 }]*/ +/*eslint import-x/no-cycle: [2, { maxDepth: 1 }]*/ // dep-c.js import './dep-a.js' @@ -47,7 +49,9 @@ import './dep-a.js' // dep-b.js import './dep-c.js' -export function b() { /* ... */ } +export function b() { + /* ... */ +} ``` ```js @@ -63,12 +67,14 @@ for reducing total project lint time, if needed. An `ignoreExternal` option is available to prevent the cycle detection to expand to external modules: ```js -/*eslint import/no-cycle: [2, { ignoreExternal: true }]*/ +/*eslint import-x/no-cycle: [2, { ignoreExternal: true }]*/ // dep-a.js import 'module-b/dep-b.js' -export function a() { /* ... */ } +export function a() { + /* ... */ +} ``` ```js @@ -84,12 +90,14 @@ This option disable reporting of errors if a cycle is detected with at least one ```js // bar.js -import { foo } from './foo'; -export const bar = foo; +import { foo } from './foo' +export const bar = foo // foo.js -export const foo = 'Foo'; -export function getBar() { return import('./bar'); } +export const foo = 'Foo' +export function getBar() { + return import('./bar') +} ``` > Cyclic dependency are **always** a dangerous anti-pattern as discussed extensively in [#2265](https://github.com/import-js/eslint-plugin-import/issues/2265). Please be extra careful about using this option. @@ -102,10 +110,9 @@ this rule enabled. ## Further Reading - - [Original inspiring issue](https://github.com/import-js/eslint-plugin-import/issues/941) - - Rule to detect that module imports itself: [`no-self-import`] - - [`import/external-module-folders`] setting +- [Original inspiring issue](https://github.com/import-js/eslint-plugin-import/issues/941) +- Rule to detect that module imports itself: [`no-self-import`] +- [`import-x/external-module-folders`] setting [`no-self-import`]: ./no-self-import.md - -[`import/external-module-folders`]: ../../README.md#importexternal-module-folders +[`import-x/external-module-folders`]: ../../README.md#importexternal-module-folders diff --git a/docs/rules/no-default-export.md b/docs/rules/no-default-export.md index 586d5e745..cdffba6a6 100644 --- a/docs/rules/no-default-export.md +++ b/docs/rules/no-default-export.md @@ -1,4 +1,4 @@ -# import/no-default-export +# import-x/no-default-export @@ -14,16 +14,16 @@ The following patterns are considered warnings: // bad1.js // There is a default export. -export const foo = 'foo'; -const bar = 'bar'; -export default 'bar'; +export const foo = 'foo' +const bar = 'bar' +export default 'bar' ``` ```javascript // bad2.js // There is a default export. -const foo = 'foo'; +const foo = 'foo' export { foo as default } ``` @@ -33,23 +33,23 @@ The following patterns are not warnings: // good1.js // There is only a single module export and it's a named export. -export const foo = 'foo'; +export const foo = 'foo' ``` ```javascript // good2.js // There is more than one named export in the module. -export const foo = 'foo'; -export const bar = 'bar'; +export const foo = 'foo' +export const bar = 'bar' ``` ```javascript // good3.js // There is more than one named export in the module -const foo = 'foo'; -const bar = 'bar'; +const foo = 'foo' +const bar = 'bar' export { foo, bar } ``` diff --git a/docs/rules/no-deprecated.md b/docs/rules/no-deprecated.md index a647d77ad..f4edffcd3 100644 --- a/docs/rules/no-deprecated.md +++ b/docs/rules/no-deprecated.md @@ -1,4 +1,4 @@ -# import/no-deprecated +# import-x/no-deprecated @@ -43,20 +43,20 @@ export function multiply(six, nine) { ``` Only JSDoc is enabled by default. Other documentation styles can be enabled with -the `import/docstyle` setting. +the `import-x/docstyle` setting. ```yaml # .eslintrc.yml settings: - import/docstyle: ['jsdoc', 'tomdoc'] + import-x/docstyle: ['jsdoc', 'tomdoc'] ``` ## Worklist - - [x] report explicit imports on the import node - - [x] support namespaces - - [x] should bubble up through deep namespaces (#157) - - [x] report explicit imports at reference time (at the identifier) similar to namespace - - [x] mark module deprecated if file JSDoc has a @deprecated tag? - - [ ] don't flag redeclaration of imported, deprecated names - - [ ] flag destructuring +- [x] report explicit imports on the import node +- [x] support namespaces + - [x] should bubble up through deep namespaces (#157) +- [x] report explicit imports at reference time (at the identifier) similar to namespace +- [x] mark module deprecated if file JSDoc has a @deprecated tag? +- [ ] don't flag redeclaration of imported, deprecated names +- [ ] flag destructuring diff --git a/docs/rules/no-duplicates.md b/docs/rules/no-duplicates.md index 29c16f15d..7bdf789ee 100644 --- a/docs/rules/no-duplicates.md +++ b/docs/rules/no-duplicates.md @@ -1,4 +1,4 @@ -# import/no-duplicates +# import-x/no-duplicates ⚠️ This rule _warns_ in the following configs: ☑️ `recommended`, 🚸 `warnings`. @@ -49,7 +49,7 @@ By default, this rule ignores query strings (i.e. paths followed by a question m Config: ```json -"import/no-duplicates": ["error", {"considerQueryString": true}] +"import-x/no-duplicates": ["error", {"considerQueryString": true}] ``` And then the following code becomes valid: @@ -76,7 +76,7 @@ TypeScript 4.5 introduced a new [feature](https://devblogs.microsoft.com/typescr Config: ```json -"import/no-duplicates": ["error", {"prefer-inline": true}] +"import-x/no-duplicates": ["error", {"prefer-inline": true}] ``` @@ -103,7 +103,7 @@ import { CValue, type CType } from './papa-mia' ## When Not To Use It -If the core ESLint version is good enough (i.e. you're _not_ using Flow and you _are_ using [`import/extensions`](./extensions.md)), keep it and don't use this. +If the core ESLint version is good enough (i.e. you're _not_ using Flow and you _are_ using [`import-x/extensions`](./extensions.md)), keep it and don't use this. If you like to split up imports across lines or may need to import a default and a namespace, you may not want to enable this rule. diff --git a/docs/rules/no-dynamic-require.md b/docs/rules/no-dynamic-require.md index 292055fcd..e63f3de34 100644 --- a/docs/rules/no-dynamic-require.md +++ b/docs/rules/no-dynamic-require.md @@ -1,4 +1,4 @@ -# import/no-dynamic-require +# import-x/no-dynamic-require @@ -11,15 +11,15 @@ This rule forbids every call to `require()` that uses expressions for the module ### Fail ```js -require(name); -require('../' + name); -require(`../${name}`); -require(name()); +require(name) +require('../' + name) +require(`../${name}`) +require(name()) ``` ### Pass ```js -require('../name'); -require(`../name`); +require('../name') +require(`../name`) ``` diff --git a/docs/rules/no-empty-named-blocks.md b/docs/rules/no-empty-named-blocks.md index ad83c535f..642413d7a 100644 --- a/docs/rules/no-empty-named-blocks.md +++ b/docs/rules/no-empty-named-blocks.md @@ -1,4 +1,4 @@ -# import/no-empty-named-blocks +# import-x/no-empty-named-blocks 🔧💡 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix) and manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions). @@ -31,7 +31,7 @@ import typeof { mod } from 'mod' ```js import {} from 'mod' -import Default, {} from 'mod' +import Default from 'mod' ``` When using typescript diff --git a/docs/rules/no-extraneous-dependencies.md b/docs/rules/no-extraneous-dependencies.md index 848d5bb0d..99068aaaa 100644 --- a/docs/rules/no-extraneous-dependencies.md +++ b/docs/rules/no-extraneous-dependencies.md @@ -1,4 +1,4 @@ -# import/no-extraneous-dependencies +# import-x/no-extraneous-dependencies @@ -23,13 +23,13 @@ Type imports are ignored by default. You can set the options like this: ```js -"import/no-extraneous-dependencies": ["error", {"devDependencies": false, "optionalDependencies": false, "peerDependencies": false}] +"import-x/no-extraneous-dependencies": ["error", {"devDependencies": false, "optionalDependencies": false, "peerDependencies": false}] ``` You can also use an array of globs instead of literal booleans: ```js -"import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*.test.js", "**/*.spec.js"]}] +"import-x/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*.test.js", "**/*.spec.js"]}] ``` When using an array of globs, the setting will be set to `true` (no errors reported) if the name of the file being linted (i.e. not the imported file/module) matches a single glob in the array, and `false` otherwise. @@ -37,7 +37,7 @@ When using an array of globs, the setting will be set to `true` (no errors repor There are 2 boolean options to opt into checking extra imports that are normally ignored: `includeInternal`, which enables the checking of internal modules, and `includeTypes`, which enables checking of type imports in TypeScript. ```js -"import/no-extraneous-dependencies": ["error", {"includeInternal": true, "includeTypes": true}] +"import-x/no-extraneous-dependencies": ["error", {"includeInternal": true, "includeTypes": true}] ``` Also there is one more option called `packageDir`, this option is to specify the path to the folder containing package.json. @@ -45,16 +45,16 @@ Also there is one more option called `packageDir`, this option is to specify the If provided as a relative path string, will be computed relative to the current working directory at linter execution time. If this is not ideal (does not work with some editor integrations), consider using `__dirname` to provide a path relative to your configuration. ```js -"import/no-extraneous-dependencies": ["error", {"packageDir": './some-dir/'}] +"import-x/no-extraneous-dependencies": ["error", {"packageDir": './some-dir/'}] // or -"import/no-extraneous-dependencies": ["error", {"packageDir": path.join(__dirname, 'some-dir')}] +"import-x/no-extraneous-dependencies": ["error", {"packageDir": path.join(__dirname, 'some-dir')}] ``` It may also be an array of multiple paths, to support monorepos or other novel project folder layouts: ```js -"import/no-extraneous-dependencies": ["error", {"packageDir": ['./some-dir/', './root-pkg']}] +"import-x/no-extraneous-dependencies": ["error", {"packageDir": ['./some-dir/', './root-pkg']}] ``` ## Rule Details @@ -83,9 +83,7 @@ Given the following `package.json`: "peerDependencies": { "react": ">=15.0.0 <16.0.0" }, - "bundledDependencies": [ - "@generated/foo", - ] + "bundledDependencies": ["@generated/foo"] } ``` @@ -97,23 +95,23 @@ import _ from 'lodash'; import react from 'react'; -/* eslint import/no-extraneous-dependencies: ["error", {"devDependencies": false}] */ +/* eslint import-x/no-extraneous-dependencies: ["error", {"devDependencies": false}] */ import test from 'ava'; var test = require('ava'); -/* eslint import/no-extraneous-dependencies: ["error", {"optionalDependencies": false}] */ +/* eslint import-x/no-extraneous-dependencies: ["error", {"optionalDependencies": false}] */ import isArray from 'lodash.isarray'; var isArray = require('lodash.isarray'); -/* eslint import/no-extraneous-dependencies: ["error", {"bundledDependencies": false}] */ +/* eslint import-x/no-extraneous-dependencies: ["error", {"bundledDependencies": false}] */ import foo from '"@generated/foo"'; var foo = require('"@generated/foo"'); -/* eslint import/no-extraneous-dependencies: ["error", {"includeInternal": true}] */ +/* eslint import-x/no-extraneous-dependencies: ["error", {"includeInternal": true}] */ import foo from './foo'; var foo = require('./foo'); -/* eslint import/no-extraneous-dependencies: ["error", {"includeTypes": true}] */ +/* eslint import-x/no-extraneous-dependencies: ["error", {"includeTypes": true}] */ import type { MyType } from 'foo'; ``` @@ -130,7 +128,7 @@ import isArray from 'lodash.isarray'; import foo from '"@generated/foo"'; import type { MyType } from 'foo'; -/* eslint import/no-extraneous-dependencies: ["error", {"peerDependencies": true}] */ +/* eslint import-x/no-extraneous-dependencies: ["error", {"peerDependencies": true}] */ import react from 'react'; ``` diff --git a/docs/rules/no-import-module-exports.md b/docs/rules/no-import-module-exports.md index 1c5722649..f5e941132 100644 --- a/docs/rules/no-import-module-exports.md +++ b/docs/rules/no-import-module-exports.md @@ -1,4 +1,4 @@ -# import/no-import-module-exports +# import-x/no-import-module-exports 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). @@ -14,12 +14,12 @@ If you have multiple entry points or are using `js:next` this rule includes an ### `exceptions` - - An array of globs. The rule will be omitted from any file that matches a glob - in the options array. For example, the following setting will omit the rule - in the `some-file.js` file. +- An array of globs. The rule will be omitted from any file that matches a glob + in the options array. For example, the following setting will omit the rule + in the `some-file.js` file. ```json -"import/no-import-module-exports": ["error", { +"import-x/no-import-module-exports": ["error", { "exceptions": ["**/*/some-file.js"] }] ``` @@ -39,7 +39,7 @@ import thing from 'other-thing' exports.foo = bar import thing from 'starwars' -const baz = module.exports = thing +const baz = (module.exports = thing) console.log(baz) ``` @@ -49,7 +49,7 @@ Given the following package.json: ```json { - "main": "lib/index.js", + "main": "lib/index.js" } ``` @@ -67,15 +67,15 @@ import thing from 'otherthing' console.log(thing.module.exports) // in lib/index.js -import foo from 'path'; -module.exports = foo; +import foo from 'path' +module.exports = foo // in some-file.js -// eslint import/no-import-module-exports: ["error", {"exceptions": ["**/*/some-file.js"]}] -import foo from 'path'; -module.exports = foo; +// eslint import-x/no-import-module-exports: ["error", {"exceptions": ["**/*/some-file.js"]}] +import foo from 'path' +module.exports = foo ``` ### Further Reading - - [webpack issue #4039](https://github.com/webpack/webpack/issues/4039) +- [webpack issue #4039](https://github.com/webpack/webpack/issues/4039) diff --git a/docs/rules/no-internal-modules.md b/docs/rules/no-internal-modules.md index 433b55140..7f5cf4818 100644 --- a/docs/rules/no-internal-modules.md +++ b/docs/rules/no-internal-modules.md @@ -1,4 +1,4 @@ -# import/no-internal-modules +# import-x/no-internal-modules @@ -8,8 +8,8 @@ Use this rule to prevent importing the submodules of other modules. This rule has two mutally exclusive options that are arrays of [minimatch/glob patterns](https://github.com/isaacs/node-glob#glob-primer) patterns: - - `allow` that include paths and import statements that can be imported with reaching. - - `forbid` that exclude paths and import statements that can be imported with reaching. +- `allow` that include paths and import statements that can be imported with reaching. +- `forbid` that exclude paths and import statements that can be imported with reaching. ### Examples @@ -38,7 +38,7 @@ And the .eslintrc file: { ... "rules": { - "import/no-internal-modules": [ "error", { + "import-x/no-internal-modules": [ "error", { "allow": [ "**/actions/*", "source-map-support/*" ], } ] } @@ -52,12 +52,12 @@ The following patterns are considered problems: * in my-project/entry.js */ -import { settings } from './app/index'; // Reaching to "./app/index" is not allowed -import userReducer from './reducer/user'; // Reaching to "./reducer/user" is not allowed -import configureStore from './redux/configureStore'; // Reaching to "./redux/configureStore" is not allowed +import { settings } from './app/index' // Reaching to "./app/index" is not allowed +import userReducer from './reducer/user' // Reaching to "./reducer/user" is not allowed +import configureStore from './redux/configureStore' // Reaching to "./redux/configureStore" is not allowed -export { settings } from './app/index'; // Reaching to "./app/index" is not allowed -export * from './reducer/user'; // Reaching to "./reducer/user" is not allowed +export { settings } from './app/index' // Reaching to "./app/index" is not allowed +export * from './reducer/user' // Reaching to "./reducer/user" is not allowed ``` The following patterns are NOT considered problems: @@ -67,12 +67,12 @@ The following patterns are NOT considered problems: * in my-project/entry.js */ -import 'source-map-support/register'; -import { settings } from '../app'; -import getUser from '../actions/getUser'; +import 'source-map-support/register' +import { settings } from '../app' +import getUser from '../actions/getUser' -export * from 'source-map-support/register'; -export { settings } from '../app'; +export * from 'source-map-support/register' +export { settings } from '../app' ``` Given the following folder structure: @@ -100,7 +100,7 @@ And the .eslintrc file: { ... "rules": { - "import/no-internal-modules": [ "error", { + "import-x/no-internal-modules": [ "error", { "forbid": [ "**/actions/*", "source-map-support/*" ], } ] } @@ -114,11 +114,11 @@ The following patterns are considered problems: * in my-project/entry.js */ -import 'source-map-support/register'; -import getUser from '../actions/getUser'; +import 'source-map-support/register' +import getUser from '../actions/getUser' -export * from 'source-map-support/register'; -export getUser from '../actions/getUser'; +export * from 'source-map-support/register' +export getUser from '../actions/getUser' ``` The following patterns are NOT considered problems: @@ -128,9 +128,9 @@ The following patterns are NOT considered problems: * in my-project/entry.js */ -import 'source-map-support'; -import { getUser } from '../actions'; +import 'source-map-support' +import { getUser } from '../actions' -export * from 'source-map-support'; -export { getUser } from '../actions'; +export * from 'source-map-support' +export { getUser } from '../actions' ``` diff --git a/docs/rules/no-mutable-exports.md b/docs/rules/no-mutable-exports.md index ce5162785..c2ce5ed23 100644 --- a/docs/rules/no-mutable-exports.md +++ b/docs/rules/no-mutable-exports.md @@ -1,4 +1,4 @@ -# import/no-mutable-exports +# import-x/no-mutable-exports @@ -43,8 +43,8 @@ export function getCount() {} // reported here: exported function is reassigned To prevent general reassignment of these identifiers, exported or not, you may want to enable the following core ESLint rules: - - [no-func-assign] - - [no-class-assign] +- [no-func-assign] +- [no-class-assign] [no-func-assign]: https://eslint.org/docs/rules/no-func-assign [no-class-assign]: https://eslint.org/docs/rules/no-class-assign diff --git a/docs/rules/no-named-as-default-member.md b/docs/rules/no-named-as-default-member.md index e8935fb7d..634c58e4b 100644 --- a/docs/rules/no-named-as-default-member.md +++ b/docs/rules/no-named-as-default-member.md @@ -1,4 +1,4 @@ -# import/no-named-as-default-member +# import-x/no-named-as-default-member ⚠️ This rule _warns_ in the following configs: ☑️ `recommended`, 🚸 `warnings`. @@ -25,14 +25,14 @@ Given: ```js // foo.js -export default 'foo'; -export const bar = 'baz'; +export default 'foo' +export const bar = 'baz' ``` ...this would be valid: ```js -import foo, {bar} from './foo.js'; +import foo, { bar } from './foo.js' ``` ...and the following would be reported: @@ -40,13 +40,13 @@ import foo, {bar} from './foo.js'; ```js // Caution: `foo` also has a named export `bar`. // Check if you meant to write `import {bar} from './foo.js'` instead. -import foo from './foo.js'; -const bar = foo.bar; +import foo from './foo.js' +const bar = foo.bar ``` ```js // Caution: `foo` also has a named export `bar`. // Check if you meant to write `import {bar} from './foo.js'` instead. -import foo from './foo.js'; -const {bar} = foo; +import foo from './foo.js' +const { bar } = foo ``` diff --git a/docs/rules/no-named-as-default.md b/docs/rules/no-named-as-default.md index 043d69942..6f7c2080d 100644 --- a/docs/rules/no-named-as-default.md +++ b/docs/rules/no-named-as-default.md @@ -1,4 +1,4 @@ -# import/no-named-as-default +# import-x/no-named-as-default ⚠️ This rule _warns_ in the following configs: ☑️ `recommended`, 🚸 `warnings`. @@ -8,8 +8,8 @@ Reports use of an exported name as the locally imported name of a default export Rationale: using an exported name as the name of the default export is likely... - - _misleading_: others familiar with `foo.js` probably expect the name to be `foo` - - _a mistake_: only needed to import `bar` and forgot the brackets (the case that is prompting this) +- _misleading_: others familiar with `foo.js` probably expect the name to be `foo` +- _a mistake_: only needed to import `bar` and forgot the brackets (the case that is prompting this) ## Rule Details @@ -17,37 +17,37 @@ Given: ```js // foo.js -export default 'foo'; -export const bar = 'baz'; +export default 'foo' +export const bar = 'baz' ``` ...this would be valid: ```js -import foo from './foo.js'; +import foo from './foo.js' ``` ...and this would be reported: ```js // message: Using exported name 'bar' as identifier for default export. -import bar from './foo.js'; +import bar from './foo.js' ``` For post-ES2015 `export` extensions, this also prevents exporting the default from a referenced module as a name within that module, for the same reasons: ```js // valid: -export foo from './foo.js'; +export foo from './foo.js' // message: Using exported name 'bar' as identifier for default export. -export bar from './foo.js'; +export bar from './foo.js' ``` ## Further Reading - - ECMAScript Proposal: [export ns from] - - ECMAScript Proposal: [export default from] +- ECMAScript Proposal: [export ns from] +- ECMAScript Proposal: [export default from] [export ns from]: https://github.com/leebyron/ecmascript-export-ns-from [export default from]: https://github.com/leebyron/ecmascript-export-default-from diff --git a/docs/rules/no-named-default.md b/docs/rules/no-named-default.md index 05860cde1..31c3d0eb2 100644 --- a/docs/rules/no-named-default.md +++ b/docs/rules/no-named-default.md @@ -1,4 +1,4 @@ -# import/no-named-default +# import-x/no-named-default @@ -16,21 +16,21 @@ Given: ```js // foo.js -export default 'foo'; -export const bar = 'baz'; +export default 'foo' +export const bar = 'baz' ``` ...these would be valid: ```js -import foo from './foo.js'; -import foo, { bar } from './foo.js'; +import foo from './foo.js' +import foo, { bar } from './foo.js' ``` ...and these would be reported: ```js // message: Using exported name 'bar' as identifier for default export. -import { default as foo } from './foo.js'; -import { default as foo, bar } from './foo.js'; +import { default as foo } from './foo.js' +import { default as foo, bar } from './foo.js' ``` diff --git a/docs/rules/no-named-export.md b/docs/rules/no-named-export.md index 13ea63ad7..0c9a2766d 100644 --- a/docs/rules/no-named-export.md +++ b/docs/rules/no-named-export.md @@ -1,4 +1,4 @@ -# import/no-named-export +# import-x/no-named-export @@ -14,23 +14,23 @@ The following patterns are considered warnings: // bad1.js // There is only a single module export and it's a named export. -export const foo = 'foo'; +export const foo = 'foo' ``` ```javascript // bad2.js // There is more than one named export in the module. -export const foo = 'foo'; -export const bar = 'bar'; +export const foo = 'foo' +export const bar = 'bar' ``` ```javascript // bad3.js // There is more than one named export in the module. -const foo = 'foo'; -const bar = 'bar'; +const foo = 'foo' +const bar = 'bar' export { foo, bar } ``` @@ -45,9 +45,9 @@ export * from './other-module' // bad5.js // There is a default and a named export. -export const foo = 'foo'; -const bar = 'bar'; -export default 'bar'; +export const foo = 'foo' +const bar = 'bar' +export default 'bar' ``` The following patterns are not warnings: @@ -56,14 +56,14 @@ The following patterns are not warnings: // good1.js // There is only a single module export and it's a default export. -export default 'bar'; +export default 'bar' ``` ```javascript // good2.js // There is only a single module export and it's a default export. -const foo = 'foo'; +const foo = 'foo' export { foo as default } ``` @@ -71,7 +71,7 @@ export { foo as default } // good3.js // There is only a single module export and it's a default export. -export default from './other-module'; +export default from './other-module' ``` ## When Not To Use It diff --git a/docs/rules/no-namespace.md b/docs/rules/no-namespace.md index c7346515a..5960b39bb 100644 --- a/docs/rules/no-namespace.md +++ b/docs/rules/no-namespace.md @@ -1,4 +1,4 @@ -# import/no-namespace +# import-x/no-namespace 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). @@ -12,7 +12,7 @@ The rule is auto-fixable when the namespace object is only used for direct membe This rule supports the following options: - - `ignore`: array of glob strings for modules that should be ignored by the rule. +- `ignore`: array of glob strings for modules that should be ignored by the rule. ## Rule Details @@ -20,23 +20,23 @@ Valid: ```js import defaultExport from './foo' -import { a, b } from './bar' -import defaultExport, { a, b } from './foobar' +import { a, b } from './bar' +import defaultExport, { a, b } from './foobar' ``` ```js -/* eslint import/no-namespace: ["error", {ignore: ['*.ext']}] */ -import * as bar from './ignored-module.ext'; +/* eslint import-x/no-namespace: ["error", {ignore: ['*.ext']}] */ +import * as bar from './ignored-module.ext' ``` Invalid: ```js -import * as foo from 'foo'; +import * as foo from 'foo' ``` ```js -import defaultExport, * as foo from 'foo'; +import defaultExport, * as foo from 'foo' ``` ## When Not To Use It diff --git a/docs/rules/no-nodejs-modules.md b/docs/rules/no-nodejs-modules.md index 5cbc90728..c72b56268 100644 --- a/docs/rules/no-nodejs-modules.md +++ b/docs/rules/no-nodejs-modules.md @@ -1,4 +1,4 @@ -# import/no-nodejs-modules +# import-x/no-nodejs-modules @@ -8,33 +8,33 @@ Forbid the use of Node.js builtin modules. Can be useful for client-side web pro This rule supports the following options: - - `allow`: Array of names of allowed modules. Defaults to an empty array. +- `allow`: Array of names of allowed modules. Defaults to an empty array. ## Rule Details ### Fail ```js -import fs from 'fs'; -import path from 'path'; +import fs from 'fs' +import path from 'path' -var fs = require('fs'); -var path = require('path'); +var fs = require('fs') +var path = require('path') ``` ### Pass ```js -import _ from 'lodash'; -import foo from 'foo'; -import foo from './foo'; +import _ from 'lodash' +import foo from 'foo' +import foo from './foo' -var _ = require('lodash'); -var foo = require('foo'); -var foo = require('./foo'); +var _ = require('lodash') +var foo = require('foo') +var foo = require('./foo') -/* eslint import/no-nodejs-modules: ["error", {"allow": ["path"]}] */ -import path from 'path'; +/* eslint import-x/no-nodejs-modules: ["error", {"allow": ["path"]}] */ +import path from 'path' ``` ## When Not To Use It diff --git a/docs/rules/no-relative-packages.md b/docs/rules/no-relative-packages.md index 4014ed985..8a4efc707 100644 --- a/docs/rules/no-relative-packages.md +++ b/docs/rules/no-relative-packages.md @@ -1,4 +1,4 @@ -# import/no-relative-packages +# import-x/no-relative-packages 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). @@ -31,7 +31,7 @@ And the .eslintrc file: { ... "rules": { - "import/no-relative-packages": "error" + "import-x/no-relative-packages": "error" } } ``` @@ -43,14 +43,14 @@ The following patterns are considered problems: * in my-project/packages/foo.js */ -import bar from '../bar'; // Import sibling package using relative path -import entry from '../../entry.js'; // Import from parent package using relative path +import bar from '../bar' // Import sibling package using relative path +import entry from '../../entry.js' // Import from parent package using relative path /** * in my-project/entry.js */ -import bar from './packages/bar'; // Import child package using relative path +import bar from './packages/bar' // Import child package using relative path ``` The following patterns are NOT considered problems: @@ -60,11 +60,11 @@ The following patterns are NOT considered problems: * in my-project/packages/foo.js */ -import bar from 'bar'; // Import sibling package using package name +import bar from 'bar' // Import sibling package using package name /** * in my-project/entry.js */ -import bar from 'bar'; // Import sibling package using package name +import bar from 'bar' // Import sibling package using package name ``` diff --git a/docs/rules/no-relative-parent-imports.md b/docs/rules/no-relative-parent-imports.md index c1f978487..999638825 100644 --- a/docs/rules/no-relative-parent-imports.md +++ b/docs/rules/no-relative-parent-imports.md @@ -1,4 +1,4 @@ -# import/no-relative-parent-imports +# import-x/no-relative-parent-imports @@ -21,7 +21,7 @@ export default function (numbers) { } // ./numbers/three.js -import add from '../add'; // violates import/no-relative-parent-imports +import add from '../add'; // violates import-x/no-relative-parent-imports export default function three() { return add([1, 2]); @@ -52,21 +52,21 @@ You can, ```js // three.js export default function three(add) { - return add([1, 2]); + return add([1, 2]) } // somewhere else when you use `three.js`: - import add from './add'; - import three from './numbers/three'; - console.log(three(add)); + import add from './add' + import three from './numbers/three' + console.log(three(add)) ``` 3. Make the dependency a package so it's globally available to all files in your project: ```js - import add from 'add'; // from https://www.npmjs.com/package/add + import add from 'add' // from https://www.npmjs.com/package/add export default function three() { - return add([1,2]); + return add([1, 2]) } ``` @@ -90,7 +90,7 @@ And the .eslintrc file: { ... "rules": { - "import/no-relative-parent-imports": "error" + "import-x/no-relative-parent-imports": "error" } } ``` @@ -102,7 +102,7 @@ The following patterns are considered problems: * in my-project/lib/a.js */ -import bar from '../main'; // Import parent file using a relative path +import bar from '../main' // Import parent file using a relative path ``` The following patterns are NOT considered problems: @@ -112,12 +112,12 @@ The following patterns are NOT considered problems: * in my-project/main.js */ -import foo from 'foo'; // Import package using module path -import a from './lib/a'; // Import child file using relative path +import foo from 'foo' // Import package using module path +import a from './lib/a' // Import child file using relative path /** * in my-project/lib/a.js */ -import b from './b'; // Import sibling file using relative path +import b from './b' // Import sibling file using relative path ``` diff --git a/docs/rules/no-restricted-paths.md b/docs/rules/no-restricted-paths.md index 293f3ba00..51a0c1a84 100644 --- a/docs/rules/no-restricted-paths.md +++ b/docs/rules/no-restricted-paths.md @@ -1,4 +1,4 @@ -# import/no-restricted-paths +# import-x/no-restricted-paths @@ -14,19 +14,19 @@ The default value for `basePath` is the current working directory. Each zone consists of the `target` paths, a `from` paths, and an optional `except` and `message` attribute. - - `target` contains the paths where the restricted imports should be applied. It can be expressed by - - directory string path that matches all its containing files - - glob pattern matching all the targeted files - - an array of multiple of the two types above - - `from` paths define the folders that are not allowed to be used in an import. It can be expressed by - - directory string path that matches all its containing files - - glob pattern matching all the files restricted to be imported - - an array of multiple directory string path - - an array of multiple glob patterns - - `except` may be defined for a zone, allowing exception paths that would otherwise violate the related `from`. Note that it does not alter the behaviour of `target` in any way. - - in case `from` contains only glob patterns, `except` must be an array of glob patterns as well - - in case `from` contains only directory path, `except` is relative to `from` and cannot backtrack to a parent directory - - `message` - will be displayed in case of the rule violation. +- `target` contains the paths where the restricted imports should be applied. It can be expressed by + - directory string path that matches all its containing files + - glob pattern matching all the targeted files + - an array of multiple of the two types above +- `from` paths define the folders that are not allowed to be used in an import. It can be expressed by + - directory string path that matches all its containing files + - glob pattern matching all the files restricted to be imported + - an array of multiple directory string path + - an array of multiple glob patterns +- `except` may be defined for a zone, allowing exception paths that would otherwise violate the related `from`. Note that it does not alter the behaviour of `target` in any way. + - in case `from` contains only glob patterns, `except` must be an array of glob patterns as well + - in case `from` contains only directory path, `except` is relative to `from` and cannot backtrack to a parent directory +- `message` - will be displayed in case of the rule violation. ### Examples @@ -46,16 +46,16 @@ and the current file being linted is `my-project/client/foo.js`. The following patterns are considered problems when configuration set to `{ "zones": [ { "target": "./client", "from": "./server" } ] }`: ```js -import bar from '../server/bar'; +import bar from '../server/bar' ``` The following patterns are not considered problems when configuration set to `{ "zones": [ { "target": "./client", "from": "./server" } ] }`: ```js -import baz from '../client/baz'; +import baz from '../client/baz' ``` ---------------- +--- Given the following folder structure: @@ -76,11 +76,15 @@ and the current file being linted is `my-project/server/one/a.js`. and the current configuration is set to: ```json -{ "zones": [ { - "target": "./tests/files/restricted-paths/server/one", - "from": "./tests/files/restricted-paths/server", - "except": ["./one"] -} ] } +{ + "zones": [ + { + "target": "./tests/files/restricted-paths/server/one", + "from": "./tests/files/restricted-paths/server", + "except": ["./one"] + } + ] +} ``` The following pattern is considered a problem: @@ -93,10 +97,9 @@ The following pattern is not considered a problem: ```js import b from './b' - ``` ---------------- +--- Given the following folder structure: @@ -113,10 +116,14 @@ my-project and the current configuration is set to: ```json -{ "zones": [ { - "target": "./tests/files/restricted-paths/client/!(sub-module)/**/*", - "from": "./tests/files/restricted-paths/client/sub-module/**/*", -} ] } +{ + "zones": [ + { + "target": "./tests/files/restricted-paths/client/!(sub-module)/**/*", + "from": "./tests/files/restricted-paths/client/sub-module/**/*" + } + ] +} ``` The following import is considered a problem in `my-project/client/foo.js`: @@ -131,7 +138,7 @@ The following import is not considered a problem in `my-project/client/sub-modul import b from './baz' ``` ---------------- +--- Given the following folder structure: @@ -154,8 +161,14 @@ and the current configuration is set to: { "zones": [ { - "target": ["./tests/files/restricted-paths/two/*", "./tests/files/restricted-paths/three/*"], - "from": ["./tests/files/restricted-paths/one", "./tests/files/restricted-paths/three"], + "target": [ + "./tests/files/restricted-paths/two/*", + "./tests/files/restricted-paths/three/*" + ], + "from": [ + "./tests/files/restricted-paths/one", + "./tests/files/restricted-paths/three" + ] } ] } diff --git a/docs/rules/no-self-import.md b/docs/rules/no-self-import.md index 8d8491c50..e3b0b5a38 100644 --- a/docs/rules/no-self-import.md +++ b/docs/rules/no-self-import.md @@ -1,4 +1,4 @@ -# import/no-self-import +# import-x/no-self-import @@ -10,23 +10,23 @@ Forbid a module from importing itself. This can sometimes happen during refactor ```js // foo.js -import foo from './foo'; +import foo from './foo' -const foo = require('./foo'); +const foo = require('./foo') ``` ```js // index.js -import index from '.'; +import index from '.' -const index = require('.'); +const index = require('.') ``` ### Pass ```js // foo.js -import bar from './bar'; +import bar from './bar' -const bar = require('./bar'); +const bar = require('./bar') ``` diff --git a/docs/rules/no-unassigned-import.md b/docs/rules/no-unassigned-import.md index 617395e2c..a9393ecb8 100644 --- a/docs/rules/no-unassigned-import.md +++ b/docs/rules/no-unassigned-import.md @@ -1,11 +1,11 @@ -# import/no-unassigned-import +# import-x/no-unassigned-import With both CommonJS' `require` and the ES6 modules' `import` syntax, it is possible to import a module but not to use its result. This can be done explicitly by not assigning the module to as variable. Doing so can mean either of the following things: - - The module is imported but not used - - The module has side-effects (like [`should`](https://www.npmjs.com/package/should)). Having side-effects, makes it hard to know whether the module is actually used or can be removed. It can also make it harder to test or mock parts of your application. +- The module is imported but not used +- The module has side-effects (like [`should`](https://www.npmjs.com/package/should)). Having side-effects, makes it hard to know whether the module is actually used or can be removed. It can also make it harder to test or mock parts of your application. This rule aims to remove modules with side-effects by reporting when a module is imported but not assigned. @@ -32,14 +32,14 @@ import '../styles/app.css' ```js import _ from 'foo' -import _, {foo} from 'foo' -import _, {foo as bar} from 'foo' -import {foo as bar} from 'foo' +import _, { foo } from 'foo' +import _, { foo as bar } from 'foo' +import { foo as bar } from 'foo' import * as _ from 'foo' const _ = require('foo') -const {foo} = require('foo') -const {foo: bar} = require('foo') +const { foo } = require('foo') +const { foo: bar } = require('foo') const [a, b] = require('foo') const _ = require('foo') diff --git a/docs/rules/no-unresolved.md b/docs/rules/no-unresolved.md index ca1da39c0..3fd7b993a 100644 --- a/docs/rules/no-unresolved.md +++ b/docs/rules/no-unresolved.md @@ -1,4 +1,4 @@ -# import/no-unresolved +# import-x/no-unresolved 💼 This rule is enabled in the following configs: ❗ `errors`, ☑️ `recommended`. @@ -24,27 +24,33 @@ If you are using Webpack, see the section on [resolvers](../../README.md#resolve By default, only ES6 imports will be resolved: ```js -/*eslint import/no-unresolved: 2*/ +/*eslint import-x/no-unresolved: 2*/ import x from './foo' // reports if './foo' cannot be resolved on the filesystem ``` If `{commonjs: true}` is provided, single-argument `require` calls will be resolved: ```js -/*eslint import/no-unresolved: [2, { commonjs: true }]*/ +/*eslint import-x/no-unresolved: [2, { commonjs: true }]*/ const { default: x } = require('./foo') // reported if './foo' is not found require(0) // ignored -require(['x', 'y'], function (x, y) { /*...*/ }) // ignored +require(['x', 'y'], function (x, y) { + /*...*/ +}) // ignored ``` Similarly, if `{ amd: true }` is provided, dependency paths for `define` and `require` calls will be resolved: ```js -/*eslint import/no-unresolved: [2, { amd: true }]*/ -define(['./foo'], function (foo) { /*...*/ }) // reported if './foo' is not found -require(['./foo'], function (foo) { /*...*/ }) // reported if './foo' is not found +/*eslint import-x/no-unresolved: [2, { amd: true }]*/ +define(['./foo'], function (foo) { + /*...*/ +}) // reported if './foo' is not found +require(['./foo'], function (foo) { + /*...*/ +}) // reported if './foo' is not found const { default: x } = require('./foo') // ignored ``` @@ -52,20 +58,24 @@ const { default: x } = require('./foo') // ignored Both may be provided, too: ```js -/*eslint import/no-unresolved: [2, { commonjs: true, amd: true }]*/ +/*eslint import-x/no-unresolved: [2, { commonjs: true, amd: true }]*/ const { default: x } = require('./foo') // reported if './foo' is not found -define(['./foo'], function (foo) { /*...*/ }) // reported if './foo' is not found -require(['./foo'], function (foo) { /*...*/ }) // reported if './foo' is not found +define(['./foo'], function (foo) { + /*...*/ +}) // reported if './foo' is not found +require(['./foo'], function (foo) { + /*...*/ +}) // reported if './foo' is not found ``` #### `ignore` -This rule has its own ignore list, separate from [`import/ignore`]. This is because you may want to know whether a module can be located, regardless of whether it can be parsed for exports: `node_modules`, CoffeeScript files, etc. are all good to resolve properly, but will not be parsed if configured as such via [`import/ignore`]. +This rule has its own ignore list, separate from [`import-x/ignore`]. This is because you may want to know whether a module can be located, regardless of whether it can be parsed for exports: `node_modules`, CoffeeScript files, etc. are all good to resolve properly, but will not be parsed if configured as such via [`import-x/ignore`]. To suppress errors from files that may not be properly resolved by your [resolver settings](../../README.md#resolver-plugins), you may add an `ignore` key with an array of `RegExp` pattern strings: ```js -/*eslint import/no-unresolved: [2, { ignore: ['\\.img$'] }]*/ +/*eslint import-x/no-unresolved: [2, { ignore: ['\\.img$'] }]*/ import { x } from './mod' // may be reported, if not resolved to a module @@ -77,7 +87,7 @@ import coolImg from '../../img/coolImg.img' // will not be reported, even if not By default, this rule will report paths whose case do not match the underlying filesystem path, if the FS is not case-sensitive. To disable this behavior, set the `caseSensitive` option to `false`. ```js -/*eslint import/no-unresolved: [2, { caseSensitive: true (default) | false }]*/ +/*eslint import-x/no-unresolved: [2, { caseSensitive: true (default) | false }]*/ const { default: x } = require('./foo') // reported if './foo' is actually './Foo' and caseSensitive: true ``` @@ -86,7 +96,7 @@ const { default: x } = require('./foo') // reported if './foo' is actually './Fo The `caseSensitive` option does not detect case for the current working directory. The `caseSensitiveStrict` option allows checking `cwd` in resolved path. By default, the option is disabled. ```js -/*eslint import/no-unresolved: [2, { caseSensitiveStrict: true }]*/ +/*eslint import-x/no-unresolved: [2, { caseSensitiveStrict: true }]*/ // Absolute paths import Foo from `/Users/fOo/bar/file.js` // reported, /Users/foo/bar/file.js @@ -102,9 +112,9 @@ If you're using a module bundler other than Node or Webpack, you may end up with ## Further Reading - - [Resolver plugins](../../README.md#resolvers) - - [Node resolver](https://npmjs.com/package/eslint-import-resolver-node) (default) - - [Webpack resolver](https://npmjs.com/package/eslint-import-resolver-webpack) - - [`import/ignore`] global setting +- [Resolver plugins](../../README.md#resolvers) +- [Node resolver](https://npmjs.com/package/eslint-import-resolver-node) (default) +- [Webpack resolver](https://npmjs.com/package/eslint-import-resolver-webpack) +- [`import-x/ignore`] global setting -[`import/ignore`]: ../../README.md#importignore +[`import-x/ignore`]: ../../README.md#importignore diff --git a/docs/rules/no-unused-modules.md b/docs/rules/no-unused-modules.md index 53c247927..b11becdbf 100644 --- a/docs/rules/no-unused-modules.md +++ b/docs/rules/no-unused-modules.md @@ -1,12 +1,12 @@ -# import/no-unused-modules +# import-x/no-unused-modules Reports: - - modules without any exports - - individual exports not being statically `import`ed or `require`ed from other modules in the same project - - dynamic imports are supported if argument is a literal string +- modules without any exports +- individual exports not being statically `import`ed or `require`ed from other modules in the same project +- dynamic imports are supported if argument is a literal string ## Rule Details @@ -19,7 +19,7 @@ Example: ```json "rules: { ...otherRules, - "import/no-unused-modules": [1, {"unusedExports": true}] + "import-x/no-unused-modules": [1, {"unusedExports": true}] } ``` @@ -27,10 +27,10 @@ Example: This rule takes the following option: - - **`missingExports`**: if `true`, files without any exports are reported (defaults to `false`) - - **`unusedExports`**: if `true`, exports without any static usage within other modules are reported (defaults to `false`) - - `src`: an array with files/paths to be analyzed. It only applies to unused exports. Defaults to `process.cwd()`, if not provided - - `ignoreExports`: an array with files/paths for which unused exports will not be reported (e.g module entry points in a published package) +- **`missingExports`**: if `true`, files without any exports are reported (defaults to `false`) +- **`unusedExports`**: if `true`, exports without any static usage within other modules are reported (defaults to `false`) +- `src`: an array with files/paths to be analyzed. It only applies to unused exports. Defaults to `process.cwd()`, if not provided +- `ignoreExports`: an array with files/paths for which unused exports will not be reported (e.g module entry points in a published package) ### Example for missing exports @@ -45,11 +45,15 @@ function makeClass() { return new MyClass(...arguments) } #### The following will not be reported ```js -export default function () { /*...*/ } +export default function () { + /*...*/ +} ``` ```js -export const foo = function () { /*...*/ } +export const foo = function () { + /*...*/ +} ``` ```js @@ -67,7 +71,7 @@ given file-f: ```js import { e } from 'file-a' import { f } from 'file-b' -import * as fileC from 'file-c' +import * as fileC from 'file-c' export { default, i0 } from 'file-d' // both will be reported export const j = 99 // will be reported @@ -111,7 +115,7 @@ export { d as e } // will not be reported export function doAnything() { // some code -} // will not be reported +} // will not be reported export default 5 // will not be reported ``` diff --git a/docs/rules/no-useless-path-segments.md b/docs/rules/no-useless-path-segments.md index 22c4bf965..9c3e787dc 100644 --- a/docs/rules/no-useless-path-segments.md +++ b/docs/rules/no-useless-path-segments.md @@ -1,4 +1,4 @@ -# import/no-useless-path-segments +# import-x/no-useless-path-segments 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). @@ -32,14 +32,14 @@ The following patterns are considered problems: * in my-project/app.js */ -import "./../my-project/pages/about.js"; // should be "./pages/about.js" -import "./../my-project/pages/about"; // should be "./pages/about" -import "../my-project/pages/about.js"; // should be "./pages/about.js" -import "../my-project/pages/about"; // should be "./pages/about" -import "./pages//about"; // should be "./pages/about" -import "./pages/"; // should be "./pages" -import "./pages/index"; // should be "./pages" (except if there is a ./pages.js file) -import "./pages/index.js"; // should be "./pages" (except if there is a ./pages.js file) +import './../my-project/pages/about.js' // should be "./pages/about.js" +import './../my-project/pages/about' // should be "./pages/about" +import '../my-project/pages/about.js' // should be "./pages/about.js" +import '../my-project/pages/about' // should be "./pages/about" +import './pages//about' // should be "./pages/about" +import './pages/' // should be "./pages" +import './pages/index' // should be "./pages" (except if there is a ./pages.js file) +import './pages/index.js' // should be "./pages" (except if there is a ./pages.js file) ``` The following patterns are NOT considered problems: @@ -49,12 +49,12 @@ The following patterns are NOT considered problems: * in my-project/app.js */ -import "./header.js"; -import "./pages"; -import "./pages/about"; -import "."; -import ".."; -import fs from "fs"; +import './header.js' +import './pages' +import './pages/about' +import '.' +import '..' +import fs from 'fs' ``` ## Options @@ -64,7 +64,7 @@ import fs from "fs"; If you want to detect unnecessary `/index` or `/index.js` (depending on the specified file extensions, see below) imports in your paths, you can enable the option `noUselessIndex`. By default it is set to `false`: ```js -"import/no-useless-path-segments": ["error", { +"import-x/no-useless-path-segments": ["error", { noUselessIndex: true, }] ``` @@ -73,12 +73,12 @@ Additionally to the patterns described above, the following imports are consider ```js // in my-project/app.js -import "./helpers/index"; // should be "./helpers/" (not auto-fixable to `./helpers` because this would lead to an ambiguous import of `./helpers.js` and `./helpers/index.js`) -import "./pages/index"; // should be "./pages" (auto-fixable) -import "./pages/index.js"; // should be "./pages" (auto-fixable) +import './helpers/index' // should be "./helpers/" (not auto-fixable to `./helpers` because this would lead to an ambiguous import of `./helpers.js` and `./helpers/index.js`) +import './pages/index' // should be "./pages" (auto-fixable) +import './pages/index.js' // should be "./pages" (auto-fixable) ``` -Note: `noUselessIndex` only avoids ambiguous imports for `.js` files if you haven't specified other resolved file extensions. See [Settings: import/extensions](https://github.com/import-js/eslint-plugin-import#importextensions) for details. +Note: `noUselessIndex` only avoids ambiguous imports for `.js` files if you haven't specified other resolved file extensions. See [Settings: import-x/extensions](https://github.com/import-js/eslint-plugin-import#importextensions) for details. ### commonjs diff --git a/docs/rules/no-webpack-loader-syntax.md b/docs/rules/no-webpack-loader-syntax.md index 291b1c058..56223247c 100644 --- a/docs/rules/no-webpack-loader-syntax.md +++ b/docs/rules/no-webpack-loader-syntax.md @@ -1,4 +1,4 @@ -# import/no-webpack-loader-syntax +# import-x/no-webpack-loader-syntax @@ -7,7 +7,7 @@ Forbid Webpack loader syntax in imports. [Webpack](https://webpack.js.org) allows specifying the [loaders](https://webpack.js.org/concepts/loaders/) to use in the import source string using a special syntax like this: ```js -var moduleWithOneLoader = require("my-loader!./my-awesome-module"); +var moduleWithOneLoader = require('my-loader!./my-awesome-module') ``` This syntax is non-standard, so it couples the code to Webpack. The recommended way to specify Webpack loader configuration is in a [Webpack configuration file](https://webpack.js.org/concepts/loaders/#configuration). @@ -17,21 +17,21 @@ This syntax is non-standard, so it couples the code to Webpack. The recommended ### Fail ```js -import myModule from 'my-loader!my-module'; -import theme from 'style!css!./theme.css'; +import myModule from 'my-loader!my-module' +import theme from 'style!css!./theme.css' -var myModule = require('my-loader!./my-module'); -var theme = require('style!css!./theme.css'); +var myModule = require('my-loader!./my-module') +var theme = require('style!css!./theme.css') ``` ### Pass ```js -import myModule from 'my-module'; -import theme from './theme.css'; +import myModule from 'my-module' +import theme from './theme.css' -var myModule = require('my-module'); -var theme = require('./theme.css'); +var myModule = require('my-module') +var theme = require('./theme.css') ``` ## When Not To Use It diff --git a/docs/rules/order.md b/docs/rules/order.md index 2335699e6..97aa3737c 100644 --- a/docs/rules/order.md +++ b/docs/rules/order.md @@ -1,4 +1,4 @@ -# import/order +# import-x/order 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). @@ -10,26 +10,26 @@ With the [`groups`](#groups-array) option set to `["builtin", "external", "inter ```ts // 1. node "builtin" modules -import fs from 'fs'; -import path from 'path'; +import fs from 'fs' +import path from 'path' // 2. "external" modules -import _ from 'lodash'; -import chalk from 'chalk'; +import _ from 'lodash' +import chalk from 'chalk' // 3. "internal" modules // (if you have configured your path or webpack to handle your internal paths differently) -import foo from 'src/foo'; +import foo from 'src/foo' // 4. modules from a "parent" directory -import foo from '../foo'; -import qux from '../../foo/qux'; +import foo from '../foo' +import qux from '../../foo/qux' // 5. "sibling" modules from the same or a sibling's directory -import bar from './bar'; -import baz from './bar/baz'; +import bar from './bar' +import baz from './bar/baz' // 6. "index" of the current directory -import main from './'; +import main from './' // 7. "object"-imports (only available in TypeScript) -import log = console.log; +import log = console.log // 8. "type" imports (only available in Flow and TypeScript) -import type { Foo } from 'foo'; +import type { Foo } from 'foo' ``` Unassigned imports are ignored, as the order they are imported in may be important. @@ -39,42 +39,42 @@ Statements using the ES6 `import` syntax must appear before any `require()` stat ## Fail ```ts -import _ from 'lodash'; -import path from 'path'; // `path` import should occur before import of `lodash` +import _ from 'lodash' +import path from 'path' // `path` import should occur before import of `lodash` // ----- -var _ = require('lodash'); -var path = require('path'); // `path` import should occur before import of `lodash` +var _ = require('lodash') +var path = require('path') // `path` import should occur before import of `lodash` // ----- -var path = require('path'); -import foo from './foo'; // `import` statements must be before `require` statement +var path = require('path') +import foo from './foo' // `import` statements must be before `require` statement ``` ## Pass ```ts -import path from 'path'; -import _ from 'lodash'; +import path from 'path' +import _ from 'lodash' // ----- -var path = require('path'); -var _ = require('lodash'); +var path = require('path') +var _ = require('lodash') // ----- // Allowed as ̀`babel-register` is not assigned. -require('babel-register'); -var path = require('path'); +require('babel-register') +var path = require('path') // ----- // Allowed as `import` must be before `require` -import foo from './foo'; -var path = require('path'); +import foo from './foo' +var path = require('path') ``` ## Options @@ -88,7 +88,7 @@ How groups are defined, and the order to respect. `groups` must be an array of ` The enforced order is the same as the order of each element in a group. Omitted types are implicitly grouped together as the last element. Example: ```ts -[ +;[ 'builtin', // Built-in types are first ['sibling', 'parent'], // Then sibling and parent types. They can be mingled together 'index', // Then the index file @@ -102,7 +102,7 @@ The default value is `["builtin", "external", "parent", "sibling", "index"]`. You can set the options like this: ```ts -"import/order": [ +"import-x/order": [ "error", { "groups": [ @@ -125,23 +125,26 @@ To be able to group by paths mostly needed with aliases pathGroups can be define Properties of the objects -| property | required | type | description | -|----------------|:--------:|--------|---------------| -| pattern | x | string | minimatch pattern for the paths to be in this group (will not be used for builtins or externals) | -| patternOptions | | object | options for minimatch, default: { nocomment: true } | -| group | x | string | one of the allowed groups, the pathGroup will be positioned relative to this group | +| property | required | type | description | +| -------------- | :------: | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| pattern | x | string | minimatch pattern for the paths to be in this group (will not be used for builtins or externals) | +| patternOptions | | object | options for minimatch, default: { nocomment: true } | +| group | x | string | one of the allowed groups, the pathGroup will be positioned relative to this group | | position | | string | defines where around the group the pathGroup will be positioned, can be 'after' or 'before', if not provided pathGroup will be positioned like the group | ```json { - "import/order": ["error", { - "pathGroups": [ - { - "pattern": "~/**", - "group": "external" - } - ] - }] + "import-x/order": [ + "error", + { + "pathGroups": [ + { + "pattern": "~/**", + "group": "external" + } + ] + } + ] } ``` @@ -157,17 +160,20 @@ Example: ```json { - "import/order": ["error", { - "newlines-between": "always", - "pathGroups": [ - { - "pattern": "@app/**", - "group": "external", - "position": "after" - } - ], - "distinctGroup": false - }] + "import-x/order": [ + "error", + { + "newlines-between": "always", + "pathGroups": [ + { + "pattern": "@app/**", + "group": "external", + "position": "after" + } + ], + "distinctGroup": false + } + ] } ``` @@ -180,16 +186,19 @@ Example: ```json { - "import/order": ["error", { - "pathGroups": [ - { - "pattern": "@app/**", - "group": "external", - "position": "after" - } - ], - "pathGroupsExcludedImportTypes": ["builtin"] - }] + "import-x/order": [ + "error", + { + "pathGroups": [ + { + "pattern": "@app/**", + "group": "external", + "position": "after" + } + ], + "pathGroupsExcludedImportTypes": ["builtin"] + } + ] } ``` @@ -199,7 +208,7 @@ Example: ```json { - "import/order": [ + "import-x/order": [ "error", { "pathGroups": [ @@ -221,80 +230,80 @@ The default value is `["builtin", "external", "object"]`. Enforces or forbids new lines between import groups: - - If set to `ignore`, no errors related to new lines between import groups will be reported. - - If set to `always`, at least one new line between each group will be enforced, and new lines inside a group will be forbidden. To prevent multiple lines between imports, core `no-multiple-empty-lines` rule can be used. - - If set to `always-and-inside-groups`, it will act like `always` except newlines are allowed inside import groups. - - If set to `never`, no new lines are allowed in the entire import section. +- If set to `ignore`, no errors related to new lines between import groups will be reported. +- If set to `always`, at least one new line between each group will be enforced, and new lines inside a group will be forbidden. To prevent multiple lines between imports, core `no-multiple-empty-lines` rule can be used. +- If set to `always-and-inside-groups`, it will act like `always` except newlines are allowed inside import groups. +- If set to `never`, no new lines are allowed in the entire import section. The default value is `"ignore"`. With the default group setting, the following will be invalid: ```ts -/* eslint import/order: ["error", {"newlines-between": "always"}] */ -import fs from 'fs'; -import path from 'path'; -import index from './'; -import sibling from './foo'; +/* eslint import-x/order: ["error", {"newlines-between": "always"}] */ +import fs from 'fs' +import path from 'path' +import index from './' +import sibling from './foo' ``` ```ts -/* eslint import/order: ["error", {"newlines-between": "always-and-inside-groups"}] */ -import fs from 'fs'; +/* eslint import-x/order: ["error", {"newlines-between": "always-and-inside-groups"}] */ +import fs from 'fs' -import path from 'path'; -import index from './'; -import sibling from './foo'; +import path from 'path' +import index from './' +import sibling from './foo' ``` ```ts -/* eslint import/order: ["error", {"newlines-between": "never"}] */ -import fs from 'fs'; -import path from 'path'; +/* eslint import-x/order: ["error", {"newlines-between": "never"}] */ +import fs from 'fs' +import path from 'path' -import index from './'; +import index from './' -import sibling from './foo'; +import sibling from './foo' ``` while those will be valid: ```ts -/* eslint import/order: ["error", {"newlines-between": "always"}] */ -import fs from 'fs'; -import path from 'path'; +/* eslint import-x/order: ["error", {"newlines-between": "always"}] */ +import fs from 'fs' +import path from 'path' -import index from './'; +import index from './' -import sibling from './foo'; +import sibling from './foo' ``` ```ts -/* eslint import/order: ["error", {"newlines-between": "always-and-inside-groups"}] */ -import fs from 'fs'; +/* eslint import-x/order: ["error", {"newlines-between": "always-and-inside-groups"}] */ +import fs from 'fs' -import path from 'path'; +import path from 'path' -import index from './'; +import index from './' -import sibling from './foo'; +import sibling from './foo' ``` ```ts -/* eslint import/order: ["error", {"newlines-between": "never"}] */ -import fs from 'fs'; -import path from 'path'; -import index from './'; -import sibling from './foo'; +/* eslint import-x/order: ["error", {"newlines-between": "never"}] */ +import fs from 'fs' +import path from 'path' +import index from './' +import sibling from './foo' ``` ### `alphabetize: {order: asc|desc|ignore, orderImportKind: asc|desc|ignore, caseInsensitive: true|false}` Sort the order within each group in alphabetical manner based on **import path**: - - `order`: use `asc` to sort in ascending order, and `desc` to sort in descending order (default: `ignore`). - - `orderImportKind`: use `asc` to sort in ascending order various import kinds, e.g. imports prefixed with `type` or `typeof`, with same import path. Use `desc` to sort in descending order (default: `ignore`). - - `caseInsensitive`: use `true` to ignore case, and `false` to consider case (default: `false`). +- `order`: use `asc` to sort in ascending order, and `desc` to sort in descending order (default: `ignore`). +- `orderImportKind`: use `asc` to sort in ascending order various import kinds, e.g. imports prefixed with `type` or `typeof`, with same import path. Use `desc` to sort in descending order (default: `ignore`). +- `caseInsensitive`: use `true` to ignore case, and `false` to consider case (default: `false`). Example setting: @@ -308,30 +317,30 @@ alphabetize: { This will fail the rule check: ```ts -/* eslint import/order: ["error", {"alphabetize": {"order": "asc", "caseInsensitive": true}}] */ -import React, { PureComponent } from 'react'; -import aTypes from 'prop-types'; -import { compose, apply } from 'xcompose'; -import * as classnames from 'classnames'; -import blist from 'BList'; +/* eslint import-x/order: ["error", {"alphabetize": {"order": "asc", "caseInsensitive": true}}] */ +import React, { PureComponent } from 'react' +import aTypes from 'prop-types' +import { compose, apply } from 'xcompose' +import * as classnames from 'classnames' +import blist from 'BList' ``` While this will pass: ```ts -/* eslint import/order: ["error", {"alphabetize": {"order": "asc", "caseInsensitive": true}}] */ -import blist from 'BList'; -import * as classnames from 'classnames'; -import aTypes from 'prop-types'; -import React, { PureComponent } from 'react'; -import { compose, apply } from 'xcompose'; +/* eslint import-x/order: ["error", {"alphabetize": {"order": "asc", "caseInsensitive": true}}] */ +import blist from 'BList' +import * as classnames from 'classnames' +import aTypes from 'prop-types' +import React, { PureComponent } from 'react' +import { compose, apply } from 'xcompose' ``` ### `warnOnUnassignedImports: true|false` - - default: `false` +- default: `false` -Warns when unassigned imports are out of order. These warning will not be fixed +Warns when unassigned imports are out of order. These warning will not be fixed with `--fix` because unassigned imports are used for side-effects and changing the import of order of modules with side effects can not be done automatically in a way that is safe. @@ -339,27 +348,26 @@ way that is safe. This will fail the rule check: ```ts -/* eslint import/order: ["error", {"warnOnUnassignedImports": true}] */ -import fs from 'fs'; -import './styles.css'; -import path from 'path'; +/* eslint import-x/order: ["error", {"warnOnUnassignedImports": true}] */ +import fs from 'fs' +import './styles.css' +import path from 'path' ``` While this will pass: ```ts -/* eslint import/order: ["error", {"warnOnUnassignedImports": true}] */ -import fs from 'fs'; -import path from 'path'; -import './styles.css'; +/* eslint import-x/order: ["error", {"warnOnUnassignedImports": true}] */ +import fs from 'fs' +import path from 'path' +import './styles.css' ``` ## Related - - [`import/external-module-folders`] setting - - - [`import/internal-regex`] setting +- [`import-x/external-module-folders`] setting -[`import/external-module-folders`]: ../../README.md#importexternal-module-folders +- [`import-x/internal-regex`] setting -[`import/internal-regex`]: ../../README.md#importinternal-regex +[`import-x/external-module-folders`]: ../../README.md#importexternal-module-folders +[`import-x/internal-regex`]: ../../README.md#importinternal-regex diff --git a/docs/rules/prefer-default-export.md b/docs/rules/prefer-default-export.md index e2a7bacd7..22c8111c6 100644 --- a/docs/rules/prefer-default-export.md +++ b/docs/rules/prefer-default-export.md @@ -1,4 +1,4 @@ -# import/prefer-default-export +# import-x/prefer-default-export @@ -9,7 +9,7 @@ In exporting files, this rule checks if there is default export or not. ### rule schema ```javascript -"import/prefer-default-export": [ +"import-x/prefer-default-export": [ ( "off" | "warn" | "error" ), { "target": "single" | "any" } // default is "single" ] @@ -28,7 +28,7 @@ How to setup config file for this rule: ```javascript // you can manually specify it "rules": { - "import/prefer-default-export": [ + "import-x/prefer-default-export": [ ( "off" | "warn" | "error" ), { "target": "single" } ] @@ -36,7 +36,7 @@ How to setup config file for this rule: // config setup below will also work "rules": { - "import/prefer-default-export": "off" | "warn" | "error" + "import-x/prefer-default-export": "off" | "warn" | "error" } ``` @@ -46,8 +46,7 @@ The following patterns are considered warnings: // bad.js // There is only a single module export and it's a named export. -export const foo = 'foo'; - +export const foo = 'foo' ``` The following patterns are not warnings: @@ -56,25 +55,25 @@ The following patterns are not warnings: // good1.js // There is a default export. -export const foo = 'foo'; -const bar = 'bar'; -export default bar; +export const foo = 'foo' +const bar = 'bar' +export default bar ``` ```javascript // good2.js // There is more than one named export in the module. -export const foo = 'foo'; -export const bar = 'bar'; +export const foo = 'foo' +export const bar = 'bar' ``` ```javascript // good3.js // There is more than one named export in the module -const foo = 'foo'; -const bar = 'bar'; +const foo = 'foo' +const bar = 'bar' export { foo, bar } ``` @@ -82,7 +81,7 @@ export { foo, bar } // good4.js // There is a default export. -const foo = 'foo'; +const foo = 'foo' export { foo as default } ``` @@ -102,27 +101,27 @@ How to setup config file for this rule: ```javascript // you have to manually specify it "rules": { - "import/prefer-default-export": [ + "import-x/prefer-default-export": [ ( "off" | "warn" | "error" ), { "target": "any" } ] } ``` -The following patterns are *not* considered warnings: +The following patterns are _not_ considered warnings: ```javascript // good1.js //has default export -export default function bar() {}; +export default function bar() {} ``` ```javascript // good2.js // has default export -let foo; +let foo export { foo as default } ``` @@ -130,9 +129,9 @@ export { foo as default } // good3.js //contains multiple exports AND default export -export const a = 5; -export function bar(){}; -let foo; +export const a = 5 +export function bar() {} +let foo export { foo as default } ``` @@ -140,7 +139,7 @@ export { foo as default } // good4.js // does not contain any exports => file is not checked by the rule -import * as foo from './foo'; +import * as foo from './foo' ``` ```javascript @@ -156,15 +155,15 @@ The following patterns are considered warnings: // bad1.js //has 2 named exports, but no default export -export const foo = 'foo'; -export const bar = 'bar'; +export const foo = 'foo' +export const bar = 'bar' ``` ```javascript // bad2.js // does not have default export -let foo, bar; +let foo, bar export { foo, bar } ``` @@ -172,14 +171,14 @@ export { foo, bar } // bad3.js // does not have default export -export { a, b } from "foo.js" +export { a, b } from 'foo.js' ``` ```javascript // bad4.js // does not have default export -let item; -export const foo = item; -export { item }; +let item +export const foo = item +export { item } ``` diff --git a/docs/rules/unambiguous.md b/docs/rules/unambiguous.md index e9e5bf73d..31f73b776 100644 --- a/docs/rules/unambiguous.md +++ b/docs/rules/unambiguous.md @@ -1,4 +1,4 @@ -# import/unambiguous +# import-x/unambiguous @@ -19,22 +19,30 @@ For files parsed as `module` by ESLint, the following are valid: ```js import 'foo' -function x() { return 42 } +function x() { + return 42 +} ``` ```js -export function x() { return 42 } +export function x() { + return 42 +} ``` ```js -(function x() { return 42 })() +;(function x() { + return 42 +})() export {} // simple way to mark side-effects-only file as 'module' without any imports/exports ``` ...whereas the following file would be reported: ```js -(function x() { return 42 })() +;(function x() { + return 42 +})() ``` ## When Not To Use It @@ -49,9 +57,9 @@ a `module`. ## Further Reading - - [Unambiguous JavaScript Grammar] - - [`parserOptions.sourceType`] - - [node-eps#13](https://github.com/nodejs/node-eps/issues/13) +- [Unambiguous JavaScript Grammar] +- [`parserOptions.sourceType`] +- [node-eps#13](https://github.com/nodejs/node-eps/issues/13) [`parserOptions.sourceType`]: https://eslint.org/docs/user-guide/configuring#specifying-parser-options [Unambiguous JavaScript Grammar]: https://github.com/nodejs/node-eps/blob/HEAD/002-es-modules.md#32-determining-if-source-is-an-es-module diff --git a/import.sublime-project b/import.sublime-project deleted file mode 100644 index 3b7feb346..000000000 --- a/import.sublime-project +++ /dev/null @@ -1,27 +0,0 @@ -{ - "folders": - [ - { - "path": ".", - "folder_exclude_patterns": ["coverage", "node_modules", "lib"] - } - ], - "SublimeLinter": - { - "linters": - { - "eslint": - { - "chdir": "${project}" - }, - "eslint_d": - { - "disable": true, - "chdir": "${project}" - } - }, - "paths": { - "osx": ["${project}/node_modules/.bin"] - } - } -} diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 000000000..d66621954 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,19 @@ +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + collectCoverage: true, + modulePathIgnorePatterns: ['/test/fixtures/with-syntax-error'], + moduleNameMapper: { + '^core/(.+)$': '/src/core/$1', + '^eslint-plugin-import-x$': '/src/index.js', + '^eslint-plugin-import-x/config/(.+)$': '/config/$1', + '^eslint-plugin-import-x/package.json$': '/package.json', + '^eslint-plugin-import-x/(.+)$': '/src/$1', + '^rules/(.+)$': '/src/rules/$1', + }, + testMatch: [ + '/test/**/*.spec.js', + '!/test/fixtures/**/*.js', + ], +} diff --git a/memo-parser/.nycrc b/memo-parser/.nycrc deleted file mode 100644 index 5d75e2157..000000000 --- a/memo-parser/.nycrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "all": true, - "check-coverage": false, - "reporter": ["text-summary", "lcov", "text", "html", "json"], - "require": [ - "babel-register" - ], - "sourceMap": true, - "instrument": false, - "exclude": [ - "coverage", - "test", - "tests", - "resolvers/*/test", - "scripts", - "memo-parser", - "lib" - ] -} diff --git a/memo-parser/README.md b/memo-parser/README.md deleted file mode 100644 index 741e0ed4d..000000000 --- a/memo-parser/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# eslint-plugin-import/memo-parser - -## NOTE - -This used to improve performance, but as of ESLint 5 and v2 of this plugin, it seems to just consume a bunch of memory and slightly increase lint times. - -**Not recommended for use at this time!** - -This parser is just a memoizing wrapper around some actual parser. - -To configure, just add your _actual_ parser to the `parserOptions`, like so: - -```yaml -parser: eslint-plugin-import/memo-parser -# parser: babel-eslint - -parserOptions: - parser: babel-eslint - sourceType: module - ecmaVersion: 6 -``` diff --git a/memo-parser/index.js b/memo-parser/index.js deleted file mode 100644 index 7868b7e95..000000000 --- a/memo-parser/index.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict'; - -const crypto = require('crypto'); -const moduleRequire = require('eslint-module-utils/module-require').default; -const hashObject = require('eslint-module-utils/hash').hashObject; - -const cache = new Map(); - -// must match ESLint default options or we'll miss the cache every time -const parserOptions = { - loc: true, - range: true, - raw: true, - tokens: true, - comment: true, - attachComment: true, -}; - -exports.parse = function parse(content, options) { - options = { ...options, ...parserOptions }; - - if (!options.filePath) { - throw new Error('no file path provided!'); - } - - const keyHash = crypto.createHash('sha256'); - keyHash.update(content); - hashObject(options, keyHash); - - const key = keyHash.digest('hex'); - - let ast = cache.get(key); - if (ast != null) { return ast; } - - const realParser = moduleRequire(options.parser); - - ast = realParser.parse(content, options); - cache.set(key, ast); - - return ast; -}; diff --git a/memo-parser/package.json b/memo-parser/package.json deleted file mode 100644 index 723005d21..000000000 --- a/memo-parser/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "memo-parser", - "version": "0.2.1", - "engines": { - "node": ">=4" - }, - "description": "Memoizing wrapper for any ESLint-compatible parser module.", - "main": "index.js", - "scripts": { - "prepublishOnly": "cp ../{LICENSE,.npmrc} ./", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/import-js/eslint-plugin-import.git" - }, - "keywords": [ - "eslint", - "eslint-plugin-import", - "parser" - ], - "author": "Ben Mosher (me@benmosher.com)", - "license": "MIT", - "bugs": { - "url": "https://github.com/import-js/eslint-plugin-import/issues" - }, - "homepage": "https://github.com/import-js/eslint-plugin-import#readme", - "peerDependencies": { - "eslint": ">=3.5.0" - }, - "dependencies": { - "eslint-module-utils": "^2.6.1" - } -} diff --git a/package.json b/package.json index 638942f97..7a26d4d5a 100644 --- a/package.json +++ b/package.json @@ -1,44 +1,28 @@ { - "name": "eslint-plugin-import", - "version": "2.29.1", + "name": "eslint-plugin-import-x", + "version": "0.0.0", "description": "Import with sanity.", + "repository": "git+https://github.com/un-es/eslint-plugin-i", + "author": "JounQin (https://www.1stG.me)", + "license": "MIT", + "packageManager": "yarn@1.22.19", "engines": { "node": ">=4" }, "main": "lib/index.js", - "directories": { - "test": "tests" + "exports": { + ".": "./lib/index.js", + "./config/*": "./config/*", + "./package.json": "./package.json", + "./*": "./lib/*" }, "files": [ "*.md", - "LICENSE", + "config", "docs", "lib", - "config", - "memo-parser/{*.js,LICENSE,*.md}" + "LICENSE" ], - "scripts": { - "prebuild": "rimraf lib", - "build": "babel --quiet --out-dir lib src", - "postbuild": "npm run copy-metafiles", - "copy-metafiles": "node --require babel-register ./scripts/copyMetafiles", - "watch": "npm run tests-only -- -- --watch", - "pretest": "linklocal", - "posttest": "eslint . && npm run update:eslint-docs -- --check && markdownlint \"**/*.md\"", - "mocha": "cross-env BABEL_ENV=test nyc mocha", - "tests-only": "npm run mocha tests/src", - "test": "npm run tests-only", - "test-compiled": "npm run prepublish && BABEL_ENV=testCompiled mocha --compilers js:babel-register tests/src", - "test-all": "node --require babel-register ./scripts/testAll", - "prepublishOnly": "safe-publish-latest && npm run build", - "prepublish": "not-in-publish || npm run prepublishOnly", - "preupdate:eslint-docs": "npm run build", - "update:eslint-docs": "eslint-doc-generator --rule-doc-title-format prefix-name --rule-doc-section-options false --rule-list-split meta.docs.category --ignore-config stage-0 --config-emoji recommended,☑️" - }, - "repository": { - "type": "git", - "url": "https://github.com/import-js/eslint-plugin-import" - }, "keywords": [ "eslint", "eslintplugin", @@ -49,56 +33,16 @@ "import", "export" ], - "author": "Ben Mosher ", - "license": "MIT", - "bugs": { - "url": "https://github.com/import-js/eslint-plugin-import/issues" - }, - "homepage": "https://github.com/import-js/eslint-plugin-import", - "devDependencies": { - "@angular-eslint/template-parser": "^13.5.0", - "@eslint/import-test-order-redirect-scoped": "file:./tests/files/order-redirect-scoped", - "@test-scope/some-module": "file:./tests/files/symlinked-module", - "@typescript-eslint/parser": "^2.23.0 || ^3.3.0 || ^4.29.3 || ^5.10.0", - "babel-cli": "^6.26.0", - "babel-core": "^6.26.3", - "babel-eslint": "=8.0.3 || ^8.2.6", - "babel-plugin-istanbul": "^4.1.6", - "babel-plugin-module-resolver": "^2.7.1", - "babel-preset-airbnb": "^2.6.0", - "babel-preset-flow": "^6.23.0", - "babel-register": "^6.26.0", - "babylon": "^6.18.0", - "chai": "^4.3.10", - "cross-env": "^4.0.0", - "escope": "^3.6.0", - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", - "eslint-doc-generator": "^1.6.1", - "eslint-import-resolver-node": "file:./resolvers/node", - "eslint-import-resolver-typescript": "^1.0.2 || ^1.1.1", - "eslint-import-resolver-webpack": "file:./resolvers/webpack", - "eslint-import-test-order-redirect": "file:./tests/files/order-redirect", - "eslint-module-utils": "file:./utils", - "eslint-plugin-eslint-plugin": "^2.3.0", - "eslint-plugin-import": "2.x", - "eslint-plugin-json": "^2.1.2", - "fs-copy-file-sync": "^1.1.1", - "glob": "^7.2.3", - "in-publish": "^2.0.1", - "jackspeak": "=2.1.1", - "jsonc-parser": "=3.2.0", - "linklocal": "^2.8.2", - "lodash.isarray": "^4.0.0", - "markdownlint-cli": "~0.35", - "mocha": "^3.5.3", - "npm-which": "^3.0.1", - "nyc": "^11.9.0", - "redux": "^3.7.2", - "rimraf": "^2.7.1", - "safe-publish-latest": "^2.0.0", - "sinon": "^2.4.1", - "typescript": "^2.8.1 || ~3.9.5 || ~4.5.2", - "typescript-eslint-parser": "^15 || ^20 || ^22" + "scripts": { + "build": "babel --quiet --out-dir lib src", + "lint": "yarn lint:es && yarn update:eslint-docs --check && yarn lint:md", + "lint:es": "eslint . --cache", + "lint:md": "markdownlint \"**/*.md\"", + "prebuild": "rimraf lib", + "prepublishOnly": "yarn build", + "test": "jest", + "update:eslint-docs": "yarn build && eslint-doc-generator --rule-doc-title-format prefix-name --rule-doc-section-options false --rule-list-split meta.docs.category --ignore-config stage-0 --config-emoji recommended,☑️", + "watch": "yarn test --watch" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" @@ -111,7 +55,6 @@ "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", "hasown": "^2.0.1", "is-core-module": "^2.13.1", "is-glob": "^4.0.3", @@ -121,5 +64,49 @@ "object.values": "^1.1.7", "semver": "^6.3.1", "tsconfig-paths": "^3.15.0" + }, + "devDependencies": { + "@1stg/prettier-config": "^4.0.1", + "@angular-eslint/template-parser": "^13.5.0", + "@babel/cli": "^7.23.9", + "@babel/core": "^7.24.0", + "@babel/eslint-parser": "^7.23.10", + "@babel/plugin-proposal-decorators": "^7.24.0", + "@babel/plugin-proposal-export-default-from": "^7.23.3", + "@babel/preset-env": "^7.24.0", + "@babel/preset-flow": "^7.24.0", + "@babel/preset-react": "^7.23.3", + "@babel/register": "^7.23.7", + "@eslint/import-test-order-redirect-scoped": "link:./test/fixtures/order-redirect-scoped", + "@test-scope/some-module": "link:./test/fixtures/symlinked-module", + "@types/jest": "^29.5.12", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/parser": "^2.23.0 || ^3.3.0 || ^4.29.3 || ^5.10.0", + "chai": "^4.3.10", + "cross-env": "^4.0.0", + "escope": "^3.6.0", + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", + "eslint-config-prettier": "^9.1.0", + "eslint-doc-generator": "^1.6.1", + "eslint-import-resolver-typescript": "^3.6.1", + "eslint-import-resolver-webpack": "^0.13.8", + "eslint-import-test-order-redirect": "link:./test/fixtures/order-redirect", + "eslint-plugin-eslint-plugin": "^2.3.0", + "eslint-plugin-import-x": "link:.", + "eslint-plugin-jest": "^27.9.0", + "eslint-plugin-json": "^2.1.2", + "eslint-plugin-prettier": "^5.1.3", + "glob": "^7.2.3", + "jest": "^29.7.0", + "jsonc-parser": "=3.2.0", + "lodash.isarray": "^4.0.0", + "markdownlint-cli": "~0.35", + "npm-which": "^3.0.1", + "prettier": "^3.2.5", + "redux": "^3.7.2", + "rimraf": "^2.7.1", + "sinon": "^2.4.1", + "svelte": "^4.2.12", + "typescript": "^2.8.1 || ~3.9.5 || ~4.5.2" } } diff --git a/resolvers/README.md b/resolvers/README.md deleted file mode 100644 index e55ebb610..000000000 --- a/resolvers/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# Resolver spec (v2) - -Resolvers must export two names: - -## `interfaceVersion => Number` - -This document currently describes version 2 of the resolver interface. As such, a resolver implementing this version should - -```js -export const interfaceVersion = 2 -``` - -or - -```js -exports.interfaceVersion = 2 -``` - -To the extent it is feasible, trailing versions of the resolvers will continue to be supported, at least until a major version bump on the plugin proper. - -Currently, version 1 is assumed if no `interfaceVersion` is available. (didn't think to define it until v2, heh. 😅) - -## `resolve(source, file, config) => { found: Boolean, path: String? }` - -Given: - -```js -// /some/path/to/module.js -import ... from './imported-file' -``` - -and - -```yaml -# .eslintrc.yml ---- -settings: - import/resolver: - my-cool-resolver: [some, stuff] - node: { paths: [a, b, c] } -``` - -### Arguments - -The arguments provided will be: - -#### `source` - -the module identifier (`./imported-file`). - -#### `file` - -the absolute path to the file making the import (`/some/path/to/module.js`) - -#### `config` - -an object provided via the `import/resolver` setting. `my-cool-resolver` will get `["some", "stuff"]` as its `config`, while - `node` will get `{ "paths": ["a", "b", "c"] }` provided as `config`. - -### Return value - -The first resolver to return `{found: true}` is considered the source of truth. The returned object has: - - - `found`: `true` if the `source` module can be resolved relative to `file`, else `false` - - `path`: an absolute path `String` if the module can be located on the filesystem; else, `null`. - -An example of a `null` path is a Node core module, such as `fs` or `crypto`. These modules can always be resolved, but the path need not be provided as the plugin will not attempt to parse core modules at this time. - -If the resolver cannot resolve `source` relative to `file`, it should just return `{ found: false }`. No `path` key is needed in this case. - -## Example - -Here is most of the [Node resolver] at the time of this writing. It is just a wrapper around substack/Browserify's synchronous [`resolve`]: - -```js -var resolve = require('resolve/sync'); -var isCoreModule = require('is-core-module'); - -exports.resolve = function (source, file, config) { - if (isCoreModule(source)) return { found: true, path: null }; - try { - return { found: true, path: resolve(source, opts(file, config)) }; - } catch (err) { - return { found: false }; - } -}; -``` - -[Node resolver]: ./node/index.js -[`resolve`]: https://www.npmjs.com/package/resolve diff --git a/resolvers/node/.nycrc b/resolvers/node/.nycrc deleted file mode 100644 index 108436087..000000000 --- a/resolvers/node/.nycrc +++ /dev/null @@ -1,15 +0,0 @@ -{ - "all": true, - "check-coverage": false, - "reporter": ["text-summary", "lcov", "text", "html", "json"], - "instrument": false, - "exclude": [ - "coverage", - "test", - "tests", - "resolvers/*/test", - "scripts", - "memo-parser", - "lib" - ] -} diff --git a/resolvers/node/CHANGELOG.md b/resolvers/node/CHANGELOG.md deleted file mode 100644 index 8e11359a9..000000000 --- a/resolvers/node/CHANGELOG.md +++ /dev/null @@ -1,79 +0,0 @@ -# Change Log -All notable changes to this resolver will be documented in this file. -This project adheres to [Semantic Versioning](https://semver.org/). -This change log adheres to standards from [Keep a CHANGELOG](https://keepachangelog.com). - -## Unreleased - -## v0.3.9 - 2023-08-08 - - [fix] restore node 6 compatibility - -## v0.3.8 - 2023-08-05 - - [deps] update `is-core-module`, `resolve` - - [eslint] tighten up rules - -## v0.3.7 - 2023-01-11 -### Changed - - [Refactor] use `is-core-module` directly - -## v0.3.6 - 2021-08-15 -### Fixed - - when "module" does not exist, fall back to "main" ([#2186], thanks [@ljharb]) - -## v0.3.5 - 2021-08-08 -### Added - - use "module" in the same spot as "jsnext:main" ([#2166], thanks [@MustafaHaddara]) - -## v0.3.4 - 2020-06-16 -### Added - - add `.node` extension ([#1663]) - -## v0.3.3 - 2020-01-10 -### Changed - - [meta] copy LICENSE file to all npm packages on prepublish ([#1595], thanks [@opichals]) - -## v0.3.2 - 2018-01-05 -### Added - - `.mjs` extension detected by default to support `experimental-modules` ([#939]) - -### Deps - - update `debug`, `resolve` - -## v0.3.1 - 2017-06-23 -### Changed - - bumped `debug` dep to match other packages - -## v0.3.0 - 2016-12-15 -### Changed - - bumped `resolve` to fix issues with Node builtins (thanks [@SkeLLLa] and [@ljharb]) - -### Fixed - - use `files` in `package.json` to ship only `index.js` ([#531], thanks for noticing [@lukeapage]) - -## v0.2.3 - 2016-08-20 -### Added - - debug logging (use `DEBUG=eslint-plugin-import:resolver:node eslint [...]`) - -## v0.2.2 - 2016-07-14 -### Fixed - - Node resolver no longer declares the import plugin as a `peerDependency`. See [#437] for a well-articulated and thoughtful expression of why this doesn't make sense. Thanks [@jasonkarns] for the issue and the PR to fix it ([#438]). Also, apologies to the others who expressed this before, but I never understood what the problem was.😅 - -## v0.2.1 -### Fixed - - find files with `.json` extensions (#333, thanks for noticing @jfmengels) - -[#2186]: https://github.com/import-js/eslint-plugin-import/issues/2186 -[#2166]: https://github.com/import-js/eslint-plugin-import/pull/2166 -[#1663]: https://github.com/import-js/eslint-plugin-import/issues/1663 -[#1595]: https://github.com/import-js/eslint-plugin-import/pull/1595 -[#939]: https://github.com/import-js/eslint-plugin-import/issues/939 -[#531]: https://github.com/import-js/eslint-plugin-import/issues/531 -[#438]: https://github.com/import-js/eslint-plugin-import/pull/438 -[#437]: https://github.com/import-js/eslint-plugin-import/issues/437 - -[@jasonkarns]: https://github.com/jasonkarns -[@ljharb]: https://github.com/ljharb -[@lukeapage]: https://github.com/lukeapage -[@MustafaHaddara]: https://github.com/MustafaHaddara -[@opichals]: https://github.com/opichals -[@SkeLLLa]: https://github.com/SkeLLLa diff --git a/resolvers/node/README.md b/resolvers/node/README.md deleted file mode 100644 index 20fe1e5af..000000000 --- a/resolvers/node/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# eslint-import-resolver-node - -[![npm](https://img.shields.io/npm/v/eslint-import-resolver-node.svg)](https://www.npmjs.com/package/eslint-import-resolver-node) - -Default Node-style module resolution plugin for [`eslint-plugin-import`](https://www.npmjs.com/package/eslint-plugin-import). - -Published separately to allow pegging to a specific version in case of breaking -changes. - -Config is passed directly through to [`resolve`](https://www.npmjs.com/package/resolve#resolve-sync-id-opts) as options: - -```yaml -settings: - import/resolver: - node: - extensions: - # if unset, default is just '.js', but it must be re-added explicitly if set - - .js - - .jsx - - .es6 - - .coffee - - paths: - # an array of absolute paths which will also be searched - # think NODE_PATH - - /usr/local/share/global_modules - - # this is technically for identifying `node_modules` alternate names - moduleDirectory: - - - node_modules # defaults to 'node_modules', but... - - bower_components - - - project/src # can add a path segment here that will act like - # a source root, for in-project aliasing (i.e. - # `import MyStore from 'stores/my-store'`) -``` - -or to use the default options: - -```yaml -settings: - import/resolver: node -``` diff --git a/resolvers/node/index.js b/resolvers/node/index.js deleted file mode 100644 index 7f207fbf3..000000000 --- a/resolvers/node/index.js +++ /dev/null @@ -1,66 +0,0 @@ -'use strict'; - -const resolve = require('resolve/sync'); -const isCoreModule = require('is-core-module'); -const path = require('path'); - -const log = require('debug')('eslint-plugin-import:resolver:node'); - -exports.interfaceVersion = 2; - -exports.resolve = function (source, file, config) { - log('Resolving:', source, 'from:', file); - let resolvedPath; - - if (isCoreModule(source)) { - log('resolved to core'); - return { found: true, path: null }; - } - - try { - const cachedFilter = function (pkg, dir) { return packageFilter(pkg, dir, config); }; - resolvedPath = resolve(source, opts(file, config, cachedFilter)); - log('Resolved to:', resolvedPath); - return { found: true, path: resolvedPath }; - } catch (err) { - log('resolve threw error:', err); - return { found: false }; - } -}; - -function opts(file, config, packageFilter) { - return Object.assign({ // more closely matches Node (#333) - // plus 'mjs' for native modules! (#939) - extensions: ['.mjs', '.js', '.json', '.node'], - }, config, { - // path.resolve will handle paths relative to CWD - basedir: path.dirname(path.resolve(file)), - packageFilter, - }); -} - -function identity(x) { return x; } - -function packageFilter(pkg, dir, config) { - let found = false; - const file = path.join(dir, 'dummy.js'); - if (pkg.module) { - try { - resolve(String(pkg.module).replace(/^(?:\.\/)?/, './'), opts(file, config, identity)); - pkg.main = pkg.module; - found = true; - } catch (err) { - log('resolve threw error trying to find pkg.module:', err); - } - } - if (!found && pkg['jsnext:main']) { - try { - resolve(String(pkg['jsnext:main']).replace(/^(?:\.\/)?/, './'), opts(file, config, identity)); - pkg.main = pkg['jsnext:main']; - found = true; - } catch (err) { - log('resolve threw error trying to find pkg[\'jsnext:main\']:', err); - } - } - return pkg; -} diff --git a/resolvers/node/package.json b/resolvers/node/package.json deleted file mode 100644 index bfaab4041..000000000 --- a/resolvers/node/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "eslint-import-resolver-node", - "version": "0.3.9", - "description": "Node default behavior import resolution plugin for eslint-plugin-import.", - "main": "index.js", - "files": [ - "index.js" - ], - "scripts": { - "prepublishOnly": "cp ../../{LICENSE,.npmrc} ./", - "tests-only": "nyc mocha", - "test": "npm run tests-only" - }, - "repository": { - "type": "git", - "url": "https://github.com/import-js/eslint-plugin-import" - }, - "keywords": [ - "eslint", - "eslintplugin", - "esnext", - "modules", - "eslint-plugin-import" - ], - "author": "Ben Mosher (me@benmosher.com)", - "license": "MIT", - "bugs": { - "url": "https://github.com/import-js/eslint-plugin-import/issues" - }, - "homepage": "https://github.com/import-js/eslint-plugin-import", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - }, - "devDependencies": { - "chai": "^3.5.0", - "mocha": "^3.5.3", - "nyc": "^11.9.0" - } -} diff --git a/resolvers/node/test/data.json b/resolvers/node/test/data.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/node/test/dot-node.node b/resolvers/node/test/dot-node.node deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/node/test/native.js b/resolvers/node/test/native.js deleted file mode 100644 index 134f23bbc..000000000 --- a/resolvers/node/test/native.js +++ /dev/null @@ -1 +0,0 @@ -exports.natively = function () { return 'but where do we feature?'; }; diff --git a/resolvers/node/test/native.mjs b/resolvers/node/test/native.mjs deleted file mode 100644 index 754bcd650..000000000 --- a/resolvers/node/test/native.mjs +++ /dev/null @@ -1 +0,0 @@ -export function natively() { return "a shining new era is tiptoeing nearer" } \ No newline at end of file diff --git a/resolvers/node/test/native.node b/resolvers/node/test/native.node deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/node/test/package-mains/jsnext/package.json b/resolvers/node/test/package-mains/jsnext/package.json deleted file mode 100644 index d336fedaf..000000000 --- a/resolvers/node/test/package-mains/jsnext/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "main": "lib/index.js", - "jsnext:main": "src/index.js" -} diff --git a/resolvers/node/test/package-mains/module-and-jsnext/package.json b/resolvers/node/test/package-mains/module-and-jsnext/package.json deleted file mode 100644 index 4b1355aae..000000000 --- a/resolvers/node/test/package-mains/module-and-jsnext/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "main": "lib/index.js", - "module": "src/index.js", - "jsnext:main": "lib/index.js" -} diff --git a/resolvers/node/test/package-mains/module-broken/main.js b/resolvers/node/test/package-mains/module-broken/main.js deleted file mode 100644 index f053ebf79..000000000 --- a/resolvers/node/test/package-mains/module-broken/main.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {}; diff --git a/resolvers/node/test/package-mains/module-broken/package.json b/resolvers/node/test/package-mains/module-broken/package.json deleted file mode 100644 index 36a318386..000000000 --- a/resolvers/node/test/package-mains/module-broken/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "main": "./main.js", - "module": "./doesNotExist.js" -} diff --git a/resolvers/node/test/package-mains/module/package.json b/resolvers/node/test/package-mains/module/package.json deleted file mode 100644 index 3a80819f8..000000000 --- a/resolvers/node/test/package-mains/module/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "main": "lib/index.js", - "module": "src/index.js" -} diff --git a/resolvers/node/test/package-mains/package.json b/resolvers/node/test/package-mains/package.json deleted file mode 100644 index 0a5fb6e23..000000000 --- a/resolvers/node/test/package-mains/package.json +++ /dev/null @@ -1 +0,0 @@ -{ "dummy": true } \ No newline at end of file diff --git a/resolvers/node/test/packageMains.js b/resolvers/node/test/packageMains.js deleted file mode 100644 index 170b10e1a..000000000 --- a/resolvers/node/test/packageMains.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -const chai = require('chai'); -const expect = chai.expect; -const path = require('path'); - -const resolver = require('../'); - -const file = path.join(__dirname, 'package-mains', 'dummy.js'); - -describe('packageMains', function () { - it('captures module', function () { - expect(resolver.resolve('./module', file)).property('path') - .to.equal(path.join(__dirname, 'package-mains', 'module', 'src', 'index.js')); - }); - - it('captures jsnext', function () { - expect(resolver.resolve('./jsnext', file)).property('path') - .to.equal(path.join(__dirname, 'package-mains', 'jsnext', 'src', 'index.js')); - }); - - it('captures module instead of jsnext', function () { - expect(resolver.resolve('./module-and-jsnext', file)).property('path') - .to.equal(path.join(__dirname, 'package-mains', 'module-and-jsnext', 'src', 'index.js')); - }); - - it('falls back from a missing "module" to "main"', function () { - expect(resolver.resolve('./module-broken', file)).property('path') - .to.equal(path.join(__dirname, 'package-mains', 'module-broken', 'main.js')); - }); -}); diff --git a/resolvers/node/test/paths.js b/resolvers/node/test/paths.js deleted file mode 100644 index e6ffdafcd..000000000 --- a/resolvers/node/test/paths.js +++ /dev/null @@ -1,58 +0,0 @@ -const expect = require('chai').expect; - -const path = require('path'); -const node = require('../index.js'); - -describe('paths', function () { - it('handles base path relative to CWD', function () { - expect(node.resolve('../', './test/file.js')) - .to.have.property('path') - .equal(path.resolve(__dirname, '../index.js')); - }); -}); - -describe('core', function () { - it('returns found, but null path, for core Node modules', function () { - const resolved = node.resolve('fs', './test/file.js'); - expect(resolved).has.property('found', true); - expect(resolved).has.property('path', null); - }); -}); - -describe('default options', function () { - - it('finds .json files', function () { - expect(node.resolve('./data', './test/file.js')) - .to.have.property('path') - .equal(path.resolve(__dirname, './data.json')); - }); - - it("ignores .json files if 'extensions' is redefined", function () { - expect(node.resolve('./data', './test/file.js', { extensions: ['.js'] })) - .to.have.property('found', false); - }); - - it('finds mjs modules, with precedence over .js', function () { - expect(node.resolve('./native', './test/file.js')) - .to.have.property('path') - .equal(path.resolve(__dirname, './native.mjs')); - }); - - it('finds .node modules, with lowest precedence', function () { - expect(node.resolve('./native.node', './test/file.js')) - .to.have.property('path') - .equal(path.resolve(__dirname, './native.node')); - }); - - it('finds .node modules', function () { - expect(node.resolve('./dot-node', './test/file.js')) - .to.have.property('path') - .equal(path.resolve(__dirname, './dot-node.node')); - }); - - it('still finds .js if explicit', function () { - expect(node.resolve('./native.js', './test/file.js')) - .to.have.property('path') - .equal(path.resolve(__dirname, './native.js')); - }); -}); diff --git a/resolvers/webpack/.babelrc b/resolvers/webpack/.babelrc deleted file mode 120000 index bdc08253c..000000000 --- a/resolvers/webpack/.babelrc +++ /dev/null @@ -1 +0,0 @@ -../../.babelrc \ No newline at end of file diff --git a/resolvers/webpack/.nycrc b/resolvers/webpack/.nycrc deleted file mode 100644 index 108436087..000000000 --- a/resolvers/webpack/.nycrc +++ /dev/null @@ -1,15 +0,0 @@ -{ - "all": true, - "check-coverage": false, - "reporter": ["text-summary", "lcov", "text", "html", "json"], - "instrument": false, - "exclude": [ - "coverage", - "test", - "tests", - "resolvers/*/test", - "scripts", - "memo-parser", - "lib" - ] -} diff --git a/resolvers/webpack/CHANGELOG.md b/resolvers/webpack/CHANGELOG.md deleted file mode 100644 index 4fed046b4..000000000 --- a/resolvers/webpack/CHANGELOG.md +++ /dev/null @@ -1,246 +0,0 @@ -# Change Log -All notable changes to this resolver will be documented in this file. -This project adheres to [Semantic Versioning](https://semver.org/). -This change log adheres to standards from [Keep a CHANGELOG](https://keepachangelog.com). - -## Unreleased - -## 0.13.8 - 2023-10-22 - - [refactor] use `hasown` instead of `has` - - [deps] update `array.prototype.find`, `is-core-module`, `resolve` - - -## 0.13.7 - 2023-08-19 - - [fix] use the `dirname` of the `configPath` as `basedir` ([#2859]) - -## 0.13.6 - 2023-08-16 - - [refactor] revert back to `lodash/isEqual` - -## 0.13.5 - 2023-08-15 - - [refactor] replace `lodash/isEqual` usage with `deep-equal` - - [refactor] remove `lodash/get` usage - - [refactor] switch to a maintained `array.prototype.find` package - - [deps] update `resolve` - -## 0.13.4 - 2023-08-08 - - [fix] restore node 6 compatibility - -## 0.13.3 - 2023-08-05 - - [deps] update `is-core-module`, `resolve`, `semver` - - [eslint] tighten up rules - - [Tests] consolidate eslint config - - [Docs] HTTP => HTTPS ([#2287], thanks [@Schweinepriester]) - -## 0.13.2 - 2021-10-20 - -### Changed - - [meta] expand `engines.node` to include node 17 ([#2268], thanks [@ljharb]) - -## 0.13.1 - 2021-05-13 - -### Added - - add support for webpack5 'externals function' ([#2023], thanks [@jet2jet]) - -### Changed - - Add warning about async Webpack configs ([#1962], thanks [@ogonkov]) - - Replace `node-libs-browser` with `is-core-module` ([#1967], thanks [@andersk]) - - [meta] add "engines" field to document existing requirements - - [Refactor] use `is-regex` instead of `instanceof RegExp` - - [Refactor] use `Array.isArray` instead of `instanceof Array` - - [deps] update `debug`, `interpret`, `is-core-module`, `lodash`, `resolve` - -## 0.13.0 - 2020-09-27 - -### Breaking - - [Breaking] Allow to resolve config path relative to working directory (#1276) - -## 0.12.2 - 2020-06-16 - -### Fixed - - [fix] provide config fallback ([#1705], thanks [@migueloller]) - -## 0.12.1 - 2020-01-10 - -### Changed - - [meta] copy LICENSE file to all npm packages on prepublish ([#1595], thanks [@opichals]) - -## 0.12.0 - 2019-12-07 - -### Added - - [New] enable passing cwd as an option to `eslint-import-resolver-webpack` ([#1503], thanks [@Aghassi]) - -## 0.11.1 - 2019-04-13 - -### Fixed -- [fix] match coreLibs after resolveSync in webpack-resolver ([#1297], thanks [@echenley]) - -## 0.11.0 - 2018-01-22 - -### Added - - support for `argv` parameter when config is a function. ([#1261], thanks [@keann]) - -### Fixed - - crash when webpack config is an array of functions ([#1219]/[#1220] by [@idudinov]) - -## 0.10.1 - 2018-06-24 -### Fixed - - log a useful error in a module bug arises ([#768]/[#767], thanks [@mattkrick]) - -## 0.10.0 - 2018-05-17 -### Changed - - cache webpack resolve function, for performance ([#788]/[#1091]) - -## 0.9.0 - 2018-03-29 -### Breaking - - Fix with `pnpm` by bumping `resolve` ([#968]) - -## 0.8.4 - 2018-01-05 -### Changed - - allow newer version of node-libs-browser ([#969]) - -## 0.8.3 - 2017-06-23 -### Changed - - `debug` bumped to match others - -## 0.8.2 - 2017-06-22 -### Changed - - `webpack` peer dep updated to >= 1.11 (works fine with webpack 3 AFAICT) - -## 0.8.1 - 2017-01-19 -### Changed - - official support for Webpack 2.2.0 (RC), thanks [@graingert] - -## 0.8.0 - 2016-12-15 -### Changed - - bumped `resolve` to fix issues with Node builtins (thanks [@SkeLLLa] and [@ljharb]) - - allow `enhanced-resolve` to be version `>= 2` (thanks [@Kovensky]) - -## 0.7.1 -### Fixed - - missing `has` dependency ([#681] + [#683], thanks [@benmvp] + [@ljharb]) - -## 0.7.0 -### Added - - Support for explicit Webpack config object in `.eslintrc.*`. ([#572], thanks [@jameslnewell]) - - Added `resolve.modules` to configs for webpack2 support ([#569], thanks [@toshafed]) - -## 0.6.0 - 2016-09-13 -### Added - - support for config-as-function ([#533], thanks [@grahamb]) - -## 0.5.1 - 2016-08-11 -### Fixed - - don't throw and die if no webpack config is found - -## 0.5.0 - 2016-08-11 -### Added - - support for Webpack 2 + `module` package.json key! ([#475], thanks [@taion]) - -### Changed - - don't swallow errors, assume config exists ([#435], thanks [@Kovensky]) - -## 0.4.0 - 2016-07-17 -### Added - - support for `webpack.ResolverPlugin` ([#377], thanks [@Rogeres]) - -### Fixed - - provide string `context` to `externals` functions ([#411] + [#413], thanks [@Satyam]) - -## 0.3.2 - 2016-06-30 -### Added - - shared config ([config.js](./config.js)) with barebones settings needed to use this resolver. ([#283]) - -### Fixed - - strip resource query ([#357], thanks [@daltones]) - - allow `externals` to be defined as a function ([#363], thanks [@kesne]) - -## 0.3.1 - 2016-06-02 -### Added - - debug logging. run with `DEBUG=eslint-plugin-import:*` to see log output. - -## 0.3.0 - 2016-06-01 -### Changed - - use `enhanced-resolve` to support additional plugins instead of re-implementing aliases, etc. - -## 0.2.5 - 2016-05-23 -### Added - - Added support for multiple webpack configs ([#181], thanks [@GreenGremlin]) - -## 0.2.4 - 2016-04-29 -### Changed - - automatically find webpack config with `interpret`-able extensions ([#287], thanks [@taion]) - -## 0.2.3 - 2016-04-28 -### Fixed - - `interpret` dependency was declared in the wrong `package.json`. Thanks [@jonboiser] for sleuthing ([#286]) and fixing ([#289]). - -## 0.2.2 - 2016-04-27 -### Added - - `interpret` configs (such as `.babel.js`). Thanks to [@gausie] for the initial PR ([#164], ages ago! 😅) and [@jquense] for tests ([#278]). - -[#2287]: https://github.com/import-js/eslint-plugin-import/pull/2287 -[#2023]: https://github.com/import-js/eslint-plugin-import/pull/2023 -[#1967]: https://github.com/import-js/eslint-plugin-import/pull/1967 -[#1962]: https://github.com/import-js/eslint-plugin-import/pull/1962 -[#1705]: https://github.com/import-js/eslint-plugin-import/pull/1705 -[#1595]: https://github.com/import-js/eslint-plugin-import/pull/1595 -[#1503]: https://github.com/import-js/eslint-plugin-import/pull/1503 -[#1297]: https://github.com/import-js/eslint-plugin-import/pull/1297 -[#1261]: https://github.com/import-js/eslint-plugin-import/pull/1261 -[#1220]: https://github.com/import-js/eslint-plugin-import/pull/1220 -[#1091]: https://github.com/import-js/eslint-plugin-import/pull/1091 -[#969]: https://github.com/import-js/eslint-plugin-import/pull/969 -[#968]: https://github.com/import-js/eslint-plugin-import/pull/968 -[#768]: https://github.com/import-js/eslint-plugin-import/pull/768 -[#683]: https://github.com/import-js/eslint-plugin-import/pull/683 -[#572]: https://github.com/import-js/eslint-plugin-import/pull/572 -[#569]: https://github.com/import-js/eslint-plugin-import/pull/569 -[#533]: https://github.com/import-js/eslint-plugin-import/pull/533 -[#413]: https://github.com/import-js/eslint-plugin-import/pull/413 -[#377]: https://github.com/import-js/eslint-plugin-import/pull/377 -[#363]: https://github.com/import-js/eslint-plugin-import/pull/363 -[#289]: https://github.com/import-js/eslint-plugin-import/pull/289 -[#287]: https://github.com/import-js/eslint-plugin-import/pull/287 -[#278]: https://github.com/import-js/eslint-plugin-import/pull/278 -[#181]: https://github.com/import-js/eslint-plugin-import/pull/181 -[#164]: https://github.com/import-js/eslint-plugin-import/pull/164 - -[#2859]: https://github.com/import-js/eslint-plugin-import/issues/2859 -[#2268]: https://github.com/import-js/eslint-plugin-import/issues/2268 -[#1219]: https://github.com/import-js/eslint-plugin-import/issues/1219 -[#788]: https://github.com/import-js/eslint-plugin-import/issues/788 -[#767]: https://github.com/import-js/eslint-plugin-import/issues/767 -[#681]: https://github.com/import-js/eslint-plugin-import/issues/681 -[#435]: https://github.com/import-js/eslint-plugin-import/issues/435 -[#411]: https://github.com/import-js/eslint-plugin-import/issues/411 -[#357]: https://github.com/import-js/eslint-plugin-import/issues/357 -[#286]: https://github.com/import-js/eslint-plugin-import/issues/286 -[#283]: https://github.com/import-js/eslint-plugin-import/issues/283 - -[@Aghassi]: https://github.com/Aghassi -[@andersk]: https://github.com/andersk -[@benmvp]: https://github.com/benmvp -[@daltones]: https://github.com/daltones -[@echenley]: https://github.com/echenley -[@gausie]: https://github.com/gausie -[@grahamb]: https://github.com/grahamb -[@graingert]: https://github.com/graingert -[@GreenGremlin]: https://github.com/GreenGremlin -[@idudinov]: https://github.com/idudinov -[@jameslnewell]: https://github.com/jameslnewell -[@jet2jet]: https://github.com/jet2jet -[@jquense]: https://github.com/jquense -[@keann]: https://github.com/keann -[@kesne]: https://github.com/kesne -[@Kovensky]: https://github.com/Kovensky -[@ljharb]: https://github.com/ljharb -[@mattkrick]: https://github.com/mattkrick -[@migueloller]: https://github.com/migueloller -[@ogonkov]: https://github.com/ogonkov -[@opichals]: https://github.com/opichals -[@Rogeres]: https://github.com/Rogeres -[@Satyam]: https://github.com/Satyam -[@Schweinepriester]: https://github.com/Schweinepriester -[@SkeLLLa]: https://github.com/SkeLLLa -[@taion]: https://github.com/taion -[@toshafed]: https://github.com/toshafed diff --git a/resolvers/webpack/README.md b/resolvers/webpack/README.md deleted file mode 100644 index 06513ba14..000000000 --- a/resolvers/webpack/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# eslint-import-resolver-webpack - -[![npm](https://img.shields.io/npm/v/eslint-import-resolver-webpack.svg)](https://www.npmjs.com/package/eslint-import-resolver-webpack) - -Webpack-literate module resolution plugin for [`eslint-plugin-import`](https://www.npmjs.com/package/eslint-plugin-import). - -> :boom: Only "synchronous" Webpack configs are supported at the moment. -> If your config returns a `Promise`, this will cause problems. -> Consider splitting your asynchronous configuration to a separate config. - -Published separately to allow pegging to a specific version in case of breaking -changes. - -To use with `eslint-plugin-import`, run: - -```sh -npm i eslint-import-resolver-webpack -g -``` - -or if you manage ESLint as a dev dependency: - -```sh -# inside your project's working tree -npm install eslint-import-resolver-webpack --save-dev -``` - -Will look for `webpack.config.js` as a sibling of the first ancestral `package.json`, -or a `config` parameter may be provided with another filename/path either relative to the -`package.json`, or a complete, absolute path. - -If multiple webpack configurations are found the first configuration containing a resolve section will be used. Optionally, the `config-index` (zero-based) setting can be used to select a specific configuration. - -```yaml ---- -settings: - import/resolver: webpack # take all defaults -``` - -or with explicit config file name: - -```yaml ---- -settings: - import/resolver: - webpack: - config: 'webpack.dev.config.js' -``` - -or with explicit config file index: - -```yaml ---- -settings: - import/resolver: - webpack: - config: 'webpack.multiple.config.js' - config-index: 1 # take the config at index 1 -``` - -or with explicit config file path relative to your projects's working directory: - -```yaml ---- -settings: - import/resolver: - webpack: - config: './configs/webpack.dev.config.js' -``` - -or with explicit config object: - -```yaml ---- -settings: - import/resolver: - webpack: - config: - resolve: - extensions: - - .js - - .jsx -``` - -If your config relies on [environment variables](https://webpack.js.org/guides/environment-variables/), they can be specified using the `env` parameter. If your config is a function, it will be invoked with the value assigned to `env`: - -```yaml ---- -settings: - import/resolver: - webpack: - config: 'webpack.config.js' - env: - NODE_ENV: 'local' - production: true -``` - -## Support - -[Get supported eslint-import-resolver-webpack with the Tidelift Subscription](https://tidelift.com/subscription/pkg/npm-eslint-import-resolver-webpack?utm_source=npm-eslint-import-resolver-webpack&utm_medium=referral&utm_campaign=readme) diff --git a/resolvers/webpack/config.js b/resolvers/webpack/config.js deleted file mode 100644 index 894787d1f..000000000 --- a/resolvers/webpack/config.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * The simplest possible config to get it running with defaults. - */ -module.exports = { - settings: { - 'import/resolver': 'webpack', - }, -}; diff --git a/resolvers/webpack/index.js b/resolvers/webpack/index.js deleted file mode 100644 index 3ca2874dd..000000000 --- a/resolvers/webpack/index.js +++ /dev/null @@ -1,451 +0,0 @@ -'use strict'; - -const findRoot = require('find-root'); -const path = require('path'); -const isEqual = require('lodash/isEqual'); -const find = require('array.prototype.find'); -const interpret = require('interpret'); -const fs = require('fs'); -const isCore = require('is-core-module'); -const resolve = require('resolve/sync'); -const semver = require('semver'); -const hasOwn = require('hasown'); -const isRegex = require('is-regex'); - -const log = require('debug')('eslint-plugin-import:resolver:webpack'); - -exports.interfaceVersion = 2; - -/** - * Find the full path to 'source', given 'file' as a full reference path. - * - * resolveImport('./foo', '/Users/ben/bar.js') => '/Users/ben/foo.js' - * @param {string} source - the module to resolve; i.e './some-module' - * @param {string} file - the importing file's full path; i.e. '/usr/local/bin/file.js' - * @param {object} settings - the webpack config file name, as well as cwd - * @example - * options: { - * // Path to the webpack config - * config: 'webpack.config.js', - * // Path to be used to determine where to resolve webpack from - * // (may differ from the cwd in some cases) - * cwd: process.cwd() - * } - * @return {string?} the resolved path to source, undefined if not resolved, or null - * if resolved to a non-FS resource (i.e. script tag at page load) - */ -exports.resolve = function (source, file, settings) { - - // strip loaders - const finalBang = source.lastIndexOf('!'); - if (finalBang >= 0) { - source = source.slice(finalBang + 1); - } - - // strip resource query - const finalQuestionMark = source.lastIndexOf('?'); - if (finalQuestionMark >= 0) { - source = source.slice(0, finalQuestionMark); - } - - let webpackConfig; - - const _configPath = settings && settings.config; - /** - * Attempt to set the current working directory. - * If none is passed, default to the `cwd` where the config is located. - */ - const cwd = settings && settings.cwd; - const configIndex = settings && settings['config-index']; - const env = settings && settings.env; - const argv = settings && typeof settings.argv !== 'undefined' ? settings.argv : {}; - let packageDir; - - let configPath = typeof _configPath === 'string' && _configPath.startsWith('.') - ? path.resolve(_configPath) - : _configPath; - - log('Config path from settings:', configPath); - - // see if we've got a config path, a config object, an array of config objects or a config function - if (!configPath || typeof configPath === 'string') { - - // see if we've got an absolute path - if (!configPath || !path.isAbsolute(configPath)) { - // if not, find ancestral package.json and use its directory as base for the path - packageDir = findRoot(path.resolve(file)); - if (!packageDir) { throw new Error('package not found above ' + file); } - } - - configPath = findConfigPath(configPath, packageDir); - - log('Config path resolved to:', configPath); - if (configPath) { - try { - webpackConfig = require(configPath); - } catch (e) { - console.log('Error resolving webpackConfig', e); - throw e; - } - } else { - log('No config path found relative to', file, '; using {}'); - webpackConfig = {}; - } - - if (webpackConfig && webpackConfig.default) { - log('Using ES6 module "default" key instead of module.exports.'); - webpackConfig = webpackConfig.default; - } - - } else { - webpackConfig = configPath; - configPath = null; - } - - if (typeof webpackConfig === 'function') { - webpackConfig = webpackConfig(env, argv); - } - - if (Array.isArray(webpackConfig)) { - webpackConfig = webpackConfig.map((cfg) => { - if (typeof cfg === 'function') { - return cfg(env, argv); - } - - return cfg; - }); - - if (typeof configIndex !== 'undefined' && webpackConfig.length > configIndex) { - webpackConfig = webpackConfig[configIndex]; - } else { - webpackConfig = find(webpackConfig, function findFirstWithResolve(config) { - return !!config.resolve; - }); - } - } - - if (typeof webpackConfig.then === 'function') { - webpackConfig = {}; - - console.warn('Webpack config returns a `Promise`; that signature is not supported at the moment. Using empty object instead.'); - } - - if (webpackConfig == null) { - webpackConfig = {}; - - console.warn('No webpack configuration with a "resolve" field found. Using empty object instead.'); - } - - log('Using config: ', webpackConfig); - - const resolveSync = getResolveSync(configPath, webpackConfig, cwd); - - // externals - if (findExternal(source, webpackConfig.externals, path.dirname(file), resolveSync)) { - return { found: true, path: null }; - } - - // otherwise, resolve "normally" - - try { - return { found: true, path: resolveSync(path.dirname(file), source) }; - } catch (err) { - if (isCore(source)) { - return { found: true, path: null }; - } - - log('Error during module resolution:', err); - return { found: false }; - } -}; - -const MAX_CACHE = 10; -const _cache = []; -function getResolveSync(configPath, webpackConfig, cwd) { - const cacheKey = { configPath, webpackConfig }; - let cached = find(_cache, function (entry) { return isEqual(entry.key, cacheKey); }); - if (!cached) { - cached = { - key: cacheKey, - value: createResolveSync(configPath, webpackConfig, cwd), - }; - // put in front and pop last item - if (_cache.unshift(cached) > MAX_CACHE) { - _cache.pop(); - } - } - return cached.value; -} - -function createResolveSync(configPath, webpackConfig, cwd) { - let webpackRequire; - let basedir = null; - - if (typeof configPath === 'string') { - // This can be changed via the settings passed in when defining the resolver - basedir = cwd || path.dirname(configPath); - log(`Attempting to load webpack path from ${basedir}`); - } - - try { - // Attempt to resolve webpack from the given `basedir` - const webpackFilename = resolve('webpack', { basedir, preserveSymlinks: false }); - const webpackResolveOpts = { basedir: path.dirname(webpackFilename), preserveSymlinks: false }; - - webpackRequire = function (id) { - return require(resolve(id, webpackResolveOpts)); - }; - } catch (e) { - // Something has gone wrong (or we're in a test). Use our own bundled - // enhanced-resolve. - log('Using bundled enhanced-resolve.'); - webpackRequire = require; - } - - const enhancedResolvePackage = webpackRequire('enhanced-resolve/package.json'); - const enhancedResolveVersion = enhancedResolvePackage.version; - log('enhanced-resolve version:', enhancedResolveVersion); - - const resolveConfig = webpackConfig.resolve || {}; - - if (semver.major(enhancedResolveVersion) >= 2) { - return createWebpack2ResolveSync(webpackRequire, resolveConfig); - } - - return createWebpack1ResolveSync(webpackRequire, resolveConfig, webpackConfig.plugins); -} - -/** - * webpack 2 defaults: - * https://github.com/webpack/webpack/blob/v2.1.0-beta.20/lib/WebpackOptionsDefaulter.js#L72-L87 - * @type {Object} - */ -const webpack2DefaultResolveConfig = { - unsafeCache: true, // Probably a no-op, since how can we cache anything at all here? - modules: ['node_modules'], - extensions: ['.js', '.json'], - aliasFields: ['browser'], - mainFields: ['browser', 'module', 'main'], -}; - -function createWebpack2ResolveSync(webpackRequire, resolveConfig) { - const EnhancedResolve = webpackRequire('enhanced-resolve'); - - return EnhancedResolve.create.sync(Object.assign({}, webpack2DefaultResolveConfig, resolveConfig)); -} - -/** - * webpack 1 defaults: https://webpack.github.io/docs/configuration.html#resolve-packagemains - * @type {Array} - */ -const webpack1DefaultMains = [ - 'webpack', 'browser', 'web', 'browserify', ['jam', 'main'], 'main', -]; - -// adapted from tests & -// https://github.com/webpack/webpack/blob/v1.13.0/lib/WebpackOptionsApply.js#L322 -function createWebpack1ResolveSync(webpackRequire, resolveConfig, plugins) { - const Resolver = webpackRequire('enhanced-resolve/lib/Resolver'); - const SyncNodeJsInputFileSystem = webpackRequire('enhanced-resolve/lib/SyncNodeJsInputFileSystem'); - - const ModuleAliasPlugin = webpackRequire('enhanced-resolve/lib/ModuleAliasPlugin'); - const ModulesInDirectoriesPlugin = webpackRequire('enhanced-resolve/lib/ModulesInDirectoriesPlugin'); - const ModulesInRootPlugin = webpackRequire('enhanced-resolve/lib/ModulesInRootPlugin'); - const ModuleAsFilePlugin = webpackRequire('enhanced-resolve/lib/ModuleAsFilePlugin'); - const ModuleAsDirectoryPlugin = webpackRequire('enhanced-resolve/lib/ModuleAsDirectoryPlugin'); - const DirectoryDescriptionFilePlugin = webpackRequire('enhanced-resolve/lib/DirectoryDescriptionFilePlugin'); - const DirectoryDefaultFilePlugin = webpackRequire('enhanced-resolve/lib/DirectoryDefaultFilePlugin'); - const FileAppendPlugin = webpackRequire('enhanced-resolve/lib/FileAppendPlugin'); - const ResultSymlinkPlugin = webpackRequire('enhanced-resolve/lib/ResultSymlinkPlugin'); - const DirectoryDescriptionFileFieldAliasPlugin = webpackRequire('enhanced-resolve/lib/DirectoryDescriptionFileFieldAliasPlugin'); - - const resolver = new Resolver(new SyncNodeJsInputFileSystem()); - - resolver.apply( - resolveConfig.packageAlias - ? new DirectoryDescriptionFileFieldAliasPlugin('package.json', resolveConfig.packageAlias) - : function () {}, - new ModuleAliasPlugin(resolveConfig.alias || {}), - makeRootPlugin(ModulesInRootPlugin, 'module', resolveConfig.root), - new ModulesInDirectoriesPlugin( - 'module', - resolveConfig.modulesDirectories || resolveConfig.modules || ['web_modules', 'node_modules'] - ), - makeRootPlugin(ModulesInRootPlugin, 'module', resolveConfig.fallback), - new ModuleAsFilePlugin('module'), - new ModuleAsDirectoryPlugin('module'), - new DirectoryDescriptionFilePlugin( - 'package.json', - ['module', 'jsnext:main'].concat(resolveConfig.packageMains || webpack1DefaultMains) - ), - new DirectoryDefaultFilePlugin(['index']), - new FileAppendPlugin(resolveConfig.extensions || ['', '.webpack.js', '.web.js', '.js']), - new ResultSymlinkPlugin() - ); - - const resolvePlugins = []; - - // support webpack.ResolverPlugin - if (plugins) { - plugins.forEach(function (plugin) { - if ( - plugin.constructor - && plugin.constructor.name === 'ResolverPlugin' - && Array.isArray(plugin.plugins) - ) { - resolvePlugins.push.apply(resolvePlugins, plugin.plugins); - } - }); - } - - resolver.apply.apply(resolver, resolvePlugins); - - return function () { - return resolver.resolveSync.apply(resolver, arguments); - }; -} - -/* eslint-disable */ -// from https://github.com/webpack/webpack/blob/v1.13.0/lib/WebpackOptionsApply.js#L365 -function makeRootPlugin(ModulesInRootPlugin, name, root) { - if (typeof root === 'string') { - return new ModulesInRootPlugin(name, root); - } else if (Array.isArray(root)) { - return function() { - root.forEach(function (root) { - this.apply(new ModulesInRootPlugin(name, root)); - }, this); - }; - } - return function () {}; -} -/* eslint-enable */ - -function findExternal(source, externals, context, resolveSync) { - if (!externals) { return false; } - - // string match - if (typeof externals === 'string') { return source === externals; } - - // array: recurse - if (Array.isArray(externals)) { - return externals.some(function (e) { return findExternal(source, e, context, resolveSync); }); - } - - if (isRegex(externals)) { - return externals.test(source); - } - - if (typeof externals === 'function') { - let functionExternalFound = false; - const callback = function (err, value) { - if (err) { - functionExternalFound = false; - } else { - functionExternalFound = findExternal(source, value, context, resolveSync); - } - }; - // - for prior webpack 5, 'externals function' uses 3 arguments - // - for webpack 5, the count of arguments is less than 3 - if (externals.length === 3) { - externals.call(null, context, source, callback); - } else { - const ctx = { - context, - request: source, - contextInfo: { - issuer: '', - issuerLayer: null, - compiler: '', - }, - getResolve: () => (resolveContext, requestToResolve, cb) => { - if (cb) { - try { - cb(null, resolveSync(resolveContext, requestToResolve)); - } catch (e) { - cb(e); - } - } else { - log('getResolve without callback not supported'); - return Promise.reject(new Error('Not supported')); - } - }, - }; - const result = externals.call(null, ctx, callback); - // todo handling Promise object (using synchronous-promise package?) - if (result && typeof result.then === 'function') { - log('Asynchronous functions for externals not supported'); - } - } - return functionExternalFound; - } - - // else, vanilla object - for (const key in externals) { - if (!hasOwn(externals, key)) { continue; } - if (source === key) { return true; } - } - return false; -} - -function findConfigPath(configPath, packageDir) { - const extensions = Object.keys(interpret.extensions).sort(function (a, b) { - return a === '.js' ? -1 : b === '.js' ? 1 : a.length - b.length; - }); - let extension; - - if (configPath) { - // extensions is not reused below, so safe to mutate it here. - extensions.reverse(); - extensions.forEach(function (maybeExtension) { - if (extension) { - return; - } - - if (configPath.substr(-maybeExtension.length) === maybeExtension) { - extension = maybeExtension; - } - }); - - // see if we've got an absolute path - if (!path.isAbsolute(configPath)) { - configPath = path.join(packageDir, configPath); - } - } else { - extensions.forEach(function (maybeExtension) { - if (extension) { - return; - } - - const maybePath = path.resolve( - path.join(packageDir, 'webpack.config' + maybeExtension) - ); - if (fs.existsSync(maybePath)) { - configPath = maybePath; - extension = maybeExtension; - } - }); - } - - registerCompiler(interpret.extensions[extension]); - return configPath; -} - -function registerCompiler(moduleDescriptor) { - if (moduleDescriptor) { - if (typeof moduleDescriptor === 'string') { - require(moduleDescriptor); - } else if (!Array.isArray(moduleDescriptor)) { - moduleDescriptor.register(require(moduleDescriptor.module)); - } else { - for (let i = 0; i < moduleDescriptor.length; i++) { - try { - registerCompiler(moduleDescriptor[i]); - break; - } catch (e) { - log('Failed to register compiler for moduleDescriptor[]:', i, moduleDescriptor); - } - } - } - } -} diff --git a/resolvers/webpack/package.json b/resolvers/webpack/package.json deleted file mode 100644 index 3fa47d936..000000000 --- a/resolvers/webpack/package.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "eslint-import-resolver-webpack", - "version": "0.13.8", - "description": "Resolve paths to dependencies, given a webpack.config.js. Plugin for eslint-plugin-import.", - "main": "index.js", - "scripts": { - "prepublishOnly": "cp ../../{LICENSE,.npmrc} ./", - "tests-only": "nyc mocha -t 5s", - "test": "npm run tests-only" - }, - "files": [ - "index.js", - "config.js" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/import-js/eslint-plugin-import.git" - }, - "keywords": [ - "eslint-plugin-import", - "eslint", - "jsnext", - "modules", - "webpack" - ], - "author": "Ben Mosher (me@benmosher.com)", - "license": "MIT", - "bugs": { - "url": "https://github.com/import-js/eslint-plugin-import/issues" - }, - "homepage": "https://github.com/import-js/eslint-plugin-import/tree/HEAD/resolvers/webpack", - "dependencies": { - "array.prototype.find": "^2.2.2", - "debug": "^3.2.7", - "enhanced-resolve": "^0.9.1", - "find-root": "^1.1.0", - "hasown": "^2.0.0", - "interpret": "^1.4.0", - "is-core-module": "^2.13.1", - "is-regex": "^1.1.4", - "lodash": "^4.17.21", - "resolve": "^2.0.0-next.5", - "semver": "^5.7.2" - }, - "peerDependencies": { - "eslint-plugin-import": ">=1.4.0", - "webpack": ">=1.11.0" - }, - "devDependencies": { - "babel-plugin-istanbul": "^4.1.6", - "babel-register": "^6.26.0", - "chai": "^3.5.0", - "mocha": "^3.5.3", - "nyc": "^11.9.0", - "webpack": "https://gist.github.com/ljharb/9cdb687f3806f8e6cb8a365d0b7840eb" - }, - "engines": { - "node": ">= 6" - } -} diff --git a/resolvers/webpack/test/alias.js b/resolvers/webpack/test/alias.js deleted file mode 100644 index 946365b30..000000000 --- a/resolvers/webpack/test/alias.js +++ /dev/null @@ -1,139 +0,0 @@ -'use strict'; - -const chai = require('chai'); -const expect = chai.expect; -const path = require('path'); - -const webpack = require('../index'); - -const file = path.join(__dirname, 'files', 'dummy.js'); - -describe('resolve.alias', function () { - let resolved; - before(function () { resolved = webpack.resolve('foo', file); }); - - it('is found', function () { expect(resolved).to.have.property('found', true); }); - - it('is correct', function () { - expect(resolved).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'some', 'goofy', 'path', 'foo.js')); - }); -}); - -// todo: reimplement with resolver function / config -// describe.skip('webpack alias spec', function () { -// // from table: https://webpack.github.io/docs/configuration.html#resolve-alias -// function tableLine(alias, xyz, xyzFile) { -// describe(JSON.stringify(alias), function () { -// it('xyz: ' + xyz, function () { -// expect(resolveAlias('xyz', alias)).to.equal(xyz); -// }); -// it('xyz/file: ' + (xyzFile.name || xyzFile), function () { -// if (xyzFile === Error) { -// expect(resolveAlias.bind(null, 'xyz/file', alias)).to.throw(xyzFile); -// } else { -// expect(resolveAlias('xyz/file', alias)).to.equal(xyzFile); -// } -// }); -// }); -// } - -// tableLine( {} -// , 'xyz', 'xyz/file' ); - -// tableLine( { xyz: '/absolute/path/to/file.js' } -// , '/absolute/path/to/file.js', 'xyz/file' ); - -// tableLine( { xyz$: '/absolute/path/to/file.js' } -// , '/absolute/path/to/file.js', Error ); - -// tableLine( { xyz: './dir/file.js' } -// , './dir/file.js', 'xyz/file' ); - -// tableLine( { xyz$: './dir/file.js' } -// , './dir/file.js', Error ); - -// tableLine( { xyz: '/some/dir' } -// , '/some/dir', '/some/dir/file' ); - -// tableLine( { xyz$: '/some/dir' } -// , '/some/dir', 'xyz/file' ); - -// tableLine( { xyz: './dir' } -// , './dir', './dir/file' ); - -// tableLine( { xyz: 'modu' } -// , 'modu', 'modu/file' ); - -// tableLine( { xyz$: 'modu' } -// , 'modu', 'xyz/file' ); - -// tableLine( { xyz: 'modu/some/file.js' } -// , 'modu/some/file.js', Error ); - -// tableLine( { xyz: 'modu/dir' } -// , 'modu/dir', 'modu/dir/file' ); - -// tableLine( { xyz: 'xyz/dir' } -// , 'xyz/dir', 'xyz/dir/file' ); - -// tableLine( { xyz$: 'xyz/dir' } -// , 'xyz/dir', 'xyz/file' ); -// }); - -// describe.skip('nested module names', function () { -// // from table: https://webpack.github.io/docs/configuration.html#resolve-alias -// function nestedName(alias, xyz, xyzFile) { -// describe(JSON.stringify(alias), function () { -// it('top/xyz: ' + xyz, function () { -// expect(resolveAlias('top/xyz', alias)).to.equal(xyz); -// }); -// it('top/xyz/file: ' + (xyzFile.name || xyzFile), function () { -// if (xyzFile === Error) { -// expect(resolveAlias.bind(null, 'top/xyz/file', alias)).to.throw(xyzFile); -// } else { -// expect(resolveAlias('top/xyz/file', alias)).to.equal(xyzFile); -// } -// }); -// }); -// } - -// nestedName( { 'top/xyz': '/absolute/path/to/file.js' } -// , '/absolute/path/to/file.js', 'top/xyz/file' ); - -// nestedName( { 'top/xyz$': '/absolute/path/to/file.js' } -// , '/absolute/path/to/file.js', Error ); - -// nestedName( { 'top/xyz': './dir/file.js' } -// , './dir/file.js', 'top/xyz/file' ); - -// nestedName( { 'top/xyz$': './dir/file.js' } -// , './dir/file.js', Error ); - -// nestedName( { 'top/xyz': '/some/dir' } -// , '/some/dir', '/some/dir/file' ); - -// nestedName( { 'top/xyz$': '/some/dir' } -// , '/some/dir', 'top/xyz/file' ); - -// nestedName( { 'top/xyz': './dir' } -// , './dir', './dir/file' ); - -// nestedName( { 'top/xyz': 'modu' } -// , 'modu', 'modu/file' ); - -// nestedName( { 'top/xyz$': 'modu' } -// , 'modu', 'top/xyz/file' ); - -// nestedName( { 'top/xyz': 'modu/some/file.js' } -// , 'modu/some/file.js', Error ); - -// nestedName( { 'top/xyz': 'modu/dir' } -// , 'modu/dir', 'modu/dir/file' ); - -// nestedName( { 'top/xyz': 'top/xyz/dir' } -// , 'top/xyz/dir', 'top/xyz/dir/file' ); - -// nestedName( { 'top/xyz$': 'top/xyz/dir' } -// , 'top/xyz/dir', 'top/xyz/file' ); -// }); diff --git a/resolvers/webpack/test/config-extensions/package.json b/resolvers/webpack/test/config-extensions/package.json deleted file mode 100644 index 0a5fb6e23..000000000 --- a/resolvers/webpack/test/config-extensions/package.json +++ /dev/null @@ -1 +0,0 @@ -{ "dummy": true } \ No newline at end of file diff --git a/resolvers/webpack/test/config-extensions/src/main-module.js b/resolvers/webpack/test/config-extensions/src/main-module.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/config-extensions/webpack.config.babel.js b/resolvers/webpack/test/config-extensions/webpack.config.babel.js deleted file mode 100644 index c8b3cd578..000000000 --- a/resolvers/webpack/test/config-extensions/webpack.config.babel.js +++ /dev/null @@ -1,23 +0,0 @@ -import path from 'path'; - -export default { - resolve: { - alias: { - foo: path.join(__dirname, 'some', 'goofy', 'path', 'foo.js'), - }, - modules: [ - path.join(__dirname, 'src'), - path.join(__dirname, 'fallback'), - 'node_modules', - 'bower_components', - ], - modulesDirectories: ['node_modules', 'bower_components'], - root: path.join(__dirname, 'src'), - fallback: path.join(__dirname, 'fallback'), - }, - - externals: [ - { jquery: 'jQuery' }, - 'bootstrap', - ], -}; diff --git a/resolvers/webpack/test/config.js b/resolvers/webpack/test/config.js deleted file mode 100644 index 35f46e66c..000000000 --- a/resolvers/webpack/test/config.js +++ /dev/null @@ -1,158 +0,0 @@ -'use strict'; - -const chai = require('chai'); -const expect = chai.expect; -const path = require('path'); - -const resolve = require('../index').resolve; - -const file = path.join(__dirname, 'files', 'src', 'jsx', 'dummy.js'); -const extensionFile = path.join(__dirname, 'config-extensions', 'src', 'dummy.js'); - -const absoluteSettings = { - config: path.join(__dirname, 'files', 'some', 'absolute.path.webpack.config.js'), -}; - -describe('config', function () { - it('finds webpack.config.js in parent directories', function () { - expect(resolve('main-module', file)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'src', 'main-module.js')); - }); - - it('finds absolute webpack.config.js files', function () { - expect(resolve('foo', file, absoluteSettings)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'some', 'absolutely', 'goofy', 'path', 'foo.js')); - }); - - it('finds compile-to-js configs', function () { - const settings = { - config: path.join(__dirname, './files/webpack.config.babel.js'), - }; - - expect(resolve('main-module', file, settings)) - .to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'src', 'main-module.js')); - }); - - it('finds compile-to-js config in parent directories', function () { - expect(resolve('main-module', extensionFile)) - .to.have.property('path') - .and.equal(path.join(__dirname, 'config-extensions', 'src', 'main-module.js')); - }); - - it('finds the first config with a resolve section', function () { - const settings = { - config: path.join(__dirname, './files/webpack.config.multiple.js'), - }; - - expect(resolve('main-module', file, settings)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'src', 'main-module.js')); - }); - - it('finds the config at option config-index', function () { - const settings = { - config: path.join(__dirname, './files/webpack.config.multiple.js'), - 'config-index': 2, - }; - - expect(resolve('foo', file, settings)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'some', 'goofy', 'path', 'foo.js')); - }); - - it("doesn't swallow config load errors (#435)", function () { - const settings = { - config: path.join(__dirname, './files/webpack.config.garbage.js'), - }; - expect(function () { resolve('foo', file, settings); }).to.throw(Error); - }); - - it('finds config object when config is an object', function () { - const settings = { - config: require(path.join(__dirname, 'files', 'some', 'absolute.path.webpack.config.js')), - }; - expect(resolve('foo', file, settings)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'some', 'absolutely', 'goofy', 'path', 'foo.js')); - }); - - it('finds config object when config uses a path relative to working dir', function () { - const settings = { - config: './test/files/some/absolute.path.webpack.config.js', - }; - expect(resolve('foo', file, settings)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'some', 'absolutely', 'goofy', 'path', 'foo.js')); - }); - - it('finds the first config with a resolve section when config is an array of config objects', function () { - const settings = { - config: require(path.join(__dirname, './files/webpack.config.multiple.js')), - }; - - expect(resolve('main-module', file, settings)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'src', 'main-module.js')); - }); - - it('finds the config at option config-index when config is an array of config objects', function () { - const settings = { - config: require(path.join(__dirname, './files/webpack.config.multiple.js')), - 'config-index': 2, - }; - - expect(resolve('foo', file, settings)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'some', 'goofy', 'path', 'foo.js')); - }); - - it('finds the config at option env when config is a function', function () { - const settings = { - config: require(path.join(__dirname, './files/webpack.function.config.js')), - env: { - dummy: true, - }, - }; - - expect(resolve('bar', file, settings)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'some', 'goofy', 'path', 'bar.js')); - }); - - it('finds the config at option env when config is an array of functions', function () { - const settings = { - config: require(path.join(__dirname, './files/webpack.function.config.multiple.js')), - env: { - dummy: true, - }, - }; - - expect(resolve('bar', file, settings)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'some', 'goofy', 'path', 'bar.js')); - }); - - it('passes argv to config when it is a function', function () { - const settings = { - config: require(path.join(__dirname, './files/webpack.function.config.js')), - argv: { - mode: 'test', - }, - }; - - expect(resolve('baz', file, settings)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'some', 'bar', 'bar.js')); - }); - - it('passes a default empty argv object to config when it is a function', function () { - const settings = { - config: require(path.join(__dirname, './files/webpack.function.config.js')), - argv: undefined, - }; - - expect(function () { resolve('baz', file, settings); }).to.not.throw(Error); - }); - - it('prevents async config using', function () { - const settings = { - config: require(path.join(__dirname, './files/webpack.config.async.js')), - }; - const result = resolve('foo', file, settings); - - expect(result).not.to.have.property('path'); - expect(result).to.have.property('found').to.be.false; - }); -}); diff --git a/resolvers/webpack/test/custom-extensions/bar.coffee b/resolvers/webpack/test/custom-extensions/bar.coffee deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/custom-extensions/baz.web.js b/resolvers/webpack/test/custom-extensions/baz.web.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/custom-extensions/foo.js b/resolvers/webpack/test/custom-extensions/foo.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/custom-extensions/package.json b/resolvers/webpack/test/custom-extensions/package.json deleted file mode 100644 index 2528e5207..000000000 --- a/resolvers/webpack/test/custom-extensions/package.json +++ /dev/null @@ -1 +0,0 @@ -{ "dummy": true } diff --git a/resolvers/webpack/test/custom-extensions/webpack.config.js b/resolvers/webpack/test/custom-extensions/webpack.config.js deleted file mode 100644 index b8d39ed7c..000000000 --- a/resolvers/webpack/test/custom-extensions/webpack.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - resolve: { extensions: ['.js', '.coffee'] }, -}; diff --git a/resolvers/webpack/test/example.js b/resolvers/webpack/test/example.js deleted file mode 100644 index cd9ece015..000000000 --- a/resolvers/webpack/test/example.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -const path = require('path'); - -const resolve = require('../index').resolve; - -const file = path.join(__dirname, 'files', 'src', 'dummy.js'); - -const webpackDir = path.join(__dirname, 'different-package-location'); - -console.log(resolve('main-module', file, { config: 'webpack.config.js', cwd: webpackDir })); diff --git a/resolvers/webpack/test/extensions.js b/resolvers/webpack/test/extensions.js deleted file mode 100644 index 096df7728..000000000 --- a/resolvers/webpack/test/extensions.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; - -const chai = require('chai'); -const expect = chai.expect; -const path = require('path'); - -const resolve = require('../index').resolve; - -const file = path.join(__dirname, 'files', 'dummy.js'); -const extensions = path.join(__dirname, 'custom-extensions', 'dummy.js'); - -describe('extensions', function () { - it('respects the defaults', function () { - expect(resolve('./foo', file)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'foo.web.js')); - }); - - describe('resolve.extensions set', function () { - it('works', function () { - expect(resolve('./foo', extensions)).to.have.property('path') - .and.equal(path.join(__dirname, 'custom-extensions', 'foo.js')); - }); - - it('replaces defaults', function () { - expect(resolve('./baz', extensions)).to.have.property('found', false); - }); - - it('finds .coffee', function () { - expect(resolve('./bar', extensions)).to.have.property('path') - .and.equal(path.join(__dirname, 'custom-extensions', 'bar.coffee')); - }); - }); -}); diff --git a/resolvers/webpack/test/externals.js b/resolvers/webpack/test/externals.js deleted file mode 100644 index 27dec2033..000000000 --- a/resolvers/webpack/test/externals.js +++ /dev/null @@ -1,68 +0,0 @@ -'use strict'; - -const chai = require('chai'); -const expect = chai.expect; -const path = require('path'); -const semver = require('semver'); - -const webpack = require('../index'); - -const file = path.join(__dirname, 'files', 'dummy.js'); - -describe('externals', function () { - const settingsWebpack5 = { - config: require(path.join(__dirname, './files/webpack.config.webpack5.js')), - }; - - it('works on just a string', function () { - const resolved = webpack.resolve('bootstrap', file); - expect(resolved).to.have.property('found', true); - expect(resolved).to.have.property('path', null); - }); - - it('works on object-map', function () { - const resolved = webpack.resolve('jquery', file); - expect(resolved).to.have.property('found', true); - expect(resolved).to.have.property('path', null); - }); - - it('works on a function', function () { - const resolved = webpack.resolve('underscore', file); - expect(resolved).to.have.property('found', true); - expect(resolved).to.have.property('path', null); - }); - - it('returns null for core modules', function () { - const resolved = webpack.resolve('fs', file); - expect(resolved).to.have.property('found', true); - expect(resolved).to.have.property('path', null); - }); - - it('works on a function (synchronous) for webpack 5', function () { - const resolved = webpack.resolve('underscore', file, settingsWebpack5); - expect(resolved).to.have.property('found', true); - expect(resolved).to.have.property('path', null); - }); - - it('works on a function (synchronous) which uses getResolve for webpack 5', function () { - const resolved = webpack.resolve('graphql', file, settingsWebpack5); - expect(resolved).to.have.property('found', true); - expect(resolved).to.have.property('path', null); - }); - - (semver.satisfies(process.version, '> 6') ? describe : describe.skip)('async function in webpack 5', function () { - const settingsWebpack5Async = () => ({ - config: require(path.join(__dirname, './files/webpack.config.webpack5.async-externals.js')), - }); - - it('prevents using an asynchronous function for webpack 5', function () { - const resolved = webpack.resolve('underscore', file, settingsWebpack5Async()); - expect(resolved).to.have.property('found', false); - }); - - it('prevents using a function which uses Promise returned by getResolve for webpack 5', function () { - const resolved = webpack.resolve('graphql', file, settingsWebpack5Async()); - expect(resolved).to.have.property('found', false); - }); - }); -}); diff --git a/resolvers/webpack/test/fallback.js b/resolvers/webpack/test/fallback.js deleted file mode 100644 index b164209e1..000000000 --- a/resolvers/webpack/test/fallback.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -const chai = require('chai'); -const expect = chai.expect; -const path = require('path'); - -const resolve = require('../index').resolve; - -const file = path.join(__dirname, 'files', 'src', 'dummy.js'); - -describe('fallback', function () { - it('works', function () { - expect(resolve('fb-module', file)).property('path') - .to.equal(path.join(__dirname, 'files', 'fallback', 'fb-module.js')); - }); - it('really works', function () { - expect(resolve('jsx/some-fb-file', file)).property('path') - .to.equal(path.join(__dirname, 'files', 'fallback', 'jsx', 'some-fb-file.js')); - }); - it('prefer root', function () { - expect(resolve('jsx/some-file', file)).property('path') - .to.equal(path.join(__dirname, 'files', 'src', 'jsx', 'some-file.js')); - }); - it('supports definition as an array', function () { - expect(resolve('fb-module', file, { config: 'webpack.array-root.config.js' })) - .property('path') - .to.equal(path.join(__dirname, 'files', 'fallback', 'fb-module.js')); - }); -}); diff --git a/resolvers/webpack/test/files/bower_components/typeahead.js b/resolvers/webpack/test/files/bower_components/typeahead.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/files/fallback/fb-module.js b/resolvers/webpack/test/files/fallback/fb-module.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/files/fallback/jsx/some-fb-file.js b/resolvers/webpack/test/files/fallback/jsx/some-fb-file.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/files/fallback/jsx/some-file.js b/resolvers/webpack/test/files/fallback/jsx/some-file.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/files/fallback/main-module.js b/resolvers/webpack/test/files/fallback/main-module.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/files/foo.web.js b/resolvers/webpack/test/files/foo.web.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/files/node_modules/some-module/index.js b/resolvers/webpack/test/files/node_modules/some-module/index.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/files/node_modules/webpack-resolver-plugin-test/index.js b/resolvers/webpack/test/files/node_modules/webpack-resolver-plugin-test/index.js deleted file mode 100644 index f23d4af0c..000000000 --- a/resolvers/webpack/test/files/node_modules/webpack-resolver-plugin-test/index.js +++ /dev/null @@ -1,57 +0,0 @@ -var path = require('path') - -/** - * ResolverPlugin - * - * @param plugins - * @param types - * @constructor - */ - -function ResolverPlugin(plugins, types) { - if(!Array.isArray(plugins)) plugins = [plugins] - if(!types) types = ["normal"] - else if(!Array.isArray(types)) types = [types] - - this.plugins = plugins - this.types = types -} - -module.exports.ResolverPlugin = ResolverPlugin - - -/** - * SimpleResolver for testing - * - * @param file - * @param source - * @constructor - */ - -function SimpleResolver(file, source) { - this.file = file - this.source = source -} - -SimpleResolver.prototype.apply = function (resolver) { - - var file = this.file - var source = this.source - - resolver.plugin('directory', function (request, done) { - - var absolutePath = path.resolve(request.path, request.request) - - if (absolutePath === source) { - resolver.doResolve('file', { request: file }, function (error, result) { - return done(undefined, result || undefined) - }) - } - - return done() - - }) - -} - -module.exports.SimpleResolver = SimpleResolver diff --git a/resolvers/webpack/test/files/package.json b/resolvers/webpack/test/files/package.json deleted file mode 100644 index d1d80ba6c..000000000 --- a/resolvers/webpack/test/files/package.json +++ /dev/null @@ -1 +0,0 @@ -{ "just for show": true } diff --git a/resolvers/webpack/test/files/some/absolute.path.webpack.config.js b/resolvers/webpack/test/files/some/absolute.path.webpack.config.js deleted file mode 100644 index a7cf17d6e..000000000 --- a/resolvers/webpack/test/files/some/absolute.path.webpack.config.js +++ /dev/null @@ -1,21 +0,0 @@ -var path = require('path') - -module.exports = { - resolve: { - alias: { - 'foo': path.join(__dirname, 'absolutely', 'goofy', 'path', 'foo.js'), - }, - modules: [ - path.join(__dirname, 'src'), - 'node_modules', - 'bower_components', - ], - modulesDirectories: ['node_modules', 'bower_components'], - root: path.join(__dirname, 'src'), - }, - - externals: [ - { 'jquery': 'jQuery' }, - 'bootstrap', - ], -} diff --git a/resolvers/webpack/test/files/some/absolutely/goofy/path/foo.js b/resolvers/webpack/test/files/some/absolutely/goofy/path/foo.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/files/some/bar/bar.js b/resolvers/webpack/test/files/some/bar/bar.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/files/some/goofy/path/bar.js b/resolvers/webpack/test/files/some/goofy/path/bar.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/files/some/goofy/path/foo.js b/resolvers/webpack/test/files/some/goofy/path/foo.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/files/src/jsx/some-file.js b/resolvers/webpack/test/files/src/jsx/some-file.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/files/src/main-module.js b/resolvers/webpack/test/files/src/main-module.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/files/webpack.array-root.config.js b/resolvers/webpack/test/files/webpack.array-root.config.js deleted file mode 100644 index 931372f52..000000000 --- a/resolvers/webpack/test/files/webpack.array-root.config.js +++ /dev/null @@ -1,11 +0,0 @@ -var path = require('path') -exports.resolve = { - root: [ - path.join(__dirname, 'src'), - path.join(__dirname, 'bower_components'), - ], - fallback: [ - path.join(__dirname, 'fallback'), - path.join(__dirname, 'bower_components'), - ], -} diff --git a/resolvers/webpack/test/files/webpack.config.async.js b/resolvers/webpack/test/files/webpack.config.async.js deleted file mode 100644 index 9b7aaa7f4..000000000 --- a/resolvers/webpack/test/files/webpack.config.async.js +++ /dev/null @@ -1,7 +0,0 @@ -const config = require('./webpack.config.js') - -module.exports = function() { - return new Promise(function(resolve) { - resolve(config) - }) -} diff --git a/resolvers/webpack/test/files/webpack.config.babel.js b/resolvers/webpack/test/files/webpack.config.babel.js deleted file mode 100644 index 41dc6c8e8..000000000 --- a/resolvers/webpack/test/files/webpack.config.babel.js +++ /dev/null @@ -1,23 +0,0 @@ -import path from 'path' - -export default { - resolve: { - alias: { - 'foo': path.join(__dirname, 'some', 'goofy', 'path', 'foo.js'), - }, - modules: [ - path.join(__dirname, 'src'), - path.join(__dirname, 'fallback'), - 'node_modules', - 'bower_components', - ], - modulesDirectories: ['node_modules', 'bower_components'], - root: path.join(__dirname, 'src'), - fallback: path.join(__dirname, 'fallback'), - }, - - externals: [ - { 'jquery': 'jQuery' }, - 'bootstrap', - ], -} diff --git a/resolvers/webpack/test/files/webpack.config.garbage.js b/resolvers/webpack/test/files/webpack.config.garbage.js deleted file mode 100644 index 043b37562..000000000 --- a/resolvers/webpack/test/files/webpack.config.garbage.js +++ /dev/null @@ -1 +0,0 @@ -throw new Error("this config is garbage") \ No newline at end of file diff --git a/resolvers/webpack/test/files/webpack.config.js b/resolvers/webpack/test/files/webpack.config.js deleted file mode 100644 index 38a4c888b..000000000 --- a/resolvers/webpack/test/files/webpack.config.js +++ /dev/null @@ -1,40 +0,0 @@ -var path = require('path') -var pluginsTest = require('webpack-resolver-plugin-test') - -module.exports = { - resolve: { - alias: { - 'foo': path.join(__dirname, 'some', 'goofy', 'path', 'foo.js'), - 'some-alias': path.join(__dirname, 'some'), - }, - modules: [ - path.join(__dirname, 'src'), - path.join(__dirname, 'fallback'), - 'node_modules', - 'bower_components', - ], - modulesDirectories: ['node_modules', 'bower_components'], - root: path.join(__dirname, 'src'), - fallback: path.join(__dirname, 'fallback'), - }, - - externals: [ - { 'jquery': 'jQuery' }, - 'bootstrap', - function (context, request, callback) { - if (request === 'underscore') { - return callback(null, 'underscore') - } - callback() - }, - ], - - plugins: [ - new pluginsTest.ResolverPlugin([ - new pluginsTest.SimpleResolver( - path.join(__dirname, 'some', 'bar', 'bar.js'), - path.join(__dirname, 'some', 'bar') - ), - ]), - ], -} diff --git a/resolvers/webpack/test/files/webpack.config.multiple.js b/resolvers/webpack/test/files/webpack.config.multiple.js deleted file mode 100644 index b79c4c08a..000000000 --- a/resolvers/webpack/test/files/webpack.config.multiple.js +++ /dev/null @@ -1,25 +0,0 @@ -var path = require('path') - -module.exports = [{ - name: 'one', -}, { - name: 'two', - resolve: { - root: path.join(__dirname, 'src'), - fallback: path.join(__dirname, 'fallback'), - }, -}, { - name: 'three', - resolve: { - alias: { - 'foo': path.join(__dirname, 'some', 'goofy', 'path', 'foo.js'), - }, - modules: [ - path.join(__dirname, 'src'), - 'node_modules', - 'bower_components', - ], - modulesDirectories: ['node_modules', 'bower_components'], - root: path.join(__dirname, 'src'), - }, -}] diff --git a/resolvers/webpack/test/files/webpack.config.webpack5.async-externals.js b/resolvers/webpack/test/files/webpack.config.webpack5.async-externals.js deleted file mode 100644 index ba2902b83..000000000 --- a/resolvers/webpack/test/files/webpack.config.webpack5.async-externals.js +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = { - externals: [ - { 'jquery': 'jQuery' }, - 'bootstrap', - async function ({ request },) { - if (request === 'underscore') { - return 'underscore' - } - }, - function ({ request, getResolve }, callback) { - if (request === 'graphql') { - const resolve = getResolve() - // dummy call (some-module should be resolved on __dirname) - resolve(__dirname, 'some-module').then( - function () { callback(null, 'graphql') }, - function (e) { callback(e) } - ) - } - }, - ], -} diff --git a/resolvers/webpack/test/files/webpack.config.webpack5.js b/resolvers/webpack/test/files/webpack.config.webpack5.js deleted file mode 100644 index 88a12567a..000000000 --- a/resolvers/webpack/test/files/webpack.config.webpack5.js +++ /dev/null @@ -1,27 +0,0 @@ -module.exports = { - externals: [ - { 'jquery': 'jQuery' }, - 'bootstrap', - function ({ request }, callback) { - if (request === 'underscore') { - return callback(null, 'underscore') - } - callback() - }, - function ({ request, getResolve }, callback) { - if (request === 'graphql') { - const resolve = getResolve() - // dummy call (some-module should be resolved on __dirname) - resolve(__dirname, 'some-module', function (err, value) { - if (err) { - callback(err) - } else { - callback(null, 'graphql') - } - }) - } else { - callback() - } - }, - ], -} diff --git a/resolvers/webpack/test/files/webpack.function.config.js b/resolvers/webpack/test/files/webpack.function.config.js deleted file mode 100644 index 0dad14e06..000000000 --- a/resolvers/webpack/test/files/webpack.function.config.js +++ /dev/null @@ -1,44 +0,0 @@ -var path = require('path') -var pluginsTest = require('webpack-resolver-plugin-test') - -module.exports = function(env, argv) { - return { - resolve: { - alias: { - 'foo': path.join(__dirname, 'some', 'goofy', 'path', 'foo.js'), - 'bar': env ? path.join(__dirname, 'some', 'goofy', 'path', 'bar.js') : undefined, - 'baz': argv.mode === 'test' ? path.join(__dirname, 'some', 'bar', 'bar.js') : undefined, - 'some-alias': path.join(__dirname, 'some'), - }, - modules: [ - path.join(__dirname, 'src'), - path.join(__dirname, 'fallback'), - 'node_modules', - 'bower_components', - ], - modulesDirectories: ['node_modules', 'bower_components'], - root: path.join(__dirname, 'src'), - fallback: path.join(__dirname, 'fallback'), - }, - - externals: [ - { 'jquery': 'jQuery' }, - 'bootstrap', - function (context, request, callback) { - if (request === 'underscore') { - return callback(null, 'underscore') - } - callback() - }, - ], - - plugins: [ - new pluginsTest.ResolverPlugin([ - new pluginsTest.SimpleResolver( - path.join(__dirname, 'some', 'bar', 'bar.js'), - path.join(__dirname, 'some', 'bar') - ), - ]), - ], - } -} diff --git a/resolvers/webpack/test/files/webpack.function.config.multiple.js b/resolvers/webpack/test/files/webpack.function.config.multiple.js deleted file mode 100644 index 4dbc94bbc..000000000 --- a/resolvers/webpack/test/files/webpack.function.config.multiple.js +++ /dev/null @@ -1,43 +0,0 @@ -var path = require('path') -var pluginsTest = require('webpack-resolver-plugin-test') - -module.exports = [function(env) { - return { - resolve: { - alias: { - 'foo': path.join(__dirname, 'some', 'goofy', 'path', 'foo.js'), - 'bar': env ? path.join(__dirname, 'some', 'goofy', 'path', 'bar.js') : undefined, - 'some-alias': path.join(__dirname, 'some'), - }, - modules: [ - path.join(__dirname, 'src'), - path.join(__dirname, 'fallback'), - 'node_modules', - 'bower_components', - ], - modulesDirectories: ['node_modules', 'bower_components'], - root: path.join(__dirname, 'src'), - fallback: path.join(__dirname, 'fallback'), - }, - - externals: [ - { 'jquery': 'jQuery' }, - 'bootstrap', - function (context, request, callback) { - if (request === 'underscore') { - return callback(null, 'underscore') - } - callback() - }, - ], - - plugins: [ - new pluginsTest.ResolverPlugin([ - new pluginsTest.SimpleResolver( - path.join(__dirname, 'some', 'bar', 'bar.js'), - path.join(__dirname, 'some', 'bar') - ), - ]), - ], - } -}] diff --git a/resolvers/webpack/test/loaders.js b/resolvers/webpack/test/loaders.js deleted file mode 100644 index e250894a5..000000000 --- a/resolvers/webpack/test/loaders.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; - -const chai = require('chai'); -const expect = chai.expect; -const path = require('path'); - -const resolve = require('../index').resolve; - -const file = path.join(__dirname, 'files', 'dummy.js'); - -describe('inline loader syntax', function () { - it('strips bang-loaders', function () { - expect(resolve('css-loader!./src/main-module', file)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'src', 'main-module.js')); - }); - - it('strips loader query string', function () { - expect(resolve('some-loader?param=value!./src/main-module', file)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'src', 'main-module.js')); - }); - - it('strips resource query string', function () { - expect(resolve('./src/main-module?otherParam=otherValue', file)) - .to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'src', 'main-module.js')); - }); - - it('strips everything', function () { - expect(resolve('some-loader?param=value!./src/main-module?otherParam=otherValue', file)) - .to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'src', 'main-module.js')); - }); -}); diff --git a/resolvers/webpack/test/modules.js b/resolvers/webpack/test/modules.js deleted file mode 100644 index 066e52a6f..000000000 --- a/resolvers/webpack/test/modules.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -const chai = require('chai'); -const expect = chai.expect; -const path = require('path'); - -const resolve = require('../index').resolve; - -const file = path.join(__dirname, 'files', 'dummy.js'); - -describe('resolve.moduleDirectories', function () { - - it('finds a node module', function () { - expect(resolve('some-module', file)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'node_modules', 'some-module', 'index.js')); - }); - - it('finds a bower module', function () { - expect(resolve('typeahead.js', file)).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'bower_components', 'typeahead.js')); - }); - -}); diff --git a/resolvers/webpack/test/package-mains/jam/index.js b/resolvers/webpack/test/package-mains/jam/index.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/package-mains/jam/jam.js b/resolvers/webpack/test/package-mains/jam/jam.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/package-mains/jam/package.json b/resolvers/webpack/test/package-mains/jam/package.json deleted file mode 100644 index 00c93fcba..000000000 --- a/resolvers/webpack/test/package-mains/jam/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "jam": { "main": "jam.js" }, - "main": "index.js" -} \ No newline at end of file diff --git a/resolvers/webpack/test/package-mains/jsnext/package.json b/resolvers/webpack/test/package-mains/jsnext/package.json deleted file mode 100644 index d336fedaf..000000000 --- a/resolvers/webpack/test/package-mains/jsnext/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "main": "lib/index.js", - "jsnext:main": "src/index.js" -} diff --git a/resolvers/webpack/test/package-mains/jsnext/src/index.js b/resolvers/webpack/test/package-mains/jsnext/src/index.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/package-mains/module-and-jsnext/package.json b/resolvers/webpack/test/package-mains/module-and-jsnext/package.json deleted file mode 100644 index 4b1355aae..000000000 --- a/resolvers/webpack/test/package-mains/module-and-jsnext/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "main": "lib/index.js", - "module": "src/index.js", - "jsnext:main": "lib/index.js" -} diff --git a/resolvers/webpack/test/package-mains/module-and-jsnext/src/index.js b/resolvers/webpack/test/package-mains/module-and-jsnext/src/index.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/package-mains/module-broken/main.js b/resolvers/webpack/test/package-mains/module-broken/main.js deleted file mode 100644 index f053ebf79..000000000 --- a/resolvers/webpack/test/package-mains/module-broken/main.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {}; diff --git a/resolvers/webpack/test/package-mains/module-broken/package.json b/resolvers/webpack/test/package-mains/module-broken/package.json deleted file mode 100644 index 36a318386..000000000 --- a/resolvers/webpack/test/package-mains/module-broken/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "main": "./main.js", - "module": "./doesNotExist.js" -} diff --git a/resolvers/webpack/test/package-mains/module/package.json b/resolvers/webpack/test/package-mains/module/package.json deleted file mode 100644 index 3a80819f8..000000000 --- a/resolvers/webpack/test/package-mains/module/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "main": "lib/index.js", - "module": "src/index.js" -} diff --git a/resolvers/webpack/test/package-mains/module/src/index.js b/resolvers/webpack/test/package-mains/module/src/index.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/package-mains/package.json b/resolvers/webpack/test/package-mains/package.json deleted file mode 100644 index 0a5fb6e23..000000000 --- a/resolvers/webpack/test/package-mains/package.json +++ /dev/null @@ -1 +0,0 @@ -{ "dummy": true } \ No newline at end of file diff --git a/resolvers/webpack/test/package-mains/webpack.alt.config.js b/resolvers/webpack/test/package-mains/webpack.alt.config.js deleted file mode 100644 index b955d9d37..000000000 --- a/resolvers/webpack/test/package-mains/webpack.alt.config.js +++ /dev/null @@ -1,3 +0,0 @@ -exports.resolve = { - packageMains: ['main'], // override -}; diff --git a/resolvers/webpack/test/package-mains/webpack.config.js b/resolvers/webpack/test/package-mains/webpack.config.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/package-mains/webpack/index.js b/resolvers/webpack/test/package-mains/webpack/index.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/package-mains/webpack/package.json b/resolvers/webpack/test/package-mains/webpack/package.json deleted file mode 100644 index 0fe78c521..000000000 --- a/resolvers/webpack/test/package-mains/webpack/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "webpack": "webpack.js", - "main": "index.js" -} \ No newline at end of file diff --git a/resolvers/webpack/test/package-mains/webpack/webpack.js b/resolvers/webpack/test/package-mains/webpack/webpack.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/resolvers/webpack/test/packageMains.js b/resolvers/webpack/test/packageMains.js deleted file mode 100644 index d3ddad9da..000000000 --- a/resolvers/webpack/test/packageMains.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict'; - -const chai = require('chai'); -const expect = chai.expect; -const path = require('path'); - -const resolver = require('../'); - -const file = path.join(__dirname, 'package-mains', 'dummy.js'); - -describe('packageMains', function () { - - it('captures module', function () { - expect(resolver.resolve('./module', file)).property('path') - .to.equal(path.join(__dirname, 'package-mains', 'module', 'src', 'index.js')); - }); - - it('captures jsnext', function () { - expect(resolver.resolve('./jsnext', file)).property('path') - .to.equal(path.join(__dirname, 'package-mains', 'jsnext', 'src', 'index.js')); - }); - - it('captures module instead of jsnext', function () { - expect(resolver.resolve('./module-and-jsnext', file)).property('path') - .to.equal(path.join(__dirname, 'package-mains', 'module-and-jsnext', 'src', 'index.js')); - }); - - it('falls back from a missing "module" to "main"', function () { - expect(resolver.resolve('./module-broken', file)).property('path') - .to.equal(path.join(__dirname, 'package-mains', 'module-broken', 'main.js')); - }); - - it('captures webpack', function () { - expect(resolver.resolve('./webpack', file)).property('path') - .to.equal(path.join(__dirname, 'package-mains', 'webpack', 'webpack.js')); - }); - - it('captures jam (array path)', function () { - expect(resolver.resolve('./jam', file)).property('path') - .to.equal(path.join(__dirname, 'package-mains', 'jam', 'jam.js')); - }); - - it('uses configured packageMains, if provided', function () { - expect(resolver.resolve('./webpack', file, { config: 'webpack.alt.config.js' })).property('path') - .to.equal(path.join(__dirname, 'package-mains', 'webpack', 'index.js')); - }); - - it('always defers to module, regardless of config', function () { - expect(resolver.resolve('./module', file, { config: 'webpack.alt.config.js' })).property('path') - .to.equal(path.join(__dirname, 'package-mains', 'module', 'src', 'index.js')); - }); - - it('always defers to jsnext:main, regardless of config', function () { - expect(resolver.resolve('./jsnext', file, { config: 'webpack.alt.config.js' })).property('path') - .to.equal(path.join(__dirname, 'package-mains', 'jsnext', 'src', 'index.js')); - }); -}); diff --git a/resolvers/webpack/test/plugins.js b/resolvers/webpack/test/plugins.js deleted file mode 100644 index b964e7c30..000000000 --- a/resolvers/webpack/test/plugins.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -const chai = require('chai'); -const expect = chai.expect; -const path = require('path'); - -const webpack = require('../index'); - -const file = path.join(__dirname, 'files', 'dummy.js'); - -describe('plugins', function () { - let resolved; let aliasResolved; - - before(function () { - resolved = webpack.resolve('./some/bar', file); - aliasResolved = webpack.resolve('some-alias/bar', file); - }); - - it('work', function () { - expect(resolved).to.have.property('found', true); - }); - - it('is correct', function () { - expect(resolved).to.have.property('path') - .and.equal(path.join(__dirname, 'files', 'some', 'bar', 'bar.js')); - }); - - it('work with alias', function () { - expect(aliasResolved).to.have.property('found', true); - }); -}); diff --git a/resolvers/webpack/test/root.js b/resolvers/webpack/test/root.js deleted file mode 100644 index 194bb8fc8..000000000 --- a/resolvers/webpack/test/root.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict'; - -const chai = require('chai'); -const expect = chai.expect; -const path = require('path'); - -const resolve = require('../index').resolve; - -const file = path.join(__dirname, 'files', 'src', 'dummy.js'); -const webpackDir = path.join(__dirname, 'different-package-location'); - -describe('root', function () { - it('works', function () { - expect(resolve('main-module', file)).property('path') - .to.equal(path.join(__dirname, 'files', 'src', 'main-module.js')); - }); - it('really works', function () { - expect(resolve('jsx/some-file', file)).property('path') - .to.equal(path.join(__dirname, 'files', 'src', 'jsx', 'some-file.js')); - }); - it('supports definition as an array', function () { - expect(resolve('main-module', file, { config: 'webpack.array-root.config.js' })) - .property('path') - .to.equal(path.join(__dirname, 'files', 'src', 'main-module.js')); - expect(resolve('typeahead', file, { config: 'webpack.array-root.config.js' })) - .property('path') - .to.equal(path.join(__dirname, 'files', 'bower_components', 'typeahead.js')); - }); - it('supports definition as a function', function () { - expect(resolve('main-module', file, { config: 'webpack.function.config.js' })) - .property('path') - .to.equal(path.join(__dirname, 'files', 'src', 'main-module.js')); - expect(resolve('typeahead', file, { config: 'webpack.function.config.js' })) - .property('path') - .to.equal(path.join(__dirname, 'files', 'bower_components', 'typeahead.js')); - }); - it('supports passing a different directory to load webpack from', function () { - // Webpack should still be able to resolve the config here - expect(resolve('main-module', file, { config: 'webpack.config.js', cwd: webpackDir })) - .property('path') - .to.equal(path.join(__dirname, 'files', 'src', 'main-module.js')); - expect(resolve('typeahead', file, { config: 'webpack.config.js', cwd: webpackDir })) - .property('path') - .to.equal(path.join(__dirname, 'files', 'bower_components', 'typeahead.js')); - }); -}); diff --git a/scripts/GetCI/GetCI.psm1 b/scripts/GetCI/GetCI.psm1 deleted file mode 100644 index 818ce32fe..000000000 --- a/scripts/GetCI/GetCI.psm1 +++ /dev/null @@ -1,12 +0,0 @@ -function Get-CICommand { - $arguments = [System.Collections.ArrayList]$args - if ($env:CONFIGURATION -eq "WSL") { - $arguments.Insert(0, "wsl"); - } else { - if ($arguments[0] -eq "sudo") { - $arguments.RemoveAt(0) - } - } - $arguments.Insert(0, "echo"); - cmd /c $arguments[0] $arguments[1..$($arguments.Count - 1)]; -} diff --git a/scripts/ci.cmd b/scripts/ci.cmd deleted file mode 100644 index 04ac20265..000000000 --- a/scripts/ci.cmd +++ /dev/null @@ -1,8 +0,0 @@ -@echo off - -FOR /F "tokens=* usebackq" %%F IN (`powershell -Command "& { Import-Module %~dp0GetCI; Get-CICommand %* }"`) DO ( - SET args=%%F -) - -echo ^> cmd /c %args% -cmd /c %args% diff --git a/scripts/copyMetafiles.js b/scripts/copyMetafiles.js deleted file mode 100644 index 01ff4f36f..000000000 --- a/scripts/copyMetafiles.js +++ /dev/null @@ -1,22 +0,0 @@ -import path from 'path'; -import copyFileSync from 'fs-copy-file-sync'; -import resolverDirectories from './resolverDirectories'; - -const files = [ - 'LICENSE', - '.npmrc', -]; - -const directories = [].concat( - 'memo-parser', - resolverDirectories, - 'utils', -); - -for (const directory of directories) { - for (const file of files) { - const destination = path.join(directory, file); - copyFileSync(file, destination); - console.log(`${file} -> ${destination}`); - } -} diff --git a/scripts/resolverDirectories.js b/scripts/resolverDirectories.js deleted file mode 100644 index f0c03a3cc..000000000 --- a/scripts/resolverDirectories.js +++ /dev/null @@ -1,3 +0,0 @@ -import glob from 'glob'; - -export default glob.sync('./resolvers/*/'); diff --git a/scripts/testAll.js b/scripts/testAll.js deleted file mode 100644 index 0e4a12c68..000000000 --- a/scripts/testAll.js +++ /dev/null @@ -1,20 +0,0 @@ -import { spawnSync } from 'child_process'; -import npmWhich from 'npm-which'; -import resolverDirectories from './resolverDirectories'; - -const npmPath = npmWhich(__dirname).sync('npm'); -const spawnOptions = { - stdio: 'inherit', -}; - -spawnSync( - npmPath, - ['test'], - { cwd: __dirname, ...spawnOptions }); - -for (const resolverDir of resolverDirectories) { - spawnSync( - npmPath, - ['test'], - { cwd: resolverDir, ...spawnOptions }); -} diff --git a/src/ExportMap.js b/src/ExportMap.js index f61d3c170..db9fe593d 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -1,65 +1,69 @@ -import fs from 'fs'; -import { dirname } from 'path'; +import fs from 'fs' +import { dirname } from 'path' -import doctrine from 'doctrine'; +import doctrine from 'doctrine' -import debug from 'debug'; +import debug from 'debug' -import { SourceCode } from 'eslint'; +import { SourceCode } from 'eslint' -import parse from 'eslint-module-utils/parse'; -import visit from 'eslint-module-utils/visit'; -import resolve from 'eslint-module-utils/resolve'; -import isIgnored, { hasValidExtension } from 'eslint-module-utils/ignore'; +import parse from './utils/parse' +import visit from './utils/visit' +import resolve from './utils/resolve' +import isIgnored, { hasValidExtension } from './utils/ignore' -import { hashObject } from 'eslint-module-utils/hash'; -import * as unambiguous from 'eslint-module-utils/unambiguous'; +import { hashObject } from './utils/hash' +import * as unambiguous from './utils/unambiguous' -import { tsConfigLoader } from 'tsconfig-paths/lib/tsconfig-loader'; +import { tsConfigLoader } from 'tsconfig-paths/lib/tsconfig-loader' -import includes from 'array-includes'; +import includes from 'array-includes' -let ts; +let ts -const log = debug('eslint-plugin-import:ExportMap'); +const log = debug('eslint-plugin-import-x:ExportMap') -const exportCache = new Map(); -const tsconfigCache = new Map(); +const exportCache = new Map() +const tsconfigCache = new Map() export default class ExportMap { constructor(path) { - this.path = path; - this.namespace = new Map(); + this.path = path + this.namespace = new Map() // todo: restructure to key on path, value is resolver + map of names - this.reexports = new Map(); + this.reexports = new Map() /** * star-exports * @type {Set} of () => ExportMap */ - this.dependencies = new Set(); + this.dependencies = new Set() /** * dependencies of this module that are not explicitly re-exported * @type {Map} from path = () => ExportMap */ - this.imports = new Map(); - this.errors = []; + this.imports = new Map() + this.errors = [] /** * type {'ambiguous' | 'Module' | 'Script'} */ - this.parseGoal = 'ambiguous'; + this.parseGoal = 'ambiguous' } - get hasDefault() { return this.get('default') != null; } // stronger than this.has + get hasDefault() { + return this.get('default') != null + } // stronger than this.has get size() { - let size = this.namespace.size + this.reexports.size; - this.dependencies.forEach((dep) => { - const d = dep(); + let size = this.namespace.size + this.reexports.size + this.dependencies.forEach(dep => { + const d = dep() // CJS / ignored dependencies won't exist (#717) - if (d == null) { return; } - size += d.size; - }); - return size; + if (d == null) { + return + } + size += d.size + }) + return size } /** @@ -70,22 +74,30 @@ export default class ExportMap { * @return {Boolean} true if `name` is exported by this module. */ has(name) { - if (this.namespace.has(name)) { return true; } - if (this.reexports.has(name)) { return true; } + if (this.namespace.has(name)) { + return true + } + if (this.reexports.has(name)) { + return true + } // default exports must be explicitly re-exported (#328) if (name !== 'default') { for (const dep of this.dependencies) { - const innerMap = dep(); + const innerMap = dep() // todo: report as unresolved? - if (!innerMap) { continue; } + if (!innerMap) { + continue + } - if (innerMap.has(name)) { return true; } + if (innerMap.has(name)) { + return true + } } } - return false; + return false } /** @@ -94,114 +106,145 @@ export default class ExportMap { * @return {{ found: boolean, path: ExportMap[] }} */ hasDeep(name) { - if (this.namespace.has(name)) { return { found: true, path: [this] }; } + if (this.namespace.has(name)) { + return { found: true, path: [this] } + } if (this.reexports.has(name)) { - const reexports = this.reexports.get(name); - const imported = reexports.getImport(); + const reexports = this.reexports.get(name) + const imported = reexports.getImport() // if import is ignored, return explicit 'null' - if (imported == null) { return { found: true, path: [this] }; } + if (imported == null) { + return { found: true, path: [this] } + } // safeguard against cycles, only if name matches if (imported.path === this.path && reexports.local === name) { - return { found: false, path: [this] }; + return { found: false, path: [this] } } - const deep = imported.hasDeep(reexports.local); - deep.path.unshift(this); + const deep = imported.hasDeep(reexports.local) + deep.path.unshift(this) - return deep; + return deep } // default exports must be explicitly re-exported (#328) if (name !== 'default') { for (const dep of this.dependencies) { - const innerMap = dep(); - if (innerMap == null) { return { found: true, path: [this] }; } + const innerMap = dep() + if (innerMap == null) { + return { found: true, path: [this] } + } // todo: report as unresolved? - if (!innerMap) { continue; } + if (!innerMap) { + continue + } // safeguard against cycles - if (innerMap.path === this.path) { continue; } + if (innerMap.path === this.path) { + continue + } - const innerValue = innerMap.hasDeep(name); + const innerValue = innerMap.hasDeep(name) if (innerValue.found) { - innerValue.path.unshift(this); - return innerValue; + innerValue.path.unshift(this) + return innerValue } } } - return { found: false, path: [this] }; + return { found: false, path: [this] } } get(name) { - if (this.namespace.has(name)) { return this.namespace.get(name); } + if (this.namespace.has(name)) { + return this.namespace.get(name) + } if (this.reexports.has(name)) { - const reexports = this.reexports.get(name); - const imported = reexports.getImport(); + const reexports = this.reexports.get(name) + const imported = reexports.getImport() // if import is ignored, return explicit 'null' - if (imported == null) { return null; } + if (imported == null) { + return null + } // safeguard against cycles, only if name matches - if (imported.path === this.path && reexports.local === name) { return undefined; } + if (imported.path === this.path && reexports.local === name) { + return undefined + } - return imported.get(reexports.local); + return imported.get(reexports.local) } // default exports must be explicitly re-exported (#328) if (name !== 'default') { for (const dep of this.dependencies) { - const innerMap = dep(); + const innerMap = dep() // todo: report as unresolved? - if (!innerMap) { continue; } + if (!innerMap) { + continue + } // safeguard against cycles - if (innerMap.path === this.path) { continue; } + if (innerMap.path === this.path) { + continue + } - const innerValue = innerMap.get(name); - if (innerValue !== undefined) { return innerValue; } + const innerValue = innerMap.get(name) + if (innerValue !== undefined) { + return innerValue + } } } - return undefined; + return undefined } forEach(callback, thisArg) { - this.namespace.forEach((v, n) => { callback.call(thisArg, v, n, this); }); + this.namespace.forEach((v, n) => { + callback.call(thisArg, v, n, this) + }) this.reexports.forEach((reexports, name) => { - const reexported = reexports.getImport(); + const reexported = reexports.getImport() // can't look up meta for ignored re-exports (#348) - callback.call(thisArg, reexported && reexported.get(reexports.local), name, this); - }); - - this.dependencies.forEach((dep) => { - const d = dep(); + callback.call( + thisArg, + reexported && reexported.get(reexports.local), + name, + this, + ) + }) + + this.dependencies.forEach(dep => { + const d = dep() // CJS / ignored dependencies won't exist (#717) - if (d == null) { return; } + if (d == null) { + return + } d.forEach((v, n) => { if (n !== 'default') { - callback.call(thisArg, v, n, this); + callback.call(thisArg, v, n, this) } - }); - }); + }) + }) } // todo: keys, values, entries? reportErrors(context, declaration) { const msg = this.errors - .map((e) => `${e.message} (${e.lineNumber}:${e.column})`) - .join(', '); + .map(e => `${e.message} (${e.lineNumber}:${e.column})`) + .join(', ') context.report({ node: declaration.source, message: `Parse errors in imported module '${declaration.source.value}': ${msg}`, - }); + }) } } @@ -209,43 +252,44 @@ export default class ExportMap { * parse docs from the first node that has leading comments */ function captureDoc(source, docStyleParsers, ...nodes) { - const metadata = {}; + const metadata = {} // 'some' short-circuits on first 'true' - nodes.some((n) => { + nodes.some(n => { try { - - let leadingComments; + let leadingComments // n.leadingComments is legacy `attachComments` behavior if ('leadingComments' in n) { - leadingComments = n.leadingComments; + leadingComments = n.leadingComments } else if (n.range) { - leadingComments = source.getCommentsBefore(n); + leadingComments = source.getCommentsBefore(n) } - if (!leadingComments || leadingComments.length === 0) { return false; } + if (!leadingComments || leadingComments.length === 0) { + return false + } for (const name in docStyleParsers) { - const doc = docStyleParsers[name](leadingComments); + const doc = docStyleParsers[name](leadingComments) if (doc) { - metadata.doc = doc; + metadata.doc = doc } } - return true; + return true } catch (err) { - return false; + return false } - }); + }) - return metadata; + return metadata } const availableDocStyleParsers = { jsdoc: captureJsDoc, tomdoc: captureTomDoc, -}; +} /** * parse JSDoc from leading comments @@ -253,377 +297,449 @@ const availableDocStyleParsers = { * @return {{ doc: object }} */ function captureJsDoc(comments) { - let doc; + let doc // capture XSDoc - comments.forEach((comment) => { + comments.forEach(comment => { // skip non-block comments - if (comment.type !== 'Block') { return; } + if (comment.type !== 'Block') { + return + } try { - doc = doctrine.parse(comment.value, { unwrap: true }); + doc = doctrine.parse(comment.value, { unwrap: true }) } catch (err) { /* don't care, for now? maybe add to `errors?` */ } - }); + }) - return doc; + return doc } /** - * parse TomDoc section from comments - */ + * parse TomDoc section from comments + */ function captureTomDoc(comments) { // collect lines up to first paragraph break - const lines = []; + const lines = [] for (let i = 0; i < comments.length; i++) { - const comment = comments[i]; - if (comment.value.match(/^\s*$/)) { break; } - lines.push(comment.value.trim()); + const comment = comments[i] + if (comment.value.match(/^\s*$/)) { + break + } + lines.push(comment.value.trim()) } // return doctrine-like object - const statusMatch = lines.join(' ').match(/^(Public|Internal|Deprecated):\s*(.+)/); + const statusMatch = lines + .join(' ') + .match(/^(Public|Internal|Deprecated):\s*(.+)/) if (statusMatch) { return { description: statusMatch[2], - tags: [{ - title: statusMatch[1].toLowerCase(), - description: statusMatch[2], - }], - }; + tags: [ + { + title: statusMatch[1].toLowerCase(), + description: statusMatch[2], + }, + ], + } } } -const supportedImportTypes = new Set(['ImportDefaultSpecifier', 'ImportNamespaceSpecifier']); +const supportedImportTypes = new Set([ + 'ImportDefaultSpecifier', + 'ImportNamespaceSpecifier', +]) ExportMap.get = function (source, context) { - const path = resolve(source, context); - if (path == null) { return null; } + const path = resolve(source, context) + if (path == null) { + return null + } - return ExportMap.for(childContext(path, context)); -}; + return ExportMap.for(childContext(path, context)) +} ExportMap.for = function (context) { - const { path } = context; + const { path } = context - const cacheKey = context.cacheKey || hashObject(context).digest('hex'); - let exportMap = exportCache.get(cacheKey); + const cacheKey = context.cacheKey || hashObject(context).digest('hex') + let exportMap = exportCache.get(cacheKey) // return cached ignore - if (exportMap === null) { return null; } + if (exportMap === null) { + return null + } - const stats = fs.statSync(path); + const stats = fs.statSync(path) if (exportMap != null) { // date equality check if (exportMap.mtime - stats.mtime === 0) { - return exportMap; + return exportMap } // future: check content equality? } // check valid extensions first if (!hasValidExtension(path, context)) { - exportCache.set(cacheKey, null); - return null; + exportCache.set(cacheKey, null) + return null } // check for and cache ignore if (isIgnored(path, context)) { - log('ignored path due to ignore settings:', path); - exportCache.set(cacheKey, null); - return null; + log('ignored path due to ignore settings:', path) + exportCache.set(cacheKey, null) + return null } - const content = fs.readFileSync(path, { encoding: 'utf8' }); + const content = fs.readFileSync(path, { encoding: 'utf8' }) // check for and cache unambiguous modules if (!unambiguous.test(content)) { - log('ignored path due to unambiguous regex:', path); - exportCache.set(cacheKey, null); - return null; + log('ignored path due to unambiguous regex:', path) + exportCache.set(cacheKey, null) + return null } - log('cache miss', cacheKey, 'for path', path); - exportMap = ExportMap.parse(path, content, context); + log('cache miss', cacheKey, 'for path', path) + exportMap = ExportMap.parse(path, content, context) // ambiguous modules return null if (exportMap == null) { - log('ignored path due to ambiguous parse:', path); - exportCache.set(cacheKey, null); - return null; + log('ignored path due to ambiguous parse:', path) + exportCache.set(cacheKey, null) + return null } - exportMap.mtime = stats.mtime; + exportMap.mtime = stats.mtime - exportCache.set(cacheKey, exportMap); - return exportMap; -}; + exportCache.set(cacheKey, exportMap) + return exportMap +} ExportMap.parse = function (path, content, context) { - const m = new ExportMap(path); - const isEsModuleInteropTrue = isEsModuleInterop(); + const m = new ExportMap(path) + const isEsModuleInteropTrue = isEsModuleInterop() - let ast; - let visitorKeys; + let ast + let visitorKeys try { - const result = parse(path, content, context); - ast = result.ast; - visitorKeys = result.visitorKeys; + const result = parse(path, content, context) + ast = result.ast + visitorKeys = result.visitorKeys } catch (err) { - m.errors.push(err); - return m; // can't continue + m.errors.push(err) + return m // can't continue } - m.visitorKeys = visitorKeys; + m.visitorKeys = visitorKeys - let hasDynamicImports = false; + let hasDynamicImports = false function processDynamicImport(source) { - hasDynamicImports = true; + hasDynamicImports = true if (source.type !== 'Literal') { - return null; + return null } - const p = remotePath(source.value); + const p = remotePath(source.value) if (p == null) { - return null; + return null } - const importedSpecifiers = new Set(); - importedSpecifiers.add('ImportNamespaceSpecifier'); - const getter = thunkFor(p, context); + const importedSpecifiers = new Set() + importedSpecifiers.add('ImportNamespaceSpecifier') + const getter = thunkFor(p, context) m.imports.set(p, { getter, - declarations: new Set([{ - source: { - // capturing actual node reference holds full AST in memory! - value: source.value, - loc: source.loc, + declarations: new Set([ + { + source: { + // capturing actual node reference holds full AST in memory! + value: source.value, + loc: source.loc, + }, + importedSpecifiers, + dynamic: true, }, - importedSpecifiers, - dynamic: true, - }]), - }); + ]), + }) } visit(ast, visitorKeys, { ImportExpression(node) { - processDynamicImport(node.source); + processDynamicImport(node.source) }, CallExpression(node) { if (node.callee.type === 'Import') { - processDynamicImport(node.arguments[0]); + processDynamicImport(node.arguments[0]) } }, - }); + }) - const unambiguouslyESM = unambiguous.isModule(ast); - if (!unambiguouslyESM && !hasDynamicImports) { return null; } + const unambiguouslyESM = unambiguous.isModule(ast) + if (!unambiguouslyESM && !hasDynamicImports) { + return null + } - const docstyle = context.settings && context.settings['import/docstyle'] || ['jsdoc']; - const docStyleParsers = {}; - docstyle.forEach((style) => { - docStyleParsers[style] = availableDocStyleParsers[style]; - }); + const docstyle = (context.settings && + context.settings['import-x/docstyle']) || ['jsdoc'] + const docStyleParsers = {} + docstyle.forEach(style => { + docStyleParsers[style] = availableDocStyleParsers[style] + }) // attempt to collect module doc if (ast.comments) { - ast.comments.some((c) => { - if (c.type !== 'Block') { return false; } + ast.comments.some(c => { + if (c.type !== 'Block') { + return false + } try { - const doc = doctrine.parse(c.value, { unwrap: true }); - if (doc.tags.some((t) => t.title === 'module')) { - m.doc = doc; - return true; + const doc = doctrine.parse(c.value, { unwrap: true }) + if (doc.tags.some(t => t.title === 'module')) { + m.doc = doc + return true } - } catch (err) { /* ignore */ } - return false; - }); + } catch (err) { + /* ignore */ + } + return false + }) } - const namespaces = new Map(); + const namespaces = new Map() function remotePath(value) { - return resolve.relative(value, path, context.settings); + return resolve.relative(value, path, context.settings) } function resolveImport(value) { - const rp = remotePath(value); - if (rp == null) { return null; } - return ExportMap.for(childContext(rp, context)); + const rp = remotePath(value) + if (rp == null) { + return null + } + return ExportMap.for(childContext(rp, context)) } function getNamespace(identifier) { - if (!namespaces.has(identifier.name)) { return; } + if (!namespaces.has(identifier.name)) { + return + } return function () { - return resolveImport(namespaces.get(identifier.name)); - }; + return resolveImport(namespaces.get(identifier.name)) + } } function addNamespace(object, identifier) { - const nsfn = getNamespace(identifier); + const nsfn = getNamespace(identifier) if (nsfn) { - Object.defineProperty(object, 'namespace', { get: nsfn }); + Object.defineProperty(object, 'namespace', { get: nsfn }) } - return object; + return object } function processSpecifier(s, n, m) { - const nsource = n.source && n.source.value; - const exportMeta = {}; - let local; + const nsource = n.source && n.source.value + const exportMeta = {} + let local switch (s.type) { case 'ExportDefaultSpecifier': - if (!nsource) { return; } - local = 'default'; - break; + if (!nsource) { + return + } + local = 'default' + break case 'ExportNamespaceSpecifier': - m.namespace.set(s.exported.name, Object.defineProperty(exportMeta, 'namespace', { - get() { return resolveImport(nsource); }, - })); - return; + m.namespace.set( + s.exported.name, + Object.defineProperty(exportMeta, 'namespace', { + get() { + return resolveImport(nsource) + }, + }), + ) + return case 'ExportAllDeclaration': - m.namespace.set(s.exported.name || s.exported.value, addNamespace(exportMeta, s.source.value)); - return; + m.namespace.set( + s.exported.name || s.exported.value, + addNamespace(exportMeta, s.source.value), + ) + return case 'ExportSpecifier': if (!n.source) { - m.namespace.set(s.exported.name || s.exported.value, addNamespace(exportMeta, s.local)); - return; + m.namespace.set( + s.exported.name || s.exported.value, + addNamespace(exportMeta, s.local), + ) + return } // else falls through default: - local = s.local.name; - break; + local = s.local.name + break } // todo: JSDoc - m.reexports.set(s.exported.name, { local, getImport: () => resolveImport(nsource) }); + m.reexports.set(s.exported.name, { + local, + getImport: () => resolveImport(nsource), + }) } function captureDependencyWithSpecifiers(n) { // import type { Foo } (TS and Flow); import typeof { Foo } (Flow) - const declarationIsType = n.importKind === 'type' || n.importKind === 'typeof'; + const declarationIsType = + n.importKind === 'type' || n.importKind === 'typeof' // import './foo' or import {} from './foo' (both 0 specifiers) is a side effect and // shouldn't be considered to be just importing types - let specifiersOnlyImportingTypes = n.specifiers.length > 0; - const importedSpecifiers = new Set(); - n.specifiers.forEach((specifier) => { + let specifiersOnlyImportingTypes = n.specifiers.length > 0 + const importedSpecifiers = new Set() + n.specifiers.forEach(specifier => { if (specifier.type === 'ImportSpecifier') { - importedSpecifiers.add(specifier.imported.name || specifier.imported.value); + importedSpecifiers.add( + specifier.imported.name || specifier.imported.value, + ) } else if (supportedImportTypes.has(specifier.type)) { - importedSpecifiers.add(specifier.type); + importedSpecifiers.add(specifier.type) } // import { type Foo } (Flow); import { typeof Foo } (Flow) - specifiersOnlyImportingTypes = specifiersOnlyImportingTypes - && (specifier.importKind === 'type' || specifier.importKind === 'typeof'); - }); - captureDependency(n, declarationIsType || specifiersOnlyImportingTypes, importedSpecifiers); + specifiersOnlyImportingTypes = + specifiersOnlyImportingTypes && + (specifier.importKind === 'type' || specifier.importKind === 'typeof') + }) + captureDependency( + n, + declarationIsType || specifiersOnlyImportingTypes, + importedSpecifiers, + ) } - function captureDependency({ source }, isOnlyImportingTypes, importedSpecifiers = new Set()) { - if (source == null) { return null; } + function captureDependency( + { source }, + isOnlyImportingTypes, + importedSpecifiers = new Set(), + ) { + if (source == null) { + return null + } - const p = remotePath(source.value); - if (p == null) { return null; } + const p = remotePath(source.value) + if (p == null) { + return null + } const declarationMetadata = { // capturing actual node reference holds full AST in memory! source: { value: source.value, loc: source.loc }, isOnlyImportingTypes, importedSpecifiers, - }; + } - const existing = m.imports.get(p); + const existing = m.imports.get(p) if (existing != null) { - existing.declarations.add(declarationMetadata); - return existing.getter; + existing.declarations.add(declarationMetadata) + return existing.getter } - const getter = thunkFor(p, context); - m.imports.set(p, { getter, declarations: new Set([declarationMetadata]) }); - return getter; + const getter = thunkFor(p, context) + m.imports.set(p, { getter, declarations: new Set([declarationMetadata]) }) + return getter } - const source = makeSourceCode(content, ast); + const source = makeSourceCode(content, ast) function readTsConfig(context) { const tsconfigInfo = tsConfigLoader({ - cwd: context.parserOptions && context.parserOptions.tsconfigRootDir || process.cwd(), - getEnv: (key) => process.env[key], - }); + cwd: + (context.parserOptions && context.parserOptions.tsconfigRootDir) || + process.cwd(), + getEnv: key => process.env[key], + }) try { if (tsconfigInfo.tsConfigPath !== undefined) { // Projects not using TypeScript won't have `typescript` installed. - if (!ts) { ts = require('typescript'); } // eslint-disable-line import/no-extraneous-dependencies + if (!ts) { + ts = require('typescript') // eslint-disable-line import-x/no-extraneous-dependencies + } - const configFile = ts.readConfigFile(tsconfigInfo.tsConfigPath, ts.sys.readFile); + const configFile = ts.readConfigFile( + tsconfigInfo.tsConfigPath, + ts.sys.readFile, + ) return ts.parseJsonConfigFileContent( configFile.config, ts.sys, dirname(tsconfigInfo.tsConfigPath), - ); + ) } } catch (e) { // Catch any errors } - return null; + return null } function isEsModuleInterop() { const cacheKey = hashObject({ - tsconfigRootDir: context.parserOptions && context.parserOptions.tsconfigRootDir, - }).digest('hex'); - let tsConfig = tsconfigCache.get(cacheKey); + tsconfigRootDir: + context.parserOptions && context.parserOptions.tsconfigRootDir, + }).digest('hex') + let tsConfig = tsconfigCache.get(cacheKey) if (typeof tsConfig === 'undefined') { - tsConfig = readTsConfig(context); - tsconfigCache.set(cacheKey, tsConfig); + tsConfig = readTsConfig(context) + tsconfigCache.set(cacheKey, tsConfig) } - return tsConfig && tsConfig.options ? tsConfig.options.esModuleInterop : false; + return tsConfig && tsConfig.options + ? tsConfig.options.esModuleInterop + : false } ast.body.forEach(function (n) { if (n.type === 'ExportDefaultDeclaration') { - const exportMeta = captureDoc(source, docStyleParsers, n); + const exportMeta = captureDoc(source, docStyleParsers, n) if (n.declaration.type === 'Identifier') { - addNamespace(exportMeta, n.declaration); + addNamespace(exportMeta, n.declaration) } - m.namespace.set('default', exportMeta); - return; + m.namespace.set('default', exportMeta) + return } if (n.type === 'ExportAllDeclaration') { - const getter = captureDependency(n, n.exportKind === 'type'); - if (getter) { m.dependencies.add(getter); } + const getter = captureDependency(n, n.exportKind === 'type') + if (getter) { + m.dependencies.add(getter) + } if (n.exported) { - processSpecifier(n, n.exported, m); + processSpecifier(n, n.exported, m) } - return; + return } // capture namespaces in case of later export if (n.type === 'ImportDeclaration') { - captureDependencyWithSpecifiers(n); + captureDependencyWithSpecifiers(n) - const ns = n.specifiers.find((s) => s.type === 'ImportNamespaceSpecifier'); + const ns = n.specifiers.find(s => s.type === 'ImportNamespaceSpecifier') if (ns) { - namespaces.set(ns.local.name, n.source.value); + namespaces.set(ns.local.name, n.source.value) } - return; + return } if (n.type === 'ExportNamedDeclaration') { - captureDependencyWithSpecifiers(n); + captureDependencyWithSpecifiers(n) // capture declaration if (n.declaration != null) { switch (n.declaration.type) { case 'FunctionDeclaration': case 'ClassDeclaration': - case 'TypeAlias': // flowtype with babel-eslint parser + case 'TypeAlias': // flowtype with @babel/eslint-parser case 'InterfaceDeclaration': case 'DeclareFunction': case 'TSDeclareFunction': @@ -632,33 +748,41 @@ ExportMap.parse = function (path, content, context) { case 'TSInterfaceDeclaration': case 'TSAbstractClassDeclaration': case 'TSModuleDeclaration': - m.namespace.set(n.declaration.id.name, captureDoc(source, docStyleParsers, n)); - break; + m.namespace.set( + n.declaration.id.name, + captureDoc(source, docStyleParsers, n), + ) + break case 'VariableDeclaration': - n.declaration.declarations.forEach((d) => { - recursivePatternCapture( - d.id, - (id) => m.namespace.set(id.name, captureDoc(source, docStyleParsers, d, n)), - ); - }); - break; + n.declaration.declarations.forEach(d => { + recursivePatternCapture(d.id, id => + m.namespace.set( + id.name, + captureDoc(source, docStyleParsers, d, n), + ), + ) + }) + break default: } } - n.specifiers.forEach((s) => processSpecifier(s, n, m)); + n.specifiers.forEach(s => processSpecifier(s, n, m)) } - const exports = ['TSExportAssignment']; + const exports = ['TSExportAssignment'] if (isEsModuleInteropTrue) { - exports.push('TSNamespaceExportDeclaration'); + exports.push('TSNamespaceExportDeclaration') } // This doesn't declare anything, but changes what's being exported. if (includes(exports, n.type)) { - const exportedName = n.type === 'TSNamespaceExportDeclaration' - ? (n.id || n.name).name - : n.expression && n.expression.name || n.expression.id && n.expression.id.name || null; + const exportedName = + n.type === 'TSNamespaceExportDeclaration' + ? (n.id || n.name).name + : (n.expression && n.expression.name) || + (n.expression.id && n.expression.id.name) || + null const declTypes = [ 'VariableDeclaration', 'ClassDeclaration', @@ -668,69 +792,87 @@ ExportMap.parse = function (path, content, context) { 'TSInterfaceDeclaration', 'TSAbstractClassDeclaration', 'TSModuleDeclaration', - ]; - const exportedDecls = ast.body.filter(({ type, id, declarations }) => includes(declTypes, type) && ( - id && id.name === exportedName || declarations && declarations.find((d) => d.id.name === exportedName) - )); + ] + const exportedDecls = ast.body.filter( + ({ type, id, declarations }) => + includes(declTypes, type) && + ((id && id.name === exportedName) || + (declarations && + declarations.find(d => d.id.name === exportedName))), + ) if (exportedDecls.length === 0) { // Export is not referencing any local declaration, must be re-exporting - m.namespace.set('default', captureDoc(source, docStyleParsers, n)); - return; + m.namespace.set('default', captureDoc(source, docStyleParsers, n)) + return } if ( - isEsModuleInteropTrue // esModuleInterop is on in tsconfig - && !m.namespace.has('default') // and default isn't added already + isEsModuleInteropTrue && // esModuleInterop is on in tsconfig + !m.namespace.has('default') // and default isn't added already ) { - m.namespace.set('default', {}); // add default export + m.namespace.set('default', {}) // add default export } - exportedDecls.forEach((decl) => { + exportedDecls.forEach(decl => { if (decl.type === 'TSModuleDeclaration') { if (decl.body && decl.body.type === 'TSModuleDeclaration') { - m.namespace.set(decl.body.id.name, captureDoc(source, docStyleParsers, decl.body)); + m.namespace.set( + decl.body.id.name, + captureDoc(source, docStyleParsers, decl.body), + ) } else if (decl.body && decl.body.body) { - decl.body.body.forEach((moduleBlockNode) => { + decl.body.body.forEach(moduleBlockNode => { // Export-assignment exports all members in the namespace, // explicitly exported or not. - const namespaceDecl = moduleBlockNode.type === 'ExportNamedDeclaration' - ? moduleBlockNode.declaration - : moduleBlockNode; + const namespaceDecl = + moduleBlockNode.type === 'ExportNamedDeclaration' + ? moduleBlockNode.declaration + : moduleBlockNode if (!namespaceDecl) { // TypeScript can check this for us; we needn't } else if (namespaceDecl.type === 'VariableDeclaration') { - namespaceDecl.declarations.forEach((d) => recursivePatternCapture(d.id, (id) => m.namespace.set( - id.name, - captureDoc(source, docStyleParsers, decl, namespaceDecl, moduleBlockNode), - )), - ); + namespaceDecl.declarations.forEach(d => + recursivePatternCapture(d.id, id => + m.namespace.set( + id.name, + captureDoc( + source, + docStyleParsers, + decl, + namespaceDecl, + moduleBlockNode, + ), + ), + ), + ) } else { m.namespace.set( namespaceDecl.id.name, - captureDoc(source, docStyleParsers, moduleBlockNode)); + captureDoc(source, docStyleParsers, moduleBlockNode), + ) } - }); + }) } } else { // Export as default - m.namespace.set('default', captureDoc(source, docStyleParsers, decl)); + m.namespace.set('default', captureDoc(source, docStyleParsers, decl)) } - }); + }) } - }); + }) if ( - isEsModuleInteropTrue // esModuleInterop is on in tsconfig - && m.namespace.size > 0 // anything is exported - && !m.namespace.has('default') // and default isn't added already + isEsModuleInteropTrue && // esModuleInterop is on in tsconfig + m.namespace.size > 0 && // anything is exported + !m.namespace.has('default') // and default isn't added already ) { - m.namespace.set('default', {}); // add default export + m.namespace.set('default', {}) // add default export } if (unambiguouslyESM) { - m.parseGoal = 'Module'; + m.parseGoal = 'Module' } - return m; -}; + return m +} /** * The creation of this closure is isolated from other scopes @@ -738,7 +880,7 @@ ExportMap.parse = function (path, content, context) { * caused memory leaks. See #1266. */ function thunkFor(p, context) { - return () => ExportMap.for(childContext(p, context)); + return () => ExportMap.for(childContext(p, context)) } /** @@ -751,65 +893,71 @@ function thunkFor(p, context) { export function recursivePatternCapture(pattern, callback) { switch (pattern.type) { case 'Identifier': // base case - callback(pattern); - break; + callback(pattern) + break case 'ObjectPattern': - pattern.properties.forEach((p) => { + pattern.properties.forEach(p => { if (p.type === 'ExperimentalRestProperty' || p.type === 'RestElement') { - callback(p.argument); - return; + callback(p.argument) + return } - recursivePatternCapture(p.value, callback); - }); - break; + recursivePatternCapture(p.value, callback) + }) + break case 'ArrayPattern': - pattern.elements.forEach((element) => { - if (element == null) { return; } - if (element.type === 'ExperimentalRestProperty' || element.type === 'RestElement') { - callback(element.argument); - return; + pattern.elements.forEach(element => { + if (element == null) { + return + } + if ( + element.type === 'ExperimentalRestProperty' || + element.type === 'RestElement' + ) { + callback(element.argument) + return } - recursivePatternCapture(element, callback); - }); - break; + recursivePatternCapture(element, callback) + }) + break case 'AssignmentPattern': - callback(pattern.left); - break; + callback(pattern.left) + break default: } } -let parserOptionsHash = ''; -let prevParserOptions = ''; -let settingsHash = ''; -let prevSettings = ''; +let parserOptionsHash = '' +let prevParserOptions = '' +let settingsHash = '' +let prevSettings = '' /** * don't hold full context object in memory, just grab what we need. * also calculate a cacheKey, where parts of the cacheKey hash are memoized */ function childContext(path, context) { - const { settings, parserOptions, parserPath } = context; + const { settings, parserOptions, parserPath } = context if (JSON.stringify(settings) !== prevSettings) { - settingsHash = hashObject({ settings }).digest('hex'); - prevSettings = JSON.stringify(settings); + settingsHash = hashObject({ settings }).digest('hex') + prevSettings = JSON.stringify(settings) } if (JSON.stringify(parserOptions) !== prevParserOptions) { - parserOptionsHash = hashObject({ parserOptions }).digest('hex'); - prevParserOptions = JSON.stringify(parserOptions); + parserOptionsHash = hashObject({ parserOptions }).digest('hex') + prevParserOptions = JSON.stringify(parserOptions) } return { - cacheKey: String(parserPath) + parserOptionsHash + settingsHash + String(path), + cacheKey: + String(parserPath) + parserOptionsHash + settingsHash + String(path), settings, parserOptions, parserPath, path, - }; + } } /** @@ -818,9 +966,9 @@ function childContext(path, context) { function makeSourceCode(text, ast) { if (SourceCode.length > 1) { // ESLint 3 - return new SourceCode(text, ast); + return new SourceCode(text, ast) } else { // ESLint 4, 5 - return new SourceCode({ text, ast }); + return new SourceCode({ text, ast }) } } diff --git a/src/core/importType.js b/src/core/importType.js index 6a37d1bb1..50f209d8f 100644 --- a/src/core/importType.js +++ b/src/core/importType.js @@ -1,128 +1,160 @@ -import { isAbsolute as nodeIsAbsolute, relative, resolve as nodeResolve } from 'path'; -import isCoreModule from 'is-core-module'; +import { + isAbsolute as nodeIsAbsolute, + relative, + resolve as nodeResolve, +} from 'path' +import isCoreModule from 'is-core-module' -import resolve from 'eslint-module-utils/resolve'; -import { getContextPackagePath } from './packagePath'; +import resolve from '../utils/resolve' +import { getContextPackagePath } from './packagePath' function baseModule(name) { if (isScoped(name)) { - const [scope, pkg] = name.split('/'); - return `${scope}/${pkg}`; + const [scope, pkg] = name.split('/') + return `${scope}/${pkg}` } - const [pkg] = name.split('/'); - return pkg; + const [pkg] = name.split('/') + return pkg } function isInternalRegexMatch(name, settings) { - const internalScope = settings && settings['import/internal-regex']; - return internalScope && new RegExp(internalScope).test(name); + const internalScope = settings && settings['import-x/internal-regex'] + return internalScope && new RegExp(internalScope).test(name) } export function isAbsolute(name) { - return typeof name === 'string' && nodeIsAbsolute(name); + return typeof name === 'string' && nodeIsAbsolute(name) } // path is defined only when a resolver resolves to a non-standard path export function isBuiltIn(name, settings, path) { - if (path || !name) { return false; } - const base = baseModule(name); - const extras = settings && settings['import/core-modules'] || []; - return isCoreModule(base) || extras.indexOf(base) > -1; + if (path || !name) { + return false + } + const base = baseModule(name) + const extras = (settings && settings['import-x/core-modules']) || [] + return isCoreModule(base) || extras.indexOf(base) > -1 } export function isExternalModule(name, path, context) { if (arguments.length < 3) { - throw new TypeError('isExternalModule: name, path, and context are all required'); + throw new TypeError( + 'isExternalModule: name, path, and context are all required', + ) } - return (isModule(name) || isScoped(name)) && typeTest(name, context, path) === 'external'; + return ( + (isModule(name) || isScoped(name)) && + typeTest(name, context, path) === 'external' + ) } export function isExternalModuleMain(name, path, context) { if (arguments.length < 3) { - throw new TypeError('isExternalModule: name, path, and context are all required'); + throw new TypeError( + 'isExternalModule: name, path, and context are all required', + ) } - return isModuleMain(name) && typeTest(name, context, path) === 'external'; + return isModuleMain(name) && typeTest(name, context, path) === 'external' } -const moduleRegExp = /^\w/; +const moduleRegExp = /^\w/ function isModule(name) { - return name && moduleRegExp.test(name); + return name && moduleRegExp.test(name) } -const moduleMainRegExp = /^[\w]((?!\/).)*$/; +const moduleMainRegExp = /^[\w]((?!\/).)*$/ function isModuleMain(name) { - return name && moduleMainRegExp.test(name); + return name && moduleMainRegExp.test(name) } -const scopedRegExp = /^@[^/]+\/?[^/]+/; +const scopedRegExp = /^@[^/]+\/?[^/]+/ export function isScoped(name) { - return name && scopedRegExp.test(name); + return name && scopedRegExp.test(name) } -const scopedMainRegExp = /^@[^/]+\/?[^/]+$/; +const scopedMainRegExp = /^@[^/]+\/?[^/]+$/ export function isScopedMain(name) { - return name && scopedMainRegExp.test(name); + return name && scopedMainRegExp.test(name) } function isRelativeToParent(name) { - return (/^\.\.$|^\.\.[\\/]/).test(name); + return /^\.\.$|^\.\.[\\/]/.test(name) } -const indexFiles = ['.', './', './index', './index.js']; +const indexFiles = ['.', './', './index', './index.js'] function isIndex(name) { - return indexFiles.indexOf(name) !== -1; + return indexFiles.indexOf(name) !== -1 } function isRelativeToSibling(name) { - return (/^\.[\\/]/).test(name); + return /^\.[\\/]/.test(name) } function isExternalPath(path, context) { if (!path) { - return false; + return false } - const { settings } = context; - const packagePath = getContextPackagePath(context); + const { settings } = context + const packagePath = getContextPackagePath(context) if (relative(packagePath, path).startsWith('..')) { - return true; + return true } - const folders = settings && settings['import/external-module-folders'] || ['node_modules']; - return folders.some((folder) => { - const folderPath = nodeResolve(packagePath, folder); - const relativePath = relative(folderPath, path); - return !relativePath.startsWith('..'); - }); + const folders = (settings && + settings['import-x/external-module-folders']) || ['node_modules'] + return folders.some(folder => { + const folderPath = nodeResolve(packagePath, folder) + const relativePath = relative(folderPath, path) + return !relativePath.startsWith('..') + }) } function isInternalPath(path, context) { if (!path) { - return false; + return false } - const packagePath = getContextPackagePath(context); - return !relative(packagePath, path).startsWith('../'); + const packagePath = getContextPackagePath(context) + return !relative(packagePath, path).startsWith('../') } function isExternalLookingName(name) { - return isModule(name) || isScoped(name); + return isModule(name) || isScoped(name) } function typeTest(name, context, path) { - const { settings } = context; - if (isInternalRegexMatch(name, settings)) { return 'internal'; } - if (isAbsolute(name, settings, path)) { return 'absolute'; } - if (isBuiltIn(name, settings, path)) { return 'builtin'; } - if (isRelativeToParent(name, settings, path)) { return 'parent'; } - if (isIndex(name, settings, path)) { return 'index'; } - if (isRelativeToSibling(name, settings, path)) { return 'sibling'; } - if (isExternalPath(path, context)) { return 'external'; } - if (isInternalPath(path, context)) { return 'internal'; } - if (isExternalLookingName(name)) { return 'external'; } - return 'unknown'; + const { settings } = context + if (isInternalRegexMatch(name, settings)) { + return 'internal' + } + if (isAbsolute(name, settings, path)) { + return 'absolute' + } + if (isBuiltIn(name, settings, path)) { + return 'builtin' + } + if (isRelativeToParent(name, settings, path)) { + return 'parent' + } + if (isIndex(name, settings, path)) { + return 'index' + } + if (isRelativeToSibling(name, settings, path)) { + return 'sibling' + } + if (isExternalPath(path, context)) { + return 'external' + } + if (isInternalPath(path, context)) { + return 'internal' + } + if (isExternalLookingName(name)) { + return 'external' + } + return 'unknown' } export default function resolveImportType(name, context) { - return typeTest(name, context, resolve(name, context)); + return typeTest(name, context, resolve(name, context)) } diff --git a/src/core/packagePath.js b/src/core/packagePath.js index 1a7a28f4b..36ea6f61a 100644 --- a/src/core/packagePath.js +++ b/src/core/packagePath.js @@ -1,21 +1,25 @@ -import { dirname } from 'path'; -import pkgUp from 'eslint-module-utils/pkgUp'; -import readPkgUp from 'eslint-module-utils/readPkgUp'; +import { dirname } from 'path' +import pkgUp from '../utils/pkgUp' +import readPkgUp from '../utils/readPkgUp' export function getContextPackagePath(context) { - return getFilePackagePath(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()); + return getFilePackagePath( + context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename(), + ) } export function getFilePackagePath(filePath) { - const fp = pkgUp({ cwd: filePath }); - return dirname(fp); + const fp = pkgUp({ cwd: filePath }) + return dirname(fp) } export function getFilePackageName(filePath) { - const { pkg, path } = readPkgUp({ cwd: filePath, normalize: false }); + const { pkg, path } = readPkgUp({ cwd: filePath, normalize: false }) if (pkg) { // recursion in case of intermediate esm package.json without name found - return pkg.name || getFilePackageName(dirname(dirname(path))); + return pkg.name || getFilePackageName(dirname(dirname(path))) } - return null; + return null } diff --git a/src/core/staticRequire.js b/src/core/staticRequire.js index 88b5000c8..cd6dcd528 100644 --- a/src/core/staticRequire.js +++ b/src/core/staticRequire.js @@ -1,10 +1,12 @@ // todo: merge with module visitor export default function isStaticRequire(node) { - return node - && node.callee - && node.callee.type === 'Identifier' - && node.callee.name === 'require' - && node.arguments.length === 1 - && node.arguments[0].type === 'Literal' - && typeof node.arguments[0].value === 'string'; + return ( + node && + node.callee && + node.callee.type === 'Identifier' && + node.callee.name === 'require' && + node.arguments.length === 1 && + node.arguments[0].type === 'Literal' && + typeof node.arguments[0].value === 'string' + ) } diff --git a/src/docsUrl.js b/src/docsUrl.js index 92b838c09..96f0d76d5 100644 --- a/src/docsUrl.js +++ b/src/docsUrl.js @@ -1,7 +1,7 @@ -import pkg from '../package.json'; +import pkg from '../package.json' -const repoUrl = 'https://github.com/import-js/eslint-plugin-import'; +const repoUrl = 'https://github.com/import-js/eslint-plugin-import' export default function docsUrl(ruleName, commitish = `v${pkg.version}`) { - return `${repoUrl}/blob/${commitish}/docs/rules/${ruleName}.md`; + return `${repoUrl}/blob/${commitish}/docs/rules/${ruleName}.md` } diff --git a/src/importDeclaration.js b/src/importDeclaration.js index 0d5e1870a..a14fbb5c7 100644 --- a/src/importDeclaration.js +++ b/src/importDeclaration.js @@ -1,4 +1,4 @@ export default function importDeclaration(context) { - const ancestors = context.getAncestors(); - return ancestors[ancestors.length - 1]; + const ancestors = context.getAncestors() + return ancestors[ancestors.length - 1] } diff --git a/src/index.js b/src/index.js index feafba900..9ec76ee44 100644 --- a/src/index.js +++ b/src/index.js @@ -52,7 +52,7 @@ export const rules = { // deprecated aliases to rules 'imports-first': require('./rules/imports-first'), -}; +} export const configs = { recommended: require('../config/recommended'), @@ -68,4 +68,4 @@ export const configs = { 'react-native': require('../config/react-native'), electron: require('../config/electron'), typescript: require('../config/typescript'), -}; +} diff --git a/src/rules/consistent-type-specifier-style.js b/src/rules/consistent-type-specifier-style.js index 9119976b1..cb9f5a0b7 100644 --- a/src/rules/consistent-type-specifier-style.js +++ b/src/rules/consistent-type-specifier-style.js @@ -1,39 +1,34 @@ -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' function isComma(token) { - return token.type === 'Punctuator' && token.value === ','; + return token.type === 'Punctuator' && token.value === ',' } function removeSpecifiers(fixes, fixer, sourceCode, specifiers) { for (const specifier of specifiers) { // remove the trailing comma - const token = sourceCode.getTokenAfter(specifier); + const token = sourceCode.getTokenAfter(specifier) if (token && isComma(token)) { - fixes.push(fixer.remove(token)); + fixes.push(fixer.remove(token)) } - fixes.push(fixer.remove(specifier)); + fixes.push(fixer.remove(specifier)) } } -function getImportText( - node, - sourceCode, - specifiers, - kind, -) { - const sourceString = sourceCode.getText(node.source); +function getImportText(node, sourceCode, specifiers, kind) { + const sourceString = sourceCode.getText(node.source) if (specifiers.length === 0) { - return ''; + return '' } - const names = specifiers.map((s) => { + const names = specifiers.map(s => { if (s.imported.name === s.local.name) { - return s.imported.name; + return s.imported.name } - return `${s.imported.name} as ${s.local.name}`; - }); + return `${s.imported.name} as ${s.local.name}` + }) // insert a fresh top-level import - return `import ${kind} {${names.join(', ')}} from ${sourceString};`; + return `import ${kind} {${names.join(', ')}} from ${sourceString};` } module.exports = { @@ -41,7 +36,8 @@ module.exports = { type: 'suggestion', docs: { category: 'Style guide', - description: 'Enforce or ban the use of inline type-only markers for named imports.', + description: + 'Enforce or ban the use of inline type-only markers for named imports.', url: docsUrl('consistent-type-specifier-style'), }, fixable: 'code', @@ -55,47 +51,48 @@ module.exports = { }, create(context) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.getSourceCode() if (context.options[0] === 'prefer-inline') { return { ImportDeclaration(node) { if (node.importKind === 'value' || node.importKind == null) { // top-level value / unknown is valid - return; + return } if ( // no specifiers (import type {} from '') have no specifiers to mark as inline - node.specifiers.length === 0 - || node.specifiers.length === 1 - // default imports are both "inline" and "top-level" - && ( - node.specifiers[0].type === 'ImportDefaultSpecifier' - // namespace imports are both "inline" and "top-level" - || node.specifiers[0].type === 'ImportNamespaceSpecifier' - ) + node.specifiers.length === 0 || + (node.specifiers.length === 1 && + // default imports are both "inline" and "top-level" + (node.specifiers[0].type === 'ImportDefaultSpecifier' || + // namespace imports are both "inline" and "top-level" + node.specifiers[0].type === 'ImportNamespaceSpecifier')) ) { - return; + return } context.report({ node, - message: 'Prefer using inline {{kind}} specifiers instead of a top-level {{kind}}-only import.', + message: + 'Prefer using inline {{kind}} specifiers instead of a top-level {{kind}}-only import.', data: { kind: node.importKind, }, fix(fixer) { - const kindToken = sourceCode.getFirstToken(node, { skip: 1 }); + const kindToken = sourceCode.getFirstToken(node, { skip: 1 }) return [].concat( kindToken ? fixer.remove(kindToken) : [], - node.specifiers.map((specifier) => fixer.insertTextBefore(specifier, `${node.importKind} `)), - ); + node.specifiers.map(specifier => + fixer.insertTextBefore(specifier, `${node.importKind} `), + ), + ) }, - }); + }) }, - }; + } } // prefer-top-level @@ -103,72 +100,88 @@ module.exports = { ImportDeclaration(node) { if ( // already top-level is valid - node.importKind === 'type' - || node.importKind === 'typeof' + node.importKind === 'type' || + node.importKind === 'typeof' || // no specifiers (import {} from '') cannot have inline - so is valid - || node.specifiers.length === 0 - || node.specifiers.length === 1 - // default imports are both "inline" and "top-level" - && ( - node.specifiers[0].type === 'ImportDefaultSpecifier' - // namespace imports are both "inline" and "top-level" - || node.specifiers[0].type === 'ImportNamespaceSpecifier' - ) + node.specifiers.length === 0 || + (node.specifiers.length === 1 && + // default imports are both "inline" and "top-level" + (node.specifiers[0].type === 'ImportDefaultSpecifier' || + // namespace imports are both "inline" and "top-level" + node.specifiers[0].type === 'ImportNamespaceSpecifier')) ) { - return; + return } - const typeSpecifiers = []; - const typeofSpecifiers = []; - const valueSpecifiers = []; - let defaultSpecifier = null; + const typeSpecifiers = [] + const typeofSpecifiers = [] + const valueSpecifiers = [] + let defaultSpecifier = null for (const specifier of node.specifiers) { if (specifier.type === 'ImportDefaultSpecifier') { - defaultSpecifier = specifier; - continue; + defaultSpecifier = specifier + continue } if (specifier.importKind === 'type') { - typeSpecifiers.push(specifier); + typeSpecifiers.push(specifier) } else if (specifier.importKind === 'typeof') { - typeofSpecifiers.push(specifier); - } else if (specifier.importKind === 'value' || specifier.importKind == null) { - valueSpecifiers.push(specifier); + typeofSpecifiers.push(specifier) + } else if ( + specifier.importKind === 'value' || + specifier.importKind == null + ) { + valueSpecifiers.push(specifier) } } - const typeImport = getImportText(node, sourceCode, typeSpecifiers, 'type'); - const typeofImport = getImportText(node, sourceCode, typeofSpecifiers, 'typeof'); - const newImports = `${typeImport}\n${typeofImport}`.trim(); + const typeImport = getImportText( + node, + sourceCode, + typeSpecifiers, + 'type', + ) + const typeofImport = getImportText( + node, + sourceCode, + typeofSpecifiers, + 'typeof', + ) + const newImports = `${typeImport}\n${typeofImport}`.trim() - if (typeSpecifiers.length + typeofSpecifiers.length === node.specifiers.length) { + if ( + typeSpecifiers.length + typeofSpecifiers.length === + node.specifiers.length + ) { // all specifiers have inline specifiers - so we replace the entire import const kind = [].concat( typeSpecifiers.length > 0 ? 'type' : [], typeofSpecifiers.length > 0 ? 'typeof' : [], - ); + ) context.report({ node, - message: 'Prefer using a top-level {{kind}}-only import instead of inline {{kind}} specifiers.', + message: + 'Prefer using a top-level {{kind}}-only import instead of inline {{kind}} specifiers.', data: { kind: kind.join('/'), }, fix(fixer) { - return fixer.replaceText(node, newImports); + return fixer.replaceText(node, newImports) }, - }); + }) } else { // remove specific specifiers and insert new imports for them for (const specifier of typeSpecifiers.concat(typeofSpecifiers)) { context.report({ node: specifier, - message: 'Prefer using a top-level {{kind}}-only import instead of inline {{kind}} specifiers.', + message: + 'Prefer using a top-level {{kind}}-only import instead of inline {{kind}} specifiers.', data: { kind: specifier.importKind, }, fix(fixer) { - const fixes = []; + const fixes = [] // if there are no value specifiers, then the other report fixer will be called, not this one @@ -176,8 +189,8 @@ module.exports = { // import { Value, type Type } from 'mod'; // we can just remove the type specifiers - removeSpecifiers(fixes, fixer, sourceCode, typeSpecifiers); - removeSpecifiers(fixes, fixer, sourceCode, typeofSpecifiers); + removeSpecifiers(fixes, fixer, sourceCode, typeSpecifiers) + removeSpecifiers(fixes, fixer, sourceCode, typeofSpecifiers) // make the import nicely formatted by also removing the trailing comma after the last value import // eg @@ -186,9 +199,11 @@ module.exports = { // import { Value } from 'mod'; // not // import { Value, } from 'mod'; - const maybeComma = sourceCode.getTokenAfter(valueSpecifiers[valueSpecifiers.length - 1]); + const maybeComma = sourceCode.getTokenAfter( + valueSpecifiers[valueSpecifiers.length - 1], + ) if (isComma(maybeComma)) { - fixes.push(fixer.remove(maybeComma)); + fixes.push(fixer.remove(maybeComma)) } } else if (defaultSpecifier) { // import Default, { type Type } from 'mod'; @@ -196,26 +211,28 @@ module.exports = { // remove the entire curly block so we don't leave an empty one behind // NOTE - the default specifier *must* be the first specifier always! // so a comma exists that we also have to clean up or else it's bad syntax - const comma = sourceCode.getTokenAfter(defaultSpecifier, isComma); + const comma = sourceCode.getTokenAfter( + defaultSpecifier, + isComma, + ) const closingBrace = sourceCode.getTokenAfter( node.specifiers[node.specifiers.length - 1], - (token) => token.type === 'Punctuator' && token.value === '}', - ); - fixes.push(fixer.removeRange([ - comma.range[0], - closingBrace.range[1], - ])); + token => token.type === 'Punctuator' && token.value === '}', + ) + fixes.push( + fixer.removeRange([comma.range[0], closingBrace.range[1]]), + ) } return fixes.concat( // insert the new imports after the old declaration fixer.insertTextAfter(node, `\n${newImports}`), - ); + ) }, - }); + }) } } }, - }; + } }, -}; +} diff --git a/src/rules/default.js b/src/rules/default.js index 297a80c46..1e3e0fc53 100644 --- a/src/rules/default.js +++ b/src/rules/default.js @@ -1,12 +1,13 @@ -import Exports from '../ExportMap'; -import docsUrl from '../docsUrl'; +import Exports from '../ExportMap' +import docsUrl from '../docsUrl' module.exports = { meta: { type: 'problem', docs: { category: 'Static analysis', - description: 'Ensure a default export is present, given a default import.', + description: + 'Ensure a default export is present, given a default import.', url: docsUrl('default'), }, schema: [], @@ -15,26 +16,30 @@ module.exports = { create(context) { function checkDefault(specifierType, node) { const defaultSpecifier = node.specifiers.find( - (specifier) => specifier.type === specifierType, - ); + specifier => specifier.type === specifierType, + ) - if (!defaultSpecifier) { return; } - const imports = Exports.get(node.source.value, context); - if (imports == null) { return; } + if (!defaultSpecifier) { + return + } + const imports = Exports.get(node.source.value, context) + if (imports == null) { + return + } if (imports.errors.length) { - imports.reportErrors(context, node); + imports.reportErrors(context, node) } else if (imports.get('default') === undefined) { context.report({ node: defaultSpecifier, message: `No default export found in imported module "${node.source.value}".`, - }); + }) } } return { ImportDeclaration: checkDefault.bind(null, 'ImportDefaultSpecifier'), ExportNamedDeclaration: checkDefault.bind(null, 'ExportDefaultSpecifier'), - }; + } }, -}; +} diff --git a/src/rules/dynamic-import-chunkname.js b/src/rules/dynamic-import-chunkname.js index a62e5c6c1..81e70c220 100644 --- a/src/rules/dynamic-import-chunkname.js +++ b/src/rules/dynamic-import-chunkname.js @@ -1,123 +1,132 @@ -import vm from 'vm'; -import docsUrl from '../docsUrl'; +import vm from 'vm' +import docsUrl from '../docsUrl' module.exports = { meta: { type: 'suggestion', docs: { category: 'Style guide', - description: 'Enforce a leading comment with the webpackChunkName for dynamic imports.', + description: + 'Enforce a leading comment with the webpackChunkName for dynamic imports.', url: docsUrl('dynamic-import-chunkname'), }, - schema: [{ - type: 'object', - properties: { - importFunctions: { - type: 'array', - uniqueItems: true, - items: { + schema: [ + { + type: 'object', + properties: { + importFunctions: { + type: 'array', + uniqueItems: true, + items: { + type: 'string', + }, + }, + allowEmpty: { + type: 'boolean', + }, + webpackChunknameFormat: { type: 'string', }, }, - allowEmpty: { - type: 'boolean', - }, - webpackChunknameFormat: { - type: 'string', - }, }, - }], + ], }, create(context) { - const config = context.options[0]; - const { importFunctions = [], allowEmpty = false } = config || {}; - const { webpackChunknameFormat = '([0-9a-zA-Z-_/.]|\\[(request|index)\\])+' } = config || {}; + const config = context.options[0] + const { importFunctions = [], allowEmpty = false } = config || {} + const { + webpackChunknameFormat = '([0-9a-zA-Z-_/.]|\\[(request|index)\\])+', + } = config || {} - const paddedCommentRegex = /^ (\S[\s\S]+\S) $/; - const commentStyleRegex = /^( ((webpackChunkName: .+)|((webpackPrefetch|webpackPreload): (true|false|-?[0-9]+))|(webpackIgnore: (true|false))|((webpackInclude|webpackExclude): \/.*\/)|(webpackMode: ["'](lazy|lazy-once|eager|weak)["'])|(webpackExports: (['"]\w+['"]|\[(['"]\w+['"], *)+(['"]\w+['"]*)\]))),?)+ $/; - const chunkSubstrFormat = ` webpackChunkName: ["']${webpackChunknameFormat}["'],? `; - const chunkSubstrRegex = new RegExp(chunkSubstrFormat); + const paddedCommentRegex = /^ (\S[\s\S]+\S) $/ + const commentStyleRegex = + /^( ((webpackChunkName: .+)|((webpackPrefetch|webpackPreload): (true|false|-?[0-9]+))|(webpackIgnore: (true|false))|((webpackInclude|webpackExclude): \/.*\/)|(webpackMode: ["'](lazy|lazy-once|eager|weak)["'])|(webpackExports: (['"]\w+['"]|\[(['"]\w+['"], *)+(['"]\w+['"]*)\]))),?)+ $/ + const chunkSubstrFormat = ` webpackChunkName: ["']${webpackChunknameFormat}["'],? ` + const chunkSubstrRegex = new RegExp(chunkSubstrFormat) function run(node, arg) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.getSourceCode() const leadingComments = sourceCode.getCommentsBefore ? sourceCode.getCommentsBefore(arg) // This method is available in ESLint >= 4. - : sourceCode.getComments(arg).leading; // This method is deprecated in ESLint 7. + : sourceCode.getComments(arg).leading // This method is deprecated in ESLint 7. if ((!leadingComments || leadingComments.length === 0) && !allowEmpty) { context.report({ node, - message: 'dynamic imports require a leading comment with the webpack chunkname', - }); - return; + message: + 'dynamic imports require a leading comment with the webpack chunkname', + }) + return } - let isChunknamePresent = false; + let isChunknamePresent = false for (const comment of leadingComments) { if (comment.type !== 'Block') { context.report({ node, - message: 'dynamic imports require a /* foo */ style comment, not a // foo comment', - }); - return; + message: + 'dynamic imports require a /* foo */ style comment, not a // foo comment', + }) + return } if (!paddedCommentRegex.test(comment.value)) { context.report({ node, message: `dynamic imports require a block comment padded with spaces - /* foo */`, - }); - return; + }) + return } try { // just like webpack itself does - vm.runInNewContext(`(function() {return {${comment.value}}})()`); + vm.runInNewContext(`(function() {return {${comment.value}}})()`) } catch (error) { context.report({ node, message: `dynamic imports require a "webpack" comment with valid syntax`, - }); - return; + }) + return } if (!commentStyleRegex.test(comment.value)) { context.report({ node, - message: - `dynamic imports require a "webpack" comment with valid syntax`, - }); - return; + message: `dynamic imports require a "webpack" comment with valid syntax`, + }) + return } if (chunkSubstrRegex.test(comment.value)) { - isChunknamePresent = true; + isChunknamePresent = true } } if (!isChunknamePresent && !allowEmpty) { context.report({ node, - message: - `dynamic imports require a leading comment in the form /*${chunkSubstrFormat}*/`, - }); + message: `dynamic imports require a leading comment in the form /*${chunkSubstrFormat}*/`, + }) } } return { ImportExpression(node) { - run(node, node.source); + run(node, node.source) }, CallExpression(node) { - if (node.callee.type !== 'Import' && importFunctions.indexOf(node.callee.name) < 0) { - return; + if ( + node.callee.type !== 'Import' && + importFunctions.indexOf(node.callee.name) < 0 + ) { + return } - run(node, node.arguments[0]); + run(node, node.arguments[0]) }, - }; + } }, -}; +} diff --git a/src/rules/export.js b/src/rules/export.js index c540f1e3c..34ac56a8a 100644 --- a/src/rules/export.js +++ b/src/rules/export.js @@ -1,7 +1,7 @@ -import ExportMap, { recursivePatternCapture } from '../ExportMap'; -import docsUrl from '../docsUrl'; -import includes from 'array-includes'; -import flatMap from 'array.prototype.flatmap'; +import ExportMap, { recursivePatternCapture } from '../ExportMap' +import docsUrl from '../docsUrl' +import includes from 'array-includes' +import flatMap from 'array.prototype.flatmap' /* Notes on TypeScript namespaces aka TSModuleDeclaration: @@ -22,8 +22,8 @@ ambient namespaces: - have no other restrictions */ -const rootProgram = 'root'; -const tsTypePrefix = 'type:'; +const rootProgram = 'root' +const tsTypePrefix = 'type:' /** * Detect function overloads like: @@ -36,32 +36,30 @@ const tsTypePrefix = 'type:'; * @returns {boolean} */ function isTypescriptFunctionOverloads(nodes) { - const nodesArr = Array.from(nodes); - - const idents = flatMap( - nodesArr, - (node) => node.declaration && ( - node.declaration.type === 'TSDeclareFunction' // eslint 6+ - || node.declaration.type === 'TSEmptyBodyFunctionDeclaration' // eslint 4-5 - ) + const nodesArr = Array.from(nodes) + + const idents = flatMap(nodesArr, node => + node.declaration && + (node.declaration.type === 'TSDeclareFunction' || // eslint 6+ + node.declaration.type === 'TSEmptyBodyFunctionDeclaration') // eslint 4-5 ? node.declaration.id.name : [], - ); + ) if (new Set(idents).size !== idents.length) { - return true; + return true } - const types = new Set(nodesArr.map((node) => node.parent.type)); + const types = new Set(nodesArr.map(node => node.parent.type)) if (!types.has('TSDeclareFunction')) { - return false; + return false } if (types.size === 1) { - return true; + return true } if (types.size === 2 && types.has('FunctionDeclaration')) { - return true; + return true } - return false; + return false } /** @@ -74,18 +72,25 @@ function isTypescriptFunctionOverloads(nodes) { * @returns {boolean} */ function isTypescriptNamespaceMerging(nodes) { - const types = new Set(Array.from(nodes, (node) => node.parent.type)); - const noNamespaceNodes = Array.from(nodes).filter((node) => node.parent.type !== 'TSModuleDeclaration'); - - return types.has('TSModuleDeclaration') - && ( - types.size === 1 + const types = new Set(Array.from(nodes, node => node.parent.type)) + const noNamespaceNodes = Array.from(nodes).filter( + node => node.parent.type !== 'TSModuleDeclaration', + ) + + return ( + types.has('TSModuleDeclaration') && + (types.size === 1 || // Merging with functions - || types.size === 2 && (types.has('FunctionDeclaration') || types.has('TSDeclareFunction')) - || types.size === 3 && types.has('FunctionDeclaration') && types.has('TSDeclareFunction') + (types.size === 2 && + (types.has('FunctionDeclaration') || types.has('TSDeclareFunction'))) || + (types.size === 3 && + types.has('FunctionDeclaration') && + types.has('TSDeclareFunction')) || // Merging with classes or enums - || types.size === 2 && (types.has('ClassDeclaration') || types.has('TSEnumDeclaration')) && noNamespaceNodes.length === 1 - ); + (types.size === 2 && + (types.has('ClassDeclaration') || types.has('TSEnumDeclaration')) && + noNamespaceNodes.length === 1)) + ) } /** @@ -100,16 +105,16 @@ function isTypescriptNamespaceMerging(nodes) { * @returns {boolean} */ function shouldSkipTypescriptNamespace(node, nodes) { - const types = new Set(Array.from(nodes, (node) => node.parent.type)); - - return !isTypescriptNamespaceMerging(nodes) - && node.parent.type === 'TSModuleDeclaration' - && ( - types.has('TSEnumDeclaration') - || types.has('ClassDeclaration') - || types.has('FunctionDeclaration') - || types.has('TSDeclareFunction') - ); + const types = new Set(Array.from(nodes, node => node.parent.type)) + + return ( + !isTypescriptNamespaceMerging(nodes) && + node.parent.type === 'TSModuleDeclaration' && + (types.has('TSEnumDeclaration') || + types.has('ClassDeclaration') || + types.has('FunctionDeclaration') || + types.has('TSDeclareFunction')) + ) } module.exports = { @@ -117,45 +122,46 @@ module.exports = { type: 'problem', docs: { category: 'Helpful warnings', - description: 'Forbid any invalid exports, i.e. re-export of the same name.', + description: + 'Forbid any invalid exports, i.e. re-export of the same name.', url: docsUrl('export'), }, schema: [], }, create(context) { - const namespace = new Map([[rootProgram, new Map()]]); + const namespace = new Map([[rootProgram, new Map()]]) function addNamed(name, node, parent, isType) { if (!namespace.has(parent)) { - namespace.set(parent, new Map()); + namespace.set(parent, new Map()) } - const named = namespace.get(parent); + const named = namespace.get(parent) - const key = isType ? `${tsTypePrefix}${name}` : name; - let nodes = named.get(key); + const key = isType ? `${tsTypePrefix}${name}` : name + let nodes = named.get(key) if (nodes == null) { - nodes = new Set(); - named.set(key, nodes); + nodes = new Set() + named.set(key, nodes) } - nodes.add(node); + nodes.add(node) } function getParent(node) { if (node.parent && node.parent.type === 'TSModuleBlock') { - return node.parent.parent; + return node.parent.parent } // just in case somehow a non-ts namespace export declaration isn't directly // parented to the root Program node - return rootProgram; + return rootProgram } return { ExportDefaultDeclaration(node) { - addNamed('default', node, getParent(node)); + addNamed('default', node, getParent(node)) }, ExportSpecifier(node) { @@ -163,88 +169,119 @@ module.exports = { node.exported.name || node.exported.value, node.exported, getParent(node.parent), - ); + ) }, ExportNamedDeclaration(node) { - if (node.declaration == null) { return; } + if (node.declaration == null) { + return + } - const parent = getParent(node); + const parent = getParent(node) // support for old TypeScript versions - const isTypeVariableDecl = node.declaration.kind === 'type'; + const isTypeVariableDecl = node.declaration.kind === 'type' if (node.declaration.id != null) { - if (includes([ - 'TSTypeAliasDeclaration', - 'TSInterfaceDeclaration', - ], node.declaration.type)) { - addNamed(node.declaration.id.name, node.declaration.id, parent, true); + if ( + includes( + ['TSTypeAliasDeclaration', 'TSInterfaceDeclaration'], + node.declaration.type, + ) + ) { + addNamed( + node.declaration.id.name, + node.declaration.id, + parent, + true, + ) } else { - addNamed(node.declaration.id.name, node.declaration.id, parent, isTypeVariableDecl); + addNamed( + node.declaration.id.name, + node.declaration.id, + parent, + isTypeVariableDecl, + ) } } if (node.declaration.declarations != null) { for (const declaration of node.declaration.declarations) { - recursivePatternCapture(declaration.id, (v) => { addNamed(v.name, v, parent, isTypeVariableDecl); }); + recursivePatternCapture(declaration.id, v => { + addNamed(v.name, v, parent, isTypeVariableDecl) + }) } } }, ExportAllDeclaration(node) { - if (node.source == null) { return; } // not sure if this is ever true + if (node.source == null) { + return + } // not sure if this is ever true // `export * as X from 'path'` does not conflict - if (node.exported && node.exported.name) { return; } + if (node.exported && node.exported.name) { + return + } - const remoteExports = ExportMap.get(node.source.value, context); - if (remoteExports == null) { return; } + const remoteExports = ExportMap.get(node.source.value, context) + if (remoteExports == null) { + return + } if (remoteExports.errors.length) { - remoteExports.reportErrors(context, node); - return; + remoteExports.reportErrors(context, node) + return } - const parent = getParent(node); + const parent = getParent(node) - let any = false; + let any = false remoteExports.forEach((v, name) => { if (name !== 'default') { - any = true; // poor man's filter - addNamed(name, node, parent); + any = true // poor man's filter + addNamed(name, node, parent) } - }); + }) if (!any) { context.report( node.source, `No named exports found in module '${node.source.value}'.`, - ); + ) } }, 'Program:exit'() { for (const [, named] of namespace) { for (const [name, nodes] of named) { - if (nodes.size <= 1) { continue; } + if (nodes.size <= 1) { + continue + } - if (isTypescriptFunctionOverloads(nodes) || isTypescriptNamespaceMerging(nodes)) { continue; } + if ( + isTypescriptFunctionOverloads(nodes) || + isTypescriptNamespaceMerging(nodes) + ) { + continue + } for (const node of nodes) { - if (shouldSkipTypescriptNamespace(node, nodes)) { continue; } + if (shouldSkipTypescriptNamespace(node, nodes)) { + continue + } if (name === 'default') { - context.report(node, 'Multiple default exports.'); + context.report(node, 'Multiple default exports.') } else { context.report( node, `Multiple exports of name '${name.replace(tsTypePrefix, '')}'.`, - ); + ) } } } } }, - }; + } }, -}; +} diff --git a/src/rules/exports-last.js b/src/rules/exports-last.js index 1e79f8339..b542dd464 100644 --- a/src/rules/exports-last.js +++ b/src/rules/exports-last.js @@ -1,11 +1,13 @@ -import findLastIndex from 'array.prototype.findlastindex'; +import findLastIndex from 'array.prototype.findlastindex' -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' function isNonExportStatement({ type }) { - return type !== 'ExportDefaultDeclaration' - && type !== 'ExportNamedDeclaration' - && type !== 'ExportAllDeclaration'; + return ( + type !== 'ExportDefaultDeclaration' && + type !== 'ExportNamedDeclaration' && + type !== 'ExportAllDeclaration' + ) } module.exports = { @@ -22,19 +24,23 @@ module.exports = { create(context) { return { Program({ body }) { - const lastNonExportStatementIndex = findLastIndex(body, isNonExportStatement); + const lastNonExportStatementIndex = findLastIndex( + body, + isNonExportStatement, + ) if (lastNonExportStatementIndex !== -1) { - body.slice(0, lastNonExportStatementIndex).forEach((node) => { + body.slice(0, lastNonExportStatementIndex).forEach(node => { if (!isNonExportStatement(node)) { context.report({ node, - message: 'Export statements should appear at the end of the file', - }); + message: + 'Export statements should appear at the end of the file', + }) } - }); + }) } }, - }; + } }, -}; +} diff --git a/src/rules/extensions.js b/src/rules/extensions.js index b1e5c6d9f..b33f6f8dd 100644 --- a/src/rules/extensions.js +++ b/src/rules/extensions.js @@ -1,62 +1,60 @@ -import path from 'path'; +import path from 'path' -import resolve from 'eslint-module-utils/resolve'; -import { isBuiltIn, isExternalModule, isScoped } from '../core/importType'; -import moduleVisitor from 'eslint-module-utils/moduleVisitor'; -import docsUrl from '../docsUrl'; +import resolve from '../utils/resolve' +import { isBuiltIn, isExternalModule, isScoped } from '../core/importType' +import moduleVisitor from '../utils/moduleVisitor' +import docsUrl from '../docsUrl' -const enumValues = { enum: ['always', 'ignorePackages', 'never'] }; +const enumValues = { enum: ['always', 'ignorePackages', 'never'] } const patternProperties = { type: 'object', patternProperties: { '.*': enumValues }, -}; +} const properties = { type: 'object', properties: { pattern: patternProperties, ignorePackages: { type: 'boolean' }, }, -}; +} function buildProperties(context) { - const result = { defaultConfig: 'never', pattern: {}, ignorePackages: false, - }; - - context.options.forEach((obj) => { + } + context.options.forEach(obj => { // If this is a string, set defaultConfig to its value if (typeof obj === 'string') { - result.defaultConfig = obj; - return; + result.defaultConfig = obj + return } // If this is not the new structure, transfer all props to result.pattern if (obj.pattern === undefined && obj.ignorePackages === undefined) { - Object.assign(result.pattern, obj); - return; + Object.assign(result.pattern, obj) + return } // If pattern is provided, transfer all props if (obj.pattern !== undefined) { - Object.assign(result.pattern, obj.pattern); + Object.assign(result.pattern, obj.pattern) } // If ignorePackages is provided, transfer it to result if (obj.ignorePackages !== undefined) { - result.ignorePackages = obj.ignorePackages; + result.ignorePackages = obj.ignorePackages } - }); + }) if (result.defaultConfig === 'ignorePackages') { - result.defaultConfig = 'always'; - result.ignorePackages = true; + result.defaultConfig = 'always' + result.ignorePackages = true } - return result; + return result } module.exports = { @@ -64,7 +62,8 @@ module.exports = { type: 'suggestion', docs: { category: 'Style guide', - description: 'Ensure consistent use of file extension within the import path.', + description: + 'Ensure consistent use of file extension within the import path.', url: docsUrl('extensions'), }, @@ -77,10 +76,7 @@ module.exports = { }, { type: 'array', - items: [ - enumValues, - properties, - ], + items: [enumValues, properties], additionalItems: false, }, { @@ -95,10 +91,7 @@ module.exports = { }, { type: 'array', - items: [ - enumValues, - patternProperties, - ], + items: [enumValues, patternProperties], additionalItems: false, }, ], @@ -106,88 +99,107 @@ module.exports = { }, create(context) { - - const props = buildProperties(context); + const props = buildProperties(context) function getModifier(extension) { - return props.pattern[extension] || props.defaultConfig; + return props.pattern[extension] || props.defaultConfig } function isUseOfExtensionRequired(extension, isPackage) { - return getModifier(extension) === 'always' && (!props.ignorePackages || !isPackage); + return ( + getModifier(extension) === 'always' && + (!props.ignorePackages || !isPackage) + ) } function isUseOfExtensionForbidden(extension) { - return getModifier(extension) === 'never'; + return getModifier(extension) === 'never' } function isResolvableWithoutExtension(file) { - const extension = path.extname(file); - const fileWithoutExtension = file.slice(0, -extension.length); - const resolvedFileWithoutExtension = resolve(fileWithoutExtension, context); + const extension = path.extname(file) + const fileWithoutExtension = file.slice(0, -extension.length) + const resolvedFileWithoutExtension = resolve( + fileWithoutExtension, + context, + ) - return resolvedFileWithoutExtension === resolve(file, context); + return resolvedFileWithoutExtension === resolve(file, context) } function isExternalRootModule(file) { - if (file === '.' || file === '..') { return false; } - const slashCount = file.split('/').length - 1; + if (file === '.' || file === '..') { + return false + } + const slashCount = file.split('/').length - 1 - if (slashCount === 0) { return true; } - if (isScoped(file) && slashCount <= 1) { return true; } - return false; + if (slashCount === 0) { + return true + } + if (isScoped(file) && slashCount <= 1) { + return true + } + return false } function checkFileExtension(source, node) { // bail if the declaration doesn't have a source, e.g. "export { foo };", or if it's only partially typed like in an editor - if (!source || !source.value) { return; } + if (!source || !source.value) { + return + } - const importPathWithQueryString = source.value; + const importPathWithQueryString = source.value // don't enforce anything on builtins - if (isBuiltIn(importPathWithQueryString, context.settings)) { return; } + if (isBuiltIn(importPathWithQueryString, context.settings)) { + return + } - const importPath = importPathWithQueryString.replace(/\?(.*)$/, ''); + const importPath = importPathWithQueryString.replace(/\?(.*)$/, '') // don't enforce in root external packages as they may have names with `.js`. // Like `import Decimal from decimal.js`) - if (isExternalRootModule(importPath)) { return; } + if (isExternalRootModule(importPath)) { + return + } - const resolvedPath = resolve(importPath, context); + const resolvedPath = resolve(importPath, context) // get extension from resolved path, if possible. // for unresolved, use source value. - const extension = path.extname(resolvedPath || importPath).substring(1); + const extension = path.extname(resolvedPath || importPath).substring(1) // determine if this is a module - const isPackage = isExternalModule( - importPath, - resolve(importPath, context), - context, - ) || isScoped(importPath); + const isPackage = + isExternalModule(importPath, resolve(importPath, context), context) || + isScoped(importPath) if (!extension || !importPath.endsWith(`.${extension}`)) { // ignore type-only imports and exports - if (node.importKind === 'type' || node.exportKind === 'type') { return; } - const extensionRequired = isUseOfExtensionRequired(extension, isPackage); - const extensionForbidden = isUseOfExtensionForbidden(extension); + if (node.importKind === 'type' || node.exportKind === 'type') { + return + } + const extensionRequired = isUseOfExtensionRequired(extension, isPackage) + const extensionForbidden = isUseOfExtensionForbidden(extension) if (extensionRequired && !extensionForbidden) { context.report({ node: source, - message: - `Missing file extension ${extension ? `"${extension}" ` : ''}for "${importPathWithQueryString}"`, - }); + message: `Missing file extension ${extension ? `"${extension}" ` : ''}for "${importPathWithQueryString}"`, + }) } } else if (extension) { - if (isUseOfExtensionForbidden(extension) && isResolvableWithoutExtension(importPath)) { + if ( + isUseOfExtensionForbidden(extension) && + isResolvableWithoutExtension(importPath) + ) { context.report({ node: source, message: `Unexpected use of file extension "${extension}" for "${importPathWithQueryString}"`, - }); + }) } } } - return moduleVisitor(checkFileExtension, { commonjs: true }); + return moduleVisitor(checkFileExtension, { commonjs: true }) }, -}; +} diff --git a/src/rules/first.js b/src/rules/first.js index f8cc273a3..95561522d 100644 --- a/src/rules/first.js +++ b/src/rules/first.js @@ -1,9 +1,9 @@ -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' function getImportValue(node) { return node.type === 'ImportDeclaration' ? node.source.value - : node.moduleReference.expression.value; + : node.moduleReference.expression.value } module.exports = { @@ -25,120 +25,139 @@ module.exports = { create(context) { function isPossibleDirective(node) { - return node.type === 'ExpressionStatement' - && node.expression.type === 'Literal' - && typeof node.expression.value === 'string'; + return ( + node.type === 'ExpressionStatement' && + node.expression.type === 'Literal' && + typeof node.expression.value === 'string' + ) } return { Program(n) { - const body = n.body; + const body = n.body if (!body) { - return; + return } - const absoluteFirst = context.options[0] === 'absolute-first'; - const message = 'Import in body of module; reorder to top.'; - const sourceCode = context.getSourceCode(); - const originSourceCode = sourceCode.getText(); - let nonImportCount = 0; - let anyExpressions = false; - let anyRelative = false; - let lastLegalImp = null; - const errorInfos = []; - let shouldSort = true; - let lastSortNodesIndex = 0; + const absoluteFirst = context.options[0] === 'absolute-first' + const message = 'Import in body of module; reorder to top.' + const sourceCode = context.getSourceCode() + const originSourceCode = sourceCode.getText() + let nonImportCount = 0 + let anyExpressions = false + let anyRelative = false + let lastLegalImp = null + const errorInfos = [] + let shouldSort = true + let lastSortNodesIndex = 0 body.forEach(function (node, index) { if (!anyExpressions && isPossibleDirective(node)) { - return; + return } - anyExpressions = true; + anyExpressions = true - if (node.type === 'ImportDeclaration' || node.type === 'TSImportEqualsDeclaration') { + if ( + node.type === 'ImportDeclaration' || + node.type === 'TSImportEqualsDeclaration' + ) { if (absoluteFirst) { - if ((/^\./).test(getImportValue(node))) { - anyRelative = true; + if (/^\./.test(getImportValue(node))) { + anyRelative = true } else if (anyRelative) { context.report({ - node: node.type === 'ImportDeclaration' ? node.source : node.moduleReference, - message: 'Absolute imports should come before relative imports.', - }); + node: + node.type === 'ImportDeclaration' + ? node.source + : node.moduleReference, + message: + 'Absolute imports should come before relative imports.', + }) } } if (nonImportCount > 0) { for (const variable of context.getDeclaredVariables(node)) { - if (!shouldSort) { break; } - const references = variable.references; + if (!shouldSort) { + break + } + const references = variable.references if (references.length) { for (const reference of references) { if (reference.identifier.range[0] < node.range[1]) { - shouldSort = false; - break; + shouldSort = false + break } } } } - shouldSort && (lastSortNodesIndex = errorInfos.length); + shouldSort && (lastSortNodesIndex = errorInfos.length) errorInfos.push({ node, range: [body[index - 1].range[1], node.range[1]], - }); + }) } else { - lastLegalImp = node; + lastLegalImp = node } } else { - nonImportCount++; + nonImportCount++ } - }); - if (!errorInfos.length) { return; } + }) + if (!errorInfos.length) { + return + } errorInfos.forEach(function (errorInfo, index) { - const node = errorInfo.node; + const node = errorInfo.node const infos = { node, message, - }; + } if (index < lastSortNodesIndex) { infos.fix = function (fixer) { - return fixer.insertTextAfter(node, ''); - }; + return fixer.insertTextAfter(node, '') + } } else if (index === lastSortNodesIndex) { - const sortNodes = errorInfos.slice(0, lastSortNodesIndex + 1); + const sortNodes = errorInfos.slice(0, lastSortNodesIndex + 1) infos.fix = function (fixer) { const removeFixers = sortNodes.map(function (_errorInfo) { - return fixer.removeRange(_errorInfo.range); - }); - const range = [0, removeFixers[removeFixers.length - 1].range[1]]; - let insertSourceCode = sortNodes.map(function (_errorInfo) { - const nodeSourceCode = String.prototype.slice.apply( - originSourceCode, _errorInfo.range, - ); - if ((/\S/).test(nodeSourceCode[0])) { - return `\n${nodeSourceCode}`; - } - return nodeSourceCode; - }).join(''); - let insertFixer = null; - let replaceSourceCode = ''; + return fixer.removeRange(_errorInfo.range) + }) + const range = [0, removeFixers[removeFixers.length - 1].range[1]] + let insertSourceCode = sortNodes + .map(function (_errorInfo) { + const nodeSourceCode = String.prototype.slice.apply( + originSourceCode, + _errorInfo.range, + ) + if (/\S/.test(nodeSourceCode[0])) { + return `\n${nodeSourceCode}` + } + return nodeSourceCode + }) + .join('') + let insertFixer = null + let replaceSourceCode = '' if (!lastLegalImp) { - insertSourceCode = insertSourceCode.trim() + insertSourceCode.match(/^(\s+)/)[0]; + insertSourceCode = + insertSourceCode.trim() + insertSourceCode.match(/^(\s+)/)[0] } insertFixer = lastLegalImp ? fixer.insertTextAfter(lastLegalImp, insertSourceCode) - : fixer.insertTextBefore(body[0], insertSourceCode); + : fixer.insertTextBefore(body[0], insertSourceCode) - const fixers = [insertFixer].concat(removeFixers); + const fixers = [insertFixer].concat(removeFixers) fixers.forEach((computedFixer, i) => { - replaceSourceCode += originSourceCode.slice( - fixers[i - 1] ? fixers[i - 1].range[1] : 0, computedFixer.range[0], - ) + computedFixer.text; - }); + replaceSourceCode += + originSourceCode.slice( + fixers[i - 1] ? fixers[i - 1].range[1] : 0, + computedFixer.range[0], + ) + computedFixer.text + }) - return fixer.replaceTextRange(range, replaceSourceCode); - }; + return fixer.replaceTextRange(range, replaceSourceCode) + } } - context.report(infos); - }); + context.report(infos) + }) }, - }; + } }, -}; +} diff --git a/src/rules/group-exports.js b/src/rules/group-exports.js index 7978130d3..7888aa8d8 100644 --- a/src/rules/group-exports.js +++ b/src/rules/group-exports.js @@ -1,20 +1,23 @@ -import docsUrl from '../docsUrl'; -import values from 'object.values'; -import flat from 'array.prototype.flat'; +import docsUrl from '../docsUrl' +import values from 'object.values' +import flat from 'array.prototype.flat' const meta = { type: 'suggestion', docs: { category: 'Style guide', - description: 'Prefer named exports to be grouped together in a single export declaration', + description: + 'Prefer named exports to be grouped together in a single export declaration', url: docsUrl('group-exports'), }, -}; +} /* eslint-disable max-len */ const errors = { - ExportNamedDeclaration: 'Multiple named export declarations; consolidate all named exports into a single export declaration', - AssignmentExpression: 'Multiple CommonJS exports; consolidate all exports into a single assignment to `module.exports`', -}; + ExportNamedDeclaration: + 'Multiple named export declarations; consolidate all named exports into a single export declaration', + AssignmentExpression: + 'Multiple CommonJS exports; consolidate all exports into a single assignment to `module.exports`', +} /* eslint-enable max-len */ /** @@ -30,20 +33,20 @@ const errors = { * @private */ function accessorChain(node) { - const chain = []; + const chain = [] do { - chain.unshift(node.property.name); + chain.unshift(node.property.name) if (node.object.type === 'Identifier') { - chain.unshift(node.object.name); - break; + chain.unshift(node.object.name) + break } - node = node.object; - } while (node.type === 'MemberExpression'); + node = node.object + } while (node.type === 'MemberExpression') - return chain; + return chain } function create(context) { @@ -59,97 +62,107 @@ function create(context) { commonjs: { set: new Set(), }, - }; + } return { ExportNamedDeclaration(node) { - const target = node.exportKind === 'type' ? nodes.types : nodes.modules; + const target = node.exportKind === 'type' ? nodes.types : nodes.modules if (!node.source) { - target.set.add(node); + target.set.add(node) } else if (Array.isArray(target.sources[node.source.value])) { - target.sources[node.source.value].push(node); + target.sources[node.source.value].push(node) } else { - target.sources[node.source.value] = [node]; + target.sources[node.source.value] = [node] } }, AssignmentExpression(node) { if (node.left.type !== 'MemberExpression') { - return; + return } - const chain = accessorChain(node.left); + const chain = accessorChain(node.left) // Assignments to module.exports // Deeper assignments are ignored since they just modify what's already being exported // (ie. module.exports.exported.prop = true is ignored) - if (chain[0] === 'module' && chain[1] === 'exports' && chain.length <= 3) { - nodes.commonjs.set.add(node); - return; + if ( + chain[0] === 'module' && + chain[1] === 'exports' && + chain.length <= 3 + ) { + nodes.commonjs.set.add(node) + return } // Assignments to exports (exports.* = *) if (chain[0] === 'exports' && chain.length === 2) { - nodes.commonjs.set.add(node); - return; + nodes.commonjs.set.add(node) + return } }, 'Program:exit': function onExit() { // Report multiple `export` declarations (ES2015 modules) if (nodes.modules.set.size > 1) { - nodes.modules.set.forEach((node) => { + nodes.modules.set.forEach(node => { context.report({ node, message: errors[node.type], - }); - }); + }) + }) } // Report multiple `aggregated exports` from the same module (ES2015 modules) - flat(values(nodes.modules.sources) - .filter((nodesWithSource) => Array.isArray(nodesWithSource) && nodesWithSource.length > 1)) - .forEach((node) => { - context.report({ - node, - message: errors[node.type], - }); - }); + flat( + values(nodes.modules.sources).filter( + nodesWithSource => + Array.isArray(nodesWithSource) && nodesWithSource.length > 1, + ), + ).forEach(node => { + context.report({ + node, + message: errors[node.type], + }) + }) // Report multiple `export type` declarations (FLOW ES2015 modules) if (nodes.types.set.size > 1) { - nodes.types.set.forEach((node) => { + nodes.types.set.forEach(node => { context.report({ node, message: errors[node.type], - }); - }); + }) + }) } // Report multiple `aggregated type exports` from the same module (FLOW ES2015 modules) - flat(values(nodes.types.sources) - .filter((nodesWithSource) => Array.isArray(nodesWithSource) && nodesWithSource.length > 1)) - .forEach((node) => { - context.report({ - node, - message: errors[node.type], - }); - }); + flat( + values(nodes.types.sources).filter( + nodesWithSource => + Array.isArray(nodesWithSource) && nodesWithSource.length > 1, + ), + ).forEach(node => { + context.report({ + node, + message: errors[node.type], + }) + }) // Report multiple `module.exports` assignments (CommonJS) if (nodes.commonjs.set.size > 1) { - nodes.commonjs.set.forEach((node) => { + nodes.commonjs.set.forEach(node => { context.report({ node, message: errors[node.type], - }); - }); + }) + }) } }, - }; + } } module.exports = { meta, create, -}; +} diff --git a/src/rules/imports-first.js b/src/rules/imports-first.js index 966367e99..4220c3143 100644 --- a/src/rules/imports-first.js +++ b/src/rules/imports-first.js @@ -1,15 +1,15 @@ -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' -const first = require('./first'); +const first = require('./first') const newMeta = { ...first.meta, deprecated: true, docs: { category: 'Style guide', - description: 'Replaced by `import/first`.', + description: 'Replaced by `import-x/first`.', url: docsUrl('imports-first', '7b25c1cb95ee18acc1531002fd343e1e6031f9ed'), }, -}; +} -module.exports = { ...first, meta: newMeta }; +module.exports = { ...first, meta: newMeta } diff --git a/src/rules/max-dependencies.js b/src/rules/max-dependencies.js index 488e90618..301f2eeb3 100644 --- a/src/rules/max-dependencies.js +++ b/src/rules/max-dependencies.js @@ -1,24 +1,28 @@ -import moduleVisitor from 'eslint-module-utils/moduleVisitor'; -import docsUrl from '../docsUrl'; +import moduleVisitor from '../utils/moduleVisitor' +import docsUrl from '../docsUrl' -const DEFAULT_MAX = 10; -const DEFAULT_IGNORE_TYPE_IMPORTS = false; -const TYPE_IMPORT = 'type'; +const DEFAULT_MAX = 10 +const DEFAULT_IGNORE_TYPE_IMPORTS = false +const TYPE_IMPORT = 'type' const countDependencies = (dependencies, lastNode, context) => { - const { max } = context.options[0] || { max: DEFAULT_MAX }; + const { max } = context.options[0] || { max: DEFAULT_MAX } if (dependencies.size > max) { - context.report(lastNode, `Maximum number of dependencies (${max}) exceeded.`); + context.report( + lastNode, + `Maximum number of dependencies (${max}) exceeded.`, + ) } -}; +} module.exports = { meta: { type: 'suggestion', docs: { category: 'Style guide', - description: 'Enforce the maximum number of dependencies a module can have.', + description: + 'Enforce the maximum number of dependencies a module can have.', url: docsUrl('max-dependencies'), }, @@ -35,26 +39,25 @@ module.exports = { }, create(context) { - const { - ignoreTypeImports = DEFAULT_IGNORE_TYPE_IMPORTS, - } = context.options[0] || {}; + const { ignoreTypeImports = DEFAULT_IGNORE_TYPE_IMPORTS } = + context.options[0] || {} - const dependencies = new Set(); // keep track of dependencies - let lastNode; // keep track of the last node to report on + const dependencies = new Set() // keep track of dependencies + let lastNode // keep track of the last node to report on return { 'Program:exit'() { - countDependencies(dependencies, lastNode, context); + countDependencies(dependencies, lastNode, context) }, ...moduleVisitor( (source, { importKind }) => { if (importKind !== TYPE_IMPORT || !ignoreTypeImports) { - dependencies.add(source.value); + dependencies.add(source.value) } - lastNode = source; + lastNode = source }, { commonjs: true }, ), - }; + } }, -}; +} diff --git a/src/rules/named.js b/src/rules/named.js index e7fe4e4dc..c3083a8bb 100644 --- a/src/rules/named.js +++ b/src/rules/named.js @@ -1,13 +1,14 @@ -import * as path from 'path'; -import Exports from '../ExportMap'; -import docsUrl from '../docsUrl'; +import * as path from 'path' +import Exports from '../ExportMap' +import docsUrl from '../docsUrl' module.exports = { meta: { type: 'problem', docs: { category: 'Static analysis', - description: 'Ensure named imports correspond to a named export in the remote file.', + description: + 'Ensure named imports correspond to a named export in the remote file.', url: docsUrl('named'), }, schema: [ @@ -24,120 +25,151 @@ module.exports = { }, create(context) { - const options = context.options[0] || {}; + const options = context.options[0] || {} function checkSpecifiers(key, type, node) { // ignore local exports and type imports/exports if ( - node.source == null - || node.importKind === 'type' - || node.importKind === 'typeof' - || node.exportKind === 'type' + node.source == null || + node.importKind === 'type' || + node.importKind === 'typeof' || + node.exportKind === 'type' ) { - return; + return } - if (!node.specifiers.some((im) => im.type === type)) { - return; // no named imports/exports + if (!node.specifiers.some(im => im.type === type)) { + return // no named imports/exports } - const imports = Exports.get(node.source.value, context); + const imports = Exports.get(node.source.value, context) if (imports == null || imports.parseGoal === 'ambiguous') { - return; + return } if (imports.errors.length) { - imports.reportErrors(context, node); - return; + imports.reportErrors(context, node) + return } node.specifiers.forEach(function (im) { if ( - im.type !== type + im.type !== type || // ignore type imports - || im.importKind === 'type' || im.importKind === 'typeof' + im.importKind === 'type' || + im.importKind === 'typeof' ) { - return; + return } - const name = im[key].name || im[key].value; + const name = im[key].name || im[key].value - const deepLookup = imports.hasDeep(name); + const deepLookup = imports.hasDeep(name) if (!deepLookup.found) { if (deepLookup.path.length > 1) { const deepPath = deepLookup.path - .map((i) => path.relative(path.dirname(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()), i.path)) - .join(' -> '); - - context.report(im[key], `${name} not found via ${deepPath}`); + .map(i => + path.relative( + path.dirname( + context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename(), + ), + i.path, + ), + ) + .join(' -> ') + + context.report(im[key], `${name} not found via ${deepPath}`) } else { - context.report(im[key], `${name} not found in '${node.source.value}'`); + context.report( + im[key], + `${name} not found in '${node.source.value}'`, + ) } } - }); + }) } function checkRequire(node) { if ( - !options.commonjs - || node.type !== 'VariableDeclarator' + !options.commonjs || + node.type !== 'VariableDeclarator' || // return if it's not an object destructure or it's an empty object destructure - || !node.id || node.id.type !== 'ObjectPattern' || node.id.properties.length === 0 + !node.id || + node.id.type !== 'ObjectPattern' || + node.id.properties.length === 0 || // return if there is no call expression on the right side - || !node.init || node.init.type !== 'CallExpression' + !node.init || + node.init.type !== 'CallExpression' ) { - return; + return } - const call = node.init; - const [source] = call.arguments; - const variableImports = node.id.properties; - const variableExports = Exports.get(source.value, context); + const call = node.init + const [source] = call.arguments + const variableImports = node.id.properties + const variableExports = Exports.get(source.value, context) if ( // return if it's not a commonjs require statement - call.callee.type !== 'Identifier' || call.callee.name !== 'require' || call.arguments.length !== 1 + call.callee.type !== 'Identifier' || + call.callee.name !== 'require' || + call.arguments.length !== 1 || // return if it's not a string source - || source.type !== 'Literal' - || variableExports == null - || variableExports.parseGoal === 'ambiguous' + source.type !== 'Literal' || + variableExports == null || + variableExports.parseGoal === 'ambiguous' ) { - return; + return } if (variableExports.errors.length) { - variableExports.reportErrors(context, node); - return; + variableExports.reportErrors(context, node) + return } variableImports.forEach(function (im) { if (im.type !== 'Property' || !im.key || im.key.type !== 'Identifier') { - return; + return } - const deepLookup = variableExports.hasDeep(im.key.name); + const deepLookup = variableExports.hasDeep(im.key.name) if (!deepLookup.found) { if (deepLookup.path.length > 1) { const deepPath = deepLookup.path - .map((i) => path.relative(path.dirname(context.getFilename()), i.path)) - .join(' -> '); + .map(i => + path.relative(path.dirname(context.getFilename()), i.path), + ) + .join(' -> ') - context.report(im.key, `${im.key.name} not found via ${deepPath}`); + context.report(im.key, `${im.key.name} not found via ${deepPath}`) } else { - context.report(im.key, `${im.key.name} not found in '${source.value}'`); + context.report( + im.key, + `${im.key.name} not found in '${source.value}'`, + ) } } - }); + }) } return { - ImportDeclaration: checkSpecifiers.bind(null, 'imported', 'ImportSpecifier'), - - ExportNamedDeclaration: checkSpecifiers.bind(null, 'local', 'ExportSpecifier'), + ImportDeclaration: checkSpecifiers.bind( + null, + 'imported', + 'ImportSpecifier', + ), + + ExportNamedDeclaration: checkSpecifiers.bind( + null, + 'local', + 'ExportSpecifier', + ), VariableDeclarator: checkRequire, - }; + } }, -}; +} diff --git a/src/rules/namespace.js b/src/rules/namespace.js index 77a3ea907..88c4289cf 100644 --- a/src/rules/namespace.js +++ b/src/rules/namespace.js @@ -1,45 +1,55 @@ -import declaredScope from 'eslint-module-utils/declaredScope'; -import Exports from '../ExportMap'; -import importDeclaration from '../importDeclaration'; -import docsUrl from '../docsUrl'; +import declaredScope from '../utils/declaredScope' +import Exports from '../ExportMap' +import importDeclaration from '../importDeclaration' +import docsUrl from '../docsUrl' function processBodyStatement(context, namespaces, declaration) { - if (declaration.type !== 'ImportDeclaration') { return; } + if (declaration.type !== 'ImportDeclaration') { + return + } - if (declaration.specifiers.length === 0) { return; } + if (declaration.specifiers.length === 0) { + return + } - const imports = Exports.get(declaration.source.value, context); - if (imports == null) { return null; } + const imports = Exports.get(declaration.source.value, context) + if (imports == null) { + return null + } if (imports.errors.length > 0) { - imports.reportErrors(context, declaration); - return; + imports.reportErrors(context, declaration) + return } - declaration.specifiers.forEach((specifier) => { + declaration.specifiers.forEach(specifier => { switch (specifier.type) { case 'ImportNamespaceSpecifier': if (!imports.size) { context.report( specifier, `No exported names found in module '${declaration.source.value}'.`, - ); + ) } - namespaces.set(specifier.local.name, imports); - break; + namespaces.set(specifier.local.name, imports) + break case 'ImportDefaultSpecifier': case 'ImportSpecifier': { const meta = imports.get( - // default to 'default' for default https://i.imgur.com/nj6qAWy.jpg - specifier.imported ? specifier.imported.name || specifier.imported.value : 'default', - ); - if (!meta || !meta.namespace) { break; } - namespaces.set(specifier.local.name, meta.namespace); - break; + // default to 'default' for default https://i.imgur.com/nj6qAWy.jpg + specifier.imported + ? specifier.imported.name || specifier.imported.value + : 'default', + ) + if (!meta || !meta.namespace) { + break + } + namespaces.set(specifier.local.name, meta.namespace) + break } default: } - }); + }) } module.exports = { @@ -47,7 +57,8 @@ module.exports = { type: 'problem', docs: { category: 'Static analysis', - description: 'Ensure imported namespaces contain dereferenced properties as they are dereferenced.', + description: + 'Ensure imported namespaces contain dereferenced properties as they are dereferenced.', url: docsUrl('namespace'), }, @@ -56,7 +67,8 @@ module.exports = { type: 'object', properties: { allowComputed: { - description: 'If `false`, will report computed (and thus, un-lintable) references to namespace members.', + description: + 'If `false`, will report computed (and thus, un-lintable) references to namespace members.', type: 'boolean', default: false, }, @@ -68,151 +80,181 @@ module.exports = { create: function namespaceRule(context) { // read options - const { - allowComputed = false, - } = context.options[0] || {}; + const { allowComputed = false } = context.options[0] || {} - const namespaces = new Map(); + const namespaces = new Map() function makeMessage(last, namepath) { - return `'${last.name}' not found in ${namepath.length > 1 ? 'deeply ' : ''}imported namespace '${namepath.join('.')}'.`; + return `'${last.name}' not found in ${namepath.length > 1 ? 'deeply ' : ''}imported namespace '${namepath.join('.')}'.` } return { // pick up all imports at body entry time, to properly respect hoisting Program({ body }) { - body.forEach((x) => { processBodyStatement(context, namespaces, x); }); + body.forEach(x => { + processBodyStatement(context, namespaces, x) + }) }, // same as above, but does not add names to local map ExportNamespaceSpecifier(namespace) { - const declaration = importDeclaration(context); + const declaration = importDeclaration(context) - const imports = Exports.get(declaration.source.value, context); - if (imports == null) { return null; } + const imports = Exports.get(declaration.source.value, context) + if (imports == null) { + return null + } if (imports.errors.length) { - imports.reportErrors(context, declaration); - return; + imports.reportErrors(context, declaration) + return } if (!imports.size) { context.report( namespace, `No exported names found in module '${declaration.source.value}'.`, - ); + ) } }, // todo: check for possible redefinition MemberExpression(dereference) { - if (dereference.object.type !== 'Identifier') { return; } - if (!namespaces.has(dereference.object.name)) { return; } - if (declaredScope(context, dereference.object.name) !== 'module') { return; } + if (dereference.object.type !== 'Identifier') { + return + } + if (!namespaces.has(dereference.object.name)) { + return + } + if (declaredScope(context, dereference.object.name) !== 'module') { + return + } - if (dereference.parent.type === 'AssignmentExpression' && dereference.parent.left === dereference) { + if ( + dereference.parent.type === 'AssignmentExpression' && + dereference.parent.left === dereference + ) { context.report( dereference.parent, `Assignment to member of namespace '${dereference.object.name}'.`, - ); + ) } // go deep - let namespace = namespaces.get(dereference.object.name); - const namepath = [dereference.object.name]; + let namespace = namespaces.get(dereference.object.name) + const namepath = [dereference.object.name] // while property is namespace and parent is member expression, keep validating - while (namespace instanceof Exports && dereference.type === 'MemberExpression') { + while ( + namespace instanceof Exports && + dereference.type === 'MemberExpression' + ) { if (dereference.computed) { if (!allowComputed) { context.report( dereference.property, `Unable to validate computed reference to imported namespace '${dereference.object.name}'.`, - ); + ) } - return; + return } if (!namespace.has(dereference.property.name)) { context.report( dereference.property, makeMessage(dereference.property, namepath), - ); - break; + ) + break } - const exported = namespace.get(dereference.property.name); - if (exported == null) { return; } + const exported = namespace.get(dereference.property.name) + if (exported == null) { + return + } // stash and pop - namepath.push(dereference.property.name); - namespace = exported.namespace; - dereference = dereference.parent; + namepath.push(dereference.property.name) + namespace = exported.namespace + dereference = dereference.parent } }, VariableDeclarator({ id, init }) { - if (init == null) { return; } - if (init.type !== 'Identifier') { return; } - if (!namespaces.has(init.name)) { return; } + if (init == null) { + return + } + if (init.type !== 'Identifier') { + return + } + if (!namespaces.has(init.name)) { + return + } // check for redefinition in intermediate scopes - if (declaredScope(context, init.name) !== 'module') { return; } + if (declaredScope(context, init.name) !== 'module') { + return + } // DFS traverse child namespaces function testKey(pattern, namespace, path = [init.name]) { - if (!(namespace instanceof Exports)) { return; } + if (!(namespace instanceof Exports)) { + return + } - if (pattern.type !== 'ObjectPattern') { return; } + if (pattern.type !== 'ObjectPattern') { + return + } for (const property of pattern.properties) { if ( - property.type === 'ExperimentalRestProperty' - || property.type === 'RestElement' - || !property.key + property.type === 'ExperimentalRestProperty' || + property.type === 'RestElement' || + !property.key ) { - continue; + continue } if (property.key.type !== 'Identifier') { context.report({ node: property, message: 'Only destructure top-level names.', - }); - continue; + }) + continue } if (!namespace.has(property.key.name)) { context.report({ node: property, message: makeMessage(property.key, path), - }); - continue; + }) + continue } - path.push(property.key.name); - const dependencyExportMap = namespace.get(property.key.name); + path.push(property.key.name) + const dependencyExportMap = namespace.get(property.key.name) // could be null when ignored or ambiguous if (dependencyExportMap !== null) { - testKey(property.value, dependencyExportMap.namespace, path); + testKey(property.value, dependencyExportMap.namespace, path) } - path.pop(); + path.pop() } } - testKey(id, namespaces.get(init.name)); + testKey(id, namespaces.get(init.name)) }, JSXMemberExpression({ object, property }) { - if (!namespaces.has(object.name)) { return; } - const namespace = namespaces.get(object.name); + if (!namespaces.has(object.name)) { + return + } + const namespace = namespaces.get(object.name) if (!namespace.has(property.name)) { context.report({ node: property, message: makeMessage(property, [object.name]), - }); + }) } }, - }; + } }, -}; +} diff --git a/src/rules/newline-after-import.js b/src/rules/newline-after-import.js index a33bb615b..4f0bed0f8 100644 --- a/src/rules/newline-after-import.js +++ b/src/rules/newline-after-import.js @@ -3,53 +3,66 @@ * @author Radek Benkel */ -import isStaticRequire from '../core/staticRequire'; -import docsUrl from '../docsUrl'; +import isStaticRequire from '../core/staticRequire' +import docsUrl from '../docsUrl' -import debug from 'debug'; -const log = debug('eslint-plugin-import:rules:newline-after-import'); +import debug from 'debug' +const log = debug('eslint-plugin-import-x:rules:newline-after-import') //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ function containsNodeOrEqual(outerNode, innerNode) { - return outerNode.range[0] <= innerNode.range[0] && outerNode.range[1] >= innerNode.range[1]; + return ( + outerNode.range[0] <= innerNode.range[0] && + outerNode.range[1] >= innerNode.range[1] + ) } function getScopeBody(scope) { if (scope.block.type === 'SwitchStatement') { - log('SwitchStatement scopes not supported'); - return null; + log('SwitchStatement scopes not supported') + return null } - const { body } = scope.block; + const { body } = scope.block if (body && body.type === 'BlockStatement') { - return body.body; + return body.body } - return body; + return body } function findNodeIndexInScopeBody(body, nodeToFind) { - return body.findIndex((node) => containsNodeOrEqual(node, nodeToFind)); + return body.findIndex(node => containsNodeOrEqual(node, nodeToFind)) } function getLineDifference(node, nextNode) { - return nextNode.loc.start.line - node.loc.end.line; + return nextNode.loc.start.line - node.loc.end.line } function isClassWithDecorator(node) { - return node.type === 'ClassDeclaration' && node.decorators && node.decorators.length; + return ( + node.type === 'ClassDeclaration' && + node.decorators && + node.decorators.length + ) } function isExportDefaultClass(node) { - return node.type === 'ExportDefaultDeclaration' && node.declaration.type === 'ClassDeclaration'; + return ( + node.type === 'ExportDefaultDeclaration' && + node.declaration.type === 'ClassDeclaration' + ) } function isExportNameClass(node) { - - return node.type === 'ExportNamedDeclaration' && node.declaration && node.declaration.type === 'ClassDeclaration'; + return ( + node.type === 'ExportNamedDeclaration' && + node.declaration && + node.declaration.type === 'ClassDeclaration' + ) } module.exports = { @@ -77,37 +90,37 @@ module.exports = { ], }, create(context) { - let level = 0; - const requireCalls = []; + let level = 0 + const requireCalls = [] const options = { count: 1, exactCount: false, considerComments: false, ...context.options[0], - }; + } function checkForNewLine(node, nextNode, type) { if (isExportDefaultClass(nextNode) || isExportNameClass(nextNode)) { - const classNode = nextNode.declaration; + const classNode = nextNode.declaration if (isClassWithDecorator(classNode)) { - nextNode = classNode.decorators[0]; + nextNode = classNode.decorators[0] } } else if (isClassWithDecorator(nextNode)) { - nextNode = nextNode.decorators[0]; + nextNode = nextNode.decorators[0] } - const lineDifference = getLineDifference(node, nextNode); - const EXPECTED_LINE_DIFFERENCE = options.count + 1; + const lineDifference = getLineDifference(node, nextNode) + const EXPECTED_LINE_DIFFERENCE = options.count + 1 if ( - lineDifference < EXPECTED_LINE_DIFFERENCE - || options.exactCount && lineDifference !== EXPECTED_LINE_DIFFERENCE + lineDifference < EXPECTED_LINE_DIFFERENCE || + (options.exactCount && lineDifference !== EXPECTED_LINE_DIFFERENCE) ) { - let column = node.loc.start.column; + let column = node.loc.start.column if (node.loc.start.line !== node.loc.end.line) { - column = 0; + column = 0 } context.report({ @@ -116,23 +129,27 @@ module.exports = { column, }, message: `Expected ${options.count} empty line${options.count > 1 ? 's' : ''} after ${type} statement not followed by another ${type}.`, - fix: options.exactCount && EXPECTED_LINE_DIFFERENCE < lineDifference ? undefined : (fixer) => fixer.insertTextAfter( - node, - '\n'.repeat(EXPECTED_LINE_DIFFERENCE - lineDifference), - ), - }); + fix: + options.exactCount && EXPECTED_LINE_DIFFERENCE < lineDifference + ? undefined + : fixer => + fixer.insertTextAfter( + node, + '\n'.repeat(EXPECTED_LINE_DIFFERENCE - lineDifference), + ), + }) } } function commentAfterImport(node, nextComment) { - const lineDifference = getLineDifference(node, nextComment); - const EXPECTED_LINE_DIFFERENCE = options.count + 1; + const lineDifference = getLineDifference(node, nextComment) + const EXPECTED_LINE_DIFFERENCE = options.count + 1 if (lineDifference < EXPECTED_LINE_DIFFERENCE) { - let column = node.loc.start.column; + let column = node.loc.start.column if (node.loc.start.line !== node.loc.end.line) { - column = 0; + column = 0 } context.report({ @@ -141,46 +158,58 @@ module.exports = { column, }, message: `Expected ${options.count} empty line${options.count > 1 ? 's' : ''} after import statement not followed by another import.`, - fix: options.exactCount && EXPECTED_LINE_DIFFERENCE < lineDifference ? undefined : (fixer) => fixer.insertTextAfter( - node, - '\n'.repeat(EXPECTED_LINE_DIFFERENCE - lineDifference), - ), - }); + fix: + options.exactCount && EXPECTED_LINE_DIFFERENCE < lineDifference + ? undefined + : fixer => + fixer.insertTextAfter( + node, + '\n'.repeat(EXPECTED_LINE_DIFFERENCE - lineDifference), + ), + }) } } function incrementLevel() { - level++; + level++ } function decrementLevel() { - level--; + level-- } function checkImport(node) { - const { parent } = node; + const { parent } = node if (!parent || !parent.body) { - return; + return } - const nodePosition = parent.body.indexOf(node); - const nextNode = parent.body[nodePosition + 1]; - const endLine = node.loc.end.line; - let nextComment; + const nodePosition = parent.body.indexOf(node) + const nextNode = parent.body[nodePosition + 1] + const endLine = node.loc.end.line + let nextComment if (typeof parent.comments !== 'undefined' && options.considerComments) { - nextComment = parent.comments.find((o) => o.loc.start.line >= endLine && o.loc.start.line <= endLine + options.count + 1); + nextComment = parent.comments.find( + o => + o.loc.start.line >= endLine && + o.loc.start.line <= endLine + options.count + 1, + ) } // skip "export import"s if (node.type === 'TSImportEqualsDeclaration' && node.isExport) { - return; + return } if (nextComment && typeof nextComment !== 'undefined') { - commentAfterImport(node, nextComment); - } else if (nextNode && nextNode.type !== 'ImportDeclaration' && (nextNode.type !== 'TSImportEqualsDeclaration' || nextNode.isExport)) { - checkForNewLine(node, nextNode, 'import'); + commentAfterImport(node, nextComment) + } else if ( + nextNode && + nextNode.type !== 'ImportDeclaration' && + (nextNode.type !== 'TSImportEqualsDeclaration' || nextNode.isExport) + ) { + checkForNewLine(node, nextNode, 'import') } } @@ -189,36 +218,42 @@ module.exports = { TSImportEqualsDeclaration: checkImport, CallExpression(node) { if (isStaticRequire(node) && level === 0) { - requireCalls.push(node); + requireCalls.push(node) } }, 'Program:exit'() { - log('exit processing for', context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()); - const scopeBody = getScopeBody(context.getScope()); - log('got scope:', scopeBody); + log( + 'exit processing for', + context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename(), + ) + const scopeBody = getScopeBody(context.getScope()) + log('got scope:', scopeBody) requireCalls.forEach((node, index) => { - const nodePosition = findNodeIndexInScopeBody(scopeBody, node); - log('node position in scope:', nodePosition); + const nodePosition = findNodeIndexInScopeBody(scopeBody, node) + log('node position in scope:', nodePosition) - const statementWithRequireCall = scopeBody[nodePosition]; - const nextStatement = scopeBody[nodePosition + 1]; - const nextRequireCall = requireCalls[index + 1]; + const statementWithRequireCall = scopeBody[nodePosition] + const nextStatement = scopeBody[nodePosition + 1] + const nextRequireCall = requireCalls[index + 1] - if (nextRequireCall && containsNodeOrEqual(statementWithRequireCall, nextRequireCall)) { - return; + if ( + nextRequireCall && + containsNodeOrEqual(statementWithRequireCall, nextRequireCall) + ) { + return } if ( - nextStatement && ( - !nextRequireCall - || !containsNodeOrEqual(nextStatement, nextRequireCall) - ) + nextStatement && + (!nextRequireCall || + !containsNodeOrEqual(nextStatement, nextRequireCall)) ) { - - checkForNewLine(statementWithRequireCall, nextStatement, 'require'); + checkForNewLine(statementWithRequireCall, nextStatement, 'require') } - }); + }) }, FunctionDeclaration: incrementLevel, FunctionExpression: incrementLevel, @@ -232,6 +267,6 @@ module.exports = { 'BlockStatement:exit': decrementLevel, 'ObjectExpression:exit': decrementLevel, 'Decorator:exit': decrementLevel, - }; + } }, -}; +} diff --git a/src/rules/no-absolute-path.js b/src/rules/no-absolute-path.js index 04f67383f..5781463c0 100644 --- a/src/rules/no-absolute-path.js +++ b/src/rules/no-absolute-path.js @@ -1,7 +1,7 @@ -import path from 'path'; -import moduleVisitor, { makeOptionsSchema } from 'eslint-module-utils/moduleVisitor'; -import { isAbsolute } from '../core/importType'; -import docsUrl from '../docsUrl'; +import path from 'path' +import moduleVisitor, { makeOptionsSchema } from '../utils/moduleVisitor' +import { isAbsolute } from '../core/importType' +import docsUrl from '../docsUrl' module.exports = { meta: { @@ -22,19 +22,24 @@ module.exports = { node: source, message: 'Do not import modules using an absolute path', fix(fixer) { - const resolvedContext = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); + const resolvedContext = context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename() // node.js and web imports work with posix style paths ("/") - let relativePath = path.posix.relative(path.dirname(resolvedContext), source.value); + let relativePath = path.posix.relative( + path.dirname(resolvedContext), + source.value, + ) if (!relativePath.startsWith('.')) { - relativePath = `./${relativePath}`; + relativePath = `./${relativePath}` } - return fixer.replaceText(source, JSON.stringify(relativePath)); + return fixer.replaceText(source, JSON.stringify(relativePath)) }, - }); + }) } } - const options = { esmodule: true, commonjs: true, ...context.options[0] }; - return moduleVisitor(reportIfAbsolute, options); + const options = { esmodule: true, commonjs: true, ...context.options[0] } + return moduleVisitor(reportIfAbsolute, options) }, -}; +} diff --git a/src/rules/no-amd.js b/src/rules/no-amd.js index 5edfe3e69..21199a57a 100644 --- a/src/rules/no-amd.js +++ b/src/rules/no-amd.js @@ -3,7 +3,7 @@ * @author Jamund Ferguson */ -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' //------------------------------------------------------------------------------ // Rule Definition @@ -23,22 +23,34 @@ module.exports = { create(context) { return { CallExpression(node) { - if (context.getScope().type !== 'module') { return; } + if (context.getScope().type !== 'module') { + return + } - if (node.callee.type !== 'Identifier') { return; } - if (node.callee.name !== 'require' && node.callee.name !== 'define') { return; } + if (node.callee.type !== 'Identifier') { + return + } + if (node.callee.name !== 'require' && node.callee.name !== 'define') { + return + } // todo: capture define((require, module, exports) => {}) form? - if (node.arguments.length !== 2) { return; } + if (node.arguments.length !== 2) { + return + } - const modules = node.arguments[0]; - if (modules.type !== 'ArrayExpression') { return; } + const modules = node.arguments[0] + if (modules.type !== 'ArrayExpression') { + return + } // todo: check second arg type? (identifier or callback) - context.report(node, `Expected imports instead of AMD ${node.callee.name}().`); + context.report( + node, + `Expected imports instead of AMD ${node.callee.name}().`, + ) }, - }; - + } }, -}; +} diff --git a/src/rules/no-anonymous-default-export.js b/src/rules/no-anonymous-default-export.js index 4f6947e81..1618f6d4c 100644 --- a/src/rules/no-anonymous-default-export.js +++ b/src/rules/no-anonymous-default-export.js @@ -3,11 +3,11 @@ * @author Duncan Beevers */ -import hasOwn from 'hasown'; -import values from 'object.values'; -import fromEntries from 'object.fromentries'; +import hasOwn from 'hasown' +import values from 'object.values' +import fromEntries from 'object.fromentries' -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' const defs = { ArrayExpression: { @@ -18,25 +18,28 @@ const defs = { ArrowFunctionExpression: { option: 'allowArrowFunction', description: 'If `false`, will report default export of an arrow function', - message: 'Assign arrow function to a variable before exporting as module default', + message: + 'Assign arrow function to a variable before exporting as module default', }, CallExpression: { option: 'allowCallExpression', description: 'If `false`, will report default export of a function call', - message: 'Assign call result to a variable before exporting as module default', + message: + 'Assign call result to a variable before exporting as module default', default: true, }, ClassDeclaration: { option: 'allowAnonymousClass', description: 'If `false`, will report default export of an anonymous class', message: 'Unexpected default export of anonymous class', - forbid: (node) => !node.declaration.id, + forbid: node => !node.declaration.id, }, FunctionDeclaration: { option: 'allowAnonymousFunction', - description: 'If `false`, will report default export of an anonymous function', + description: + 'If `false`, will report default export of an anonymous function', message: 'Unexpected default export of anonymous function', - forbid: (node) => !node.declaration.id, + forbid: node => !node.declaration.id, }, Literal: { option: 'allowLiteral', @@ -45,7 +48,8 @@ const defs = { }, ObjectExpression: { option: 'allowObject', - description: 'If `false`, will report default export of an object expression', + description: + 'If `false`, will report default export of an object expression', message: 'Assign object to a variable before exporting as module default', }, TemplateLiteral: { @@ -55,17 +59,28 @@ const defs = { }, NewExpression: { option: 'allowNew', - description: 'If `false`, will report default export of a class instantiation', + description: + 'If `false`, will report default export of a class instantiation', message: 'Assign instance to a variable before exporting as module default', }, -}; +} -const schemaProperties = fromEntries(values(defs).map((def) => [def.option, { - description: def.description, - type: 'boolean', -}])); +const schemaProperties = fromEntries( + values(defs).map(def => [ + def.option, + { + description: def.description, + type: 'boolean', + }, + ]), +) -const defaults = fromEntries(values(defs).map((def) => [def.option, hasOwn(def, 'default') ? def.default : false])); +const defaults = fromEntries( + values(defs).map(def => [ + def.option, + hasOwn(def, 'default') ? def.default : false, + ]), +) module.exports = { meta: { @@ -86,18 +101,18 @@ module.exports = { }, create(context) { - const options = { ...defaults, ...context.options[0] }; + const options = { ...defaults, ...context.options[0] } return { ExportDefaultDeclaration(node) { - const def = defs[node.declaration.type]; + const def = defs[node.declaration.type] // Recognized node type and allowed by configuration, // and has no forbid check, or forbid check return value is truthy if (def && !options[def.option] && (!def.forbid || def.forbid(node))) { - context.report({ node, message: def.message }); + context.report({ node, message: def.message }) } }, - }; + } }, -}; +} diff --git a/src/rules/no-commonjs.js b/src/rules/no-commonjs.js index dde509222..4f4b2df6e 100644 --- a/src/rules/no-commonjs.js +++ b/src/rules/no-commonjs.js @@ -3,60 +3,68 @@ * @author Jamund Ferguson */ -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' -const EXPORT_MESSAGE = 'Expected "export" or "export default"'; -const IMPORT_MESSAGE = 'Expected "import" instead of "require()"'; +const EXPORT_MESSAGE = 'Expected "export" or "export default"' +const IMPORT_MESSAGE = 'Expected "import" instead of "require()"' function normalizeLegacyOptions(options) { if (options.indexOf('allow-primitive-modules') >= 0) { - return { allowPrimitiveModules: true }; + return { allowPrimitiveModules: true } } - return options[0] || {}; + return options[0] || {} } function allowPrimitive(node, options) { - if (!options.allowPrimitiveModules) { return false; } - if (node.parent.type !== 'AssignmentExpression') { return false; } - return node.parent.right.type !== 'ObjectExpression'; + if (!options.allowPrimitiveModules) { + return false + } + if (node.parent.type !== 'AssignmentExpression') { + return false + } + return node.parent.right.type !== 'ObjectExpression' } function allowRequire(node, options) { - return options.allowRequire; + return options.allowRequire } function allowConditionalRequire(node, options) { - return options.allowConditionalRequire !== false; + return options.allowConditionalRequire !== false } function validateScope(scope) { - return scope.variableScope.type === 'module'; + return scope.variableScope.type === 'module' } // https://github.com/estree/estree/blob/HEAD/es5.md function isConditional(node) { if ( - node.type === 'IfStatement' - || node.type === 'TryStatement' - || node.type === 'LogicalExpression' - || node.type === 'ConditionalExpression' + node.type === 'IfStatement' || + node.type === 'TryStatement' || + node.type === 'LogicalExpression' || + node.type === 'ConditionalExpression' ) { - return true; + return true + } + if (node.parent) { + return isConditional(node.parent) } - if (node.parent) { return isConditional(node.parent); } - return false; + return false } function isLiteralString(node) { - return node.type === 'Literal' && typeof node.value === 'string' - || node.type === 'TemplateLiteral' && node.expressions.length === 0; + return ( + (node.type === 'Literal' && typeof node.value === 'string') || + (node.type === 'TemplateLiteral' && node.expressions.length === 0) + ) } //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ -const schemaString = { enum: ['allow-primitive-modules'] }; +const schemaString = { enum: ['allow-primitive-modules'] } const schemaObject = { type: 'object', properties: { @@ -65,14 +73,15 @@ const schemaObject = { allowConditionalRequire: { type: 'boolean' }, }, additionalProperties: false, -}; +} module.exports = { meta: { type: 'suggestion', docs: { category: 'Module systems', - description: 'Forbid CommonJS `require` calls and `module.exports` or `exports.*`.', + description: + 'Forbid CommonJS `require` calls and `module.exports` or `exports.*`.', url: docsUrl('no-commonjs'), }, @@ -93,49 +102,64 @@ module.exports = { }, create(context) { - const options = normalizeLegacyOptions(context.options); + const options = normalizeLegacyOptions(context.options) return { - MemberExpression(node) { - // module.exports if (node.object.name === 'module' && node.property.name === 'exports') { - if (allowPrimitive(node, options)) { return; } - context.report({ node, message: EXPORT_MESSAGE }); + if (allowPrimitive(node, options)) { + return + } + context.report({ node, message: EXPORT_MESSAGE }) } // exports. if (node.object.name === 'exports') { - const isInScope = context.getScope() - .variables - .some((variable) => variable.name === 'exports'); + const isInScope = context + .getScope() + .variables.some(variable => variable.name === 'exports') if (!isInScope) { - context.report({ node, message: EXPORT_MESSAGE }); + context.report({ node, message: EXPORT_MESSAGE }) } } - }, CallExpression(call) { - if (!validateScope(context.getScope())) { return; } + if (!validateScope(context.getScope())) { + return + } - if (call.callee.type !== 'Identifier') { return; } - if (call.callee.name !== 'require') { return; } + if (call.callee.type !== 'Identifier') { + return + } + if (call.callee.name !== 'require') { + return + } - if (call.arguments.length !== 1) { return; } - if (!isLiteralString(call.arguments[0])) { return; } + if (call.arguments.length !== 1) { + return + } + if (!isLiteralString(call.arguments[0])) { + return + } - if (allowRequire(call, options)) { return; } + if (allowRequire(call, options)) { + return + } - if (allowConditionalRequire(call, options) && isConditional(call.parent)) { return; } + if ( + allowConditionalRequire(call, options) && + isConditional(call.parent) + ) { + return + } // keeping it simple: all 1-string-arg `require` calls are reported context.report({ node: call.callee, message: IMPORT_MESSAGE, - }); + }) }, - }; - + } }, -}; +} diff --git a/src/rules/no-cycle.js b/src/rules/no-cycle.js index 5b9d8c070..73b1d446d 100644 --- a/src/rules/no-cycle.js +++ b/src/rules/no-cycle.js @@ -3,115 +3,134 @@ * @author Ben Mosher */ -import resolve from 'eslint-module-utils/resolve'; -import Exports from '../ExportMap'; -import { isExternalModule } from '../core/importType'; -import moduleVisitor, { makeOptionsSchema } from 'eslint-module-utils/moduleVisitor'; -import docsUrl from '../docsUrl'; +import resolve from '../utils/resolve' +import Exports from '../ExportMap' +import { isExternalModule } from '../core/importType' +import moduleVisitor, { makeOptionsSchema } from '../utils/moduleVisitor' +import docsUrl from '../docsUrl' -const traversed = new Set(); +const traversed = new Set() module.exports = { meta: { type: 'suggestion', docs: { category: 'Static analysis', - description: 'Forbid a module from importing a module with a dependency path back to itself.', + description: + 'Forbid a module from importing a module with a dependency path back to itself.', url: docsUrl('no-cycle'), }, - schema: [makeOptionsSchema({ - maxDepth: { - anyOf: [ - { - description: 'maximum dependency depth to traverse', - type: 'integer', - minimum: 1, - }, - { - enum: ['∞'], - type: 'string', - }, - ], - }, - ignoreExternal: { - description: 'ignore external modules', - type: 'boolean', - default: false, - }, - allowUnsafeDynamicCyclicDependency: { - description: 'Allow cyclic dependency if there is at least one dynamic import in the chain', - type: 'boolean', - default: false, - }, - })], + schema: [ + makeOptionsSchema({ + maxDepth: { + anyOf: [ + { + description: 'maximum dependency depth to traverse', + type: 'integer', + minimum: 1, + }, + { + enum: ['∞'], + type: 'string', + }, + ], + }, + ignoreExternal: { + description: 'ignore external modules', + type: 'boolean', + default: false, + }, + allowUnsafeDynamicCyclicDependency: { + description: + 'Allow cyclic dependency if there is at least one dynamic import in the chain', + type: 'boolean', + default: false, + }, + }), + ], }, create(context) { - const myPath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); - if (myPath === '') { return {}; } // can't cycle-check a non-file - - const options = context.options[0] || {}; - const maxDepth = typeof options.maxDepth === 'number' ? options.maxDepth : Infinity; - const ignoreModule = (name) => options.ignoreExternal && isExternalModule( - name, - resolve(name, context), - context, - ); + const myPath = context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename() + if (myPath === '') { + return {} + } // can't cycle-check a non-file + + const options = context.options[0] || {} + const maxDepth = + typeof options.maxDepth === 'number' ? options.maxDepth : Infinity + const ignoreModule = name => + options.ignoreExternal && + isExternalModule(name, resolve(name, context), context) function checkSourceValue(sourceNode, importer) { if (ignoreModule(sourceNode.value)) { - return; // ignore external modules + return // ignore external modules } if ( - options.allowUnsafeDynamicCyclicDependency && ( - // Ignore `import()` - importer.type === 'ImportExpression' + options.allowUnsafeDynamicCyclicDependency && + // Ignore `import()` + (importer.type === 'ImportExpression' || // `require()` calls are always checked (if possible) - || importer.type === 'CallExpression' && importer.callee.name !== 'require' - ) + (importer.type === 'CallExpression' && + importer.callee.name !== 'require')) ) { - return; // cycle via dynamic import allowed by config + return // cycle via dynamic import allowed by config } if ( - importer.type === 'ImportDeclaration' && ( - // import type { Foo } (TS and Flow) - importer.importKind === 'type' + importer.type === 'ImportDeclaration' && + // import type { Foo } (TS and Flow) + (importer.importKind === 'type' || // import { type Foo } (Flow) - || importer.specifiers.every(({ importKind }) => importKind === 'type') - ) + importer.specifiers.every(({ importKind }) => importKind === 'type')) ) { - return; // ignore type imports + return // ignore type imports } - const imported = Exports.get(sourceNode.value, context); + const imported = Exports.get(sourceNode.value, context) if (imported == null) { - return; // no-unresolved territory + return // no-unresolved territory } if (imported.path === myPath) { - return; // no-self-import territory + return // no-self-import territory } - const untraversed = [{ mget: () => imported, route: [] }]; + const untraversed = [{ mget: () => imported, route: [] }] function detectCycle({ mget, route }) { - const m = mget(); - if (m == null) { return; } - if (traversed.has(m.path)) { return; } - traversed.add(m.path); + const m = mget() + if (m == null) { + return + } + if (traversed.has(m.path)) { + return + } + traversed.add(m.path) for (const [path, { getter, declarations }] of m.imports) { - if (traversed.has(path)) { continue; } - const toTraverse = [...declarations].filter(({ source, isOnlyImportingTypes }) => !ignoreModule(source.value) - // Ignore only type imports - && !isOnlyImportingTypes, - ); + if (traversed.has(path)) { + continue + } + const toTraverse = [...declarations].filter( + ({ source, isOnlyImportingTypes }) => + !ignoreModule(source.value) && + // Ignore only type imports + !isOnlyImportingTypes, + ) /* If cyclic dependency is allowed via dynamic import, skip checking if any module is imported dynamically */ - if (options.allowUnsafeDynamicCyclicDependency && toTraverse.some((d) => d.dynamic)) { return; } + if ( + options.allowUnsafeDynamicCyclicDependency && + toTraverse.some(d => d.dynamic) + ) { + return + } /* Only report as a cycle if there are any import declarations that are considered by @@ -123,35 +142,38 @@ module.exports = { b.ts: import type { Bar } from './a' */ - if (path === myPath && toTraverse.length > 0) { return true; } + if (path === myPath && toTraverse.length > 0) { + return true + } if (route.length + 1 < maxDepth) { for (const { source } of toTraverse) { - untraversed.push({ mget: getter, route: route.concat(source) }); + untraversed.push({ mget: getter, route: route.concat(source) }) } } } } while (untraversed.length > 0) { - const next = untraversed.shift(); // bfs! + const next = untraversed.shift() // bfs! if (detectCycle(next)) { - const message = next.route.length > 0 - ? `Dependency cycle via ${routeString(next.route)}` - : 'Dependency cycle detected.'; - context.report(importer, message); - return; + const message = + next.route.length > 0 + ? `Dependency cycle via ${routeString(next.route)}` + : 'Dependency cycle detected.' + context.report(importer, message) + return } } } return Object.assign(moduleVisitor(checkSourceValue, context.options[0]), { 'Program:exit'() { - traversed.clear(); + traversed.clear() }, - }); + }) }, -}; +} function routeString(route) { - return route.map((s) => `${s.value}:${s.loc.start.line}`).join('=>'); + return route.map(s => `${s.value}:${s.loc.start.line}`).join('=>') } diff --git a/src/rules/no-default-export.js b/src/rules/no-default-export.js index dabbae543..8ca24153e 100644 --- a/src/rules/no-default-export.js +++ b/src/rules/no-default-export.js @@ -1,4 +1,4 @@ -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' module.exports = { meta: { @@ -14,30 +14,36 @@ module.exports = { create(context) { // ignore non-modules if (context.parserOptions.sourceType !== 'module') { - return {}; + return {} } - const preferNamed = 'Prefer named exports.'; - const noAliasDefault = ({ local }) => `Do not alias \`${local.name}\` as \`default\`. Just export \`${local.name}\` itself instead.`; + const preferNamed = 'Prefer named exports.' + const noAliasDefault = ({ local }) => + `Do not alias \`${local.name}\` as \`default\`. Just export \`${local.name}\` itself instead.` return { ExportDefaultDeclaration(node) { - const { loc } = context.getSourceCode().getFirstTokens(node)[1] || {}; - context.report({ node, message: preferNamed, loc }); + const { loc } = context.getSourceCode().getFirstTokens(node)[1] || {} + context.report({ node, message: preferNamed, loc }) }, ExportNamedDeclaration(node) { node.specifiers - .filter((specifier) => (specifier.exported.name || specifier.exported.value) === 'default') - .forEach((specifier) => { - const { loc } = context.getSourceCode().getFirstTokens(node)[1] || {}; + .filter( + specifier => + (specifier.exported.name || specifier.exported.value) === + 'default', + ) + .forEach(specifier => { + const { loc } = + context.getSourceCode().getFirstTokens(node)[1] || {} if (specifier.type === 'ExportDefaultSpecifier') { - context.report({ node, message: preferNamed, loc }); + context.report({ node, message: preferNamed, loc }) } else if (specifier.type === 'ExportSpecifier') { - context.report({ node, message: noAliasDefault(specifier), loc }); + context.report({ node, message: noAliasDefault(specifier), loc }) } - }); + }) }, - }; + } }, -}; +} diff --git a/src/rules/no-deprecated.js b/src/rules/no-deprecated.js index 06eeff8ea..eb49c05f0 100644 --- a/src/rules/no-deprecated.js +++ b/src/rules/no-deprecated.js @@ -1,15 +1,17 @@ -import declaredScope from 'eslint-module-utils/declaredScope'; -import Exports from '../ExportMap'; -import docsUrl from '../docsUrl'; +import declaredScope from '../utils/declaredScope' +import Exports from '../ExportMap' +import docsUrl from '../docsUrl' function message(deprecation) { - return `Deprecated${deprecation.description ? `: ${deprecation.description}` : '.'}`; + return `Deprecated${deprecation.description ? `: ${deprecation.description}` : '.'}` } function getDeprecation(metadata) { - if (!metadata || !metadata.doc) { return; } + if (!metadata || !metadata.doc) { + return + } - return metadata.doc.tags.find((t) => t.title === 'deprecated'); + return metadata.doc.tags.find(t => t.title === 'deprecated') } module.exports = { @@ -17,122 +19,163 @@ module.exports = { type: 'suggestion', docs: { category: 'Helpful warnings', - description: 'Forbid imported names marked with `@deprecated` documentation tag.', + description: + 'Forbid imported names marked with `@deprecated` documentation tag.', url: docsUrl('no-deprecated'), }, schema: [], }, create(context) { - const deprecated = new Map(); - const namespaces = new Map(); + const deprecated = new Map() + const namespaces = new Map() function checkSpecifiers(node) { - if (node.type !== 'ImportDeclaration') { return; } - if (node.source == null) { return; } // local export, ignore + if (node.type !== 'ImportDeclaration') { + return + } + if (node.source == null) { + return + } // local export, ignore - const imports = Exports.get(node.source.value, context); - if (imports == null) { return; } + const imports = Exports.get(node.source.value, context) + if (imports == null) { + return + } - const moduleDeprecation = imports.doc && imports.doc.tags.find((t) => t.title === 'deprecated'); + const moduleDeprecation = + imports.doc && imports.doc.tags.find(t => t.title === 'deprecated') if (moduleDeprecation) { - context.report({ node, message: message(moduleDeprecation) }); + context.report({ node, message: message(moduleDeprecation) }) } if (imports.errors.length) { - imports.reportErrors(context, node); - return; + imports.reportErrors(context, node) + return } node.specifiers.forEach(function (im) { - let imported; let local; + let imported + let local switch (im.type) { - case 'ImportNamespaceSpecifier': { - if (!imports.size) { return; } - namespaces.set(im.local.name, imports); - return; + if (!imports.size) { + return + } + namespaces.set(im.local.name, imports) + return } case 'ImportDefaultSpecifier': - imported = 'default'; - local = im.local.name; - break; + imported = 'default' + local = im.local.name + break case 'ImportSpecifier': - imported = im.imported.name; - local = im.local.name; - break; + imported = im.imported.name + local = im.local.name + break - default: return; // can't handle this one + default: + return // can't handle this one } // unknown thing can't be deprecated - const exported = imports.get(imported); - if (exported == null) { return; } + const exported = imports.get(imported) + if (exported == null) { + return + } // capture import of deep namespace - if (exported.namespace) { namespaces.set(local, exported.namespace); } - - const deprecation = getDeprecation(imports.get(imported)); - if (!deprecation) { return; } + if (exported.namespace) { + namespaces.set(local, exported.namespace) + } - context.report({ node: im, message: message(deprecation) }); + const deprecation = getDeprecation(imports.get(imported)) + if (!deprecation) { + return + } - deprecated.set(local, deprecation); + context.report({ node: im, message: message(deprecation) }) - }); + deprecated.set(local, deprecation) + }) } return { Program: ({ body }) => body.forEach(checkSpecifiers), Identifier(node) { - if (node.parent.type === 'MemberExpression' && node.parent.property === node) { - return; // handled by MemberExpression + if ( + node.parent.type === 'MemberExpression' && + node.parent.property === node + ) { + return // handled by MemberExpression } // ignore specifier identifiers - if (node.parent.type.slice(0, 6) === 'Import') { return; } + if (node.parent.type.slice(0, 6) === 'Import') { + return + } - if (!deprecated.has(node.name)) { return; } + if (!deprecated.has(node.name)) { + return + } - if (declaredScope(context, node.name) !== 'module') { return; } + if (declaredScope(context, node.name) !== 'module') { + return + } context.report({ node, message: message(deprecated.get(node.name)), - }); + }) }, MemberExpression(dereference) { - if (dereference.object.type !== 'Identifier') { return; } - if (!namespaces.has(dereference.object.name)) { return; } + if (dereference.object.type !== 'Identifier') { + return + } + if (!namespaces.has(dereference.object.name)) { + return + } - if (declaredScope(context, dereference.object.name) !== 'module') { return; } + if (declaredScope(context, dereference.object.name) !== 'module') { + return + } // go deep - let namespace = namespaces.get(dereference.object.name); - const namepath = [dereference.object.name]; + let namespace = namespaces.get(dereference.object.name) + const namepath = [dereference.object.name] // while property is namespace and parent is member expression, keep validating - while (namespace instanceof Exports && dereference.type === 'MemberExpression') { + while ( + namespace instanceof Exports && + dereference.type === 'MemberExpression' + ) { // ignore computed parts for now - if (dereference.computed) { return; } + if (dereference.computed) { + return + } - const metadata = namespace.get(dereference.property.name); + const metadata = namespace.get(dereference.property.name) - if (!metadata) { break; } - const deprecation = getDeprecation(metadata); + if (!metadata) { + break + } + const deprecation = getDeprecation(metadata) if (deprecation) { - context.report({ node: dereference.property, message: message(deprecation) }); + context.report({ + node: dereference.property, + message: message(deprecation), + }) } // stash and pop - namepath.push(dereference.property.name); - namespace = metadata.namespace; - dereference = dereference.parent; + namepath.push(dereference.property.name) + namespace = metadata.namespace + dereference = dereference.parent } }, - }; + } }, -}; +} diff --git a/src/rules/no-duplicates.js b/src/rules/no-duplicates.js index 6b4f4d559..168922e52 100644 --- a/src/rules/no-duplicates.js +++ b/src/rules/no-duplicates.js @@ -1,33 +1,35 @@ -import resolve from 'eslint-module-utils/resolve'; -import semver from 'semver'; -import flatMap from 'array.prototype.flatmap'; +import resolve from '../utils/resolve' +import semver from 'semver' +import flatMap from 'array.prototype.flatmap' -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' -let typescriptPkg; +let typescriptPkg try { - typescriptPkg = require('typescript/package.json'); // eslint-disable-line import/no-extraneous-dependencies -} catch (e) { /**/ } + typescriptPkg = require('typescript/package.json') // eslint-disable-line import-x/no-extraneous-dependencies +} catch (e) { + /**/ +} function checkImports(imported, context) { for (const [module, nodes] of imported.entries()) { if (nodes.length > 1) { - const message = `'${module}' imported multiple times.`; - const [first, ...rest] = nodes; - const sourceCode = context.getSourceCode(); - const fix = getFix(first, rest, sourceCode, context); + const message = `'${module}' imported multiple times.` + const [first, ...rest] = nodes + const sourceCode = context.getSourceCode() + const fix = getFix(first, rest, sourceCode, context) context.report({ node: first.source, message, fix, // Attach the autofix (if any) to the first import. - }); + }) for (const node of rest) { context.report({ node: node.source, message, - }); + }) } } } @@ -41,7 +43,7 @@ function getFix(first, rest, sourceCode, context) { // `sourceCode.getCommentsBefore` was added in 4.0, so that's an easy thing to // check for. if (typeof sourceCode.getCommentsBefore !== 'function') { - return undefined; + return undefined } // Adjusting the first import might make it multiline, which could break @@ -49,89 +51,116 @@ function getFix(first, rest, sourceCode, context) { // import has comments. Also, if the first import is `import * as ns from // './foo'` there's nothing we can do. if (hasProblematicComments(first, sourceCode) || hasNamespace(first)) { - return undefined; + return undefined } const defaultImportNames = new Set( - flatMap([].concat(first, rest || []), (x) => getDefaultImportName(x) || []), - ); + flatMap([].concat(first, rest || []), x => getDefaultImportName(x) || []), + ) // Bail if there are multiple different default import names – it's up to the // user to choose which one to keep. if (defaultImportNames.size > 1) { - return undefined; + return undefined } // Leave it to the user to handle comments. Also skip `import * as ns from // './foo'` imports, since they cannot be merged into another import. - const restWithoutComments = rest.filter((node) => !hasProblematicComments(node, sourceCode) && !hasNamespace(node)); + const restWithoutComments = rest.filter( + node => !hasProblematicComments(node, sourceCode) && !hasNamespace(node), + ) const specifiers = restWithoutComments - .map((node) => { - const tokens = sourceCode.getTokens(node); - const openBrace = tokens.find((token) => isPunctuator(token, '{')); - const closeBrace = tokens.find((token) => isPunctuator(token, '}')); + .map(node => { + const tokens = sourceCode.getTokens(node) + const openBrace = tokens.find(token => isPunctuator(token, '{')) + const closeBrace = tokens.find(token => isPunctuator(token, '}')) if (openBrace == null || closeBrace == null) { - return undefined; + return undefined } return { importNode: node, - identifiers: sourceCode.text.slice(openBrace.range[1], closeBrace.range[0]).split(','), // Split the text into separate identifiers (retaining any whitespace before or after) + identifiers: sourceCode.text + .slice(openBrace.range[1], closeBrace.range[0]) + .split(','), // Split the text into separate identifiers (retaining any whitespace before or after) isEmpty: !hasSpecifiers(node), - }; + } }) - .filter(Boolean); + .filter(Boolean) - const unnecessaryImports = restWithoutComments.filter((node) => !hasSpecifiers(node) - && !hasNamespace(node) - && !specifiers.some((specifier) => specifier.importNode === node), - ); + const unnecessaryImports = restWithoutComments.filter( + node => + !hasSpecifiers(node) && + !hasNamespace(node) && + !specifiers.some(specifier => specifier.importNode === node), + ) - const shouldAddDefault = getDefaultImportName(first) == null && defaultImportNames.size === 1; - const shouldAddSpecifiers = specifiers.length > 0; - const shouldRemoveUnnecessary = unnecessaryImports.length > 0; + const shouldAddDefault = + getDefaultImportName(first) == null && defaultImportNames.size === 1 + const shouldAddSpecifiers = specifiers.length > 0 + const shouldRemoveUnnecessary = unnecessaryImports.length > 0 if (!(shouldAddDefault || shouldAddSpecifiers || shouldRemoveUnnecessary)) { - return undefined; + return undefined } - return (fixer) => { - const tokens = sourceCode.getTokens(first); - const openBrace = tokens.find((token) => isPunctuator(token, '{')); - const closeBrace = tokens.find((token) => isPunctuator(token, '}')); - const firstToken = sourceCode.getFirstToken(first); - const [defaultImportName] = defaultImportNames; - - const firstHasTrailingComma = closeBrace != null && isPunctuator(sourceCode.getTokenBefore(closeBrace), ','); - const firstIsEmpty = !hasSpecifiers(first); + return fixer => { + const tokens = sourceCode.getTokens(first) + const openBrace = tokens.find(token => isPunctuator(token, '{')) + const closeBrace = tokens.find(token => isPunctuator(token, '}')) + const firstToken = sourceCode.getFirstToken(first) + const [defaultImportName] = defaultImportNames + + const firstHasTrailingComma = + closeBrace != null && + isPunctuator(sourceCode.getTokenBefore(closeBrace), ',') + const firstIsEmpty = !hasSpecifiers(first) const firstExistingIdentifiers = firstIsEmpty ? new Set() - : new Set(sourceCode.text.slice(openBrace.range[1], closeBrace.range[0]) - .split(',') - .map((x) => x.trim()), - ); + : new Set( + sourceCode.text + .slice(openBrace.range[1], closeBrace.range[0]) + .split(',') + .map(x => x.trim()), + ) const [specifiersText] = specifiers.reduce( ([result, needsComma, existingIdentifiers], specifier) => { - const isTypeSpecifier = specifier.importNode.importKind === 'type'; + const isTypeSpecifier = specifier.importNode.importKind === 'type' - const preferInline = context.options[0] && context.options[0]['prefer-inline']; + const preferInline = + context.options[0] && context.options[0]['prefer-inline'] // a user might set prefer-inline but not have a supporting TypeScript version. Flow does not support inline types so this should fail in that case as well. - if (preferInline && (!typescriptPkg || !semver.satisfies(typescriptPkg.version, '>= 4.5'))) { - throw new Error('Your version of TypeScript does not support inline type imports.'); + if ( + preferInline && + (!typescriptPkg || !semver.satisfies(typescriptPkg.version, '>= 4.5')) + ) { + throw new Error( + 'Your version of TypeScript does not support inline type imports.', + ) } // Add *only* the new identifiers that don't already exist, and track any new identifiers so we don't add them again in the next loop - const [specifierText, updatedExistingIdentifiers] = specifier.identifiers.reduce(([text, set], cur) => { - const trimmed = cur.trim(); // Trim whitespace before/after to compare to our set of existing identifiers - const curWithType = trimmed.length > 0 && preferInline && isTypeSpecifier ? `type ${cur}` : cur; - if (existingIdentifiers.has(trimmed)) { - return [text, set]; - } - return [text.length > 0 ? `${text},${curWithType}` : curWithType, set.add(trimmed)]; - }, ['', existingIdentifiers]); + const [specifierText, updatedExistingIdentifiers] = + specifier.identifiers.reduce( + ([text, set], cur) => { + const trimmed = cur.trim() // Trim whitespace before/after to compare to our set of existing identifiers + const curWithType = + trimmed.length > 0 && preferInline && isTypeSpecifier + ? `type ${cur}` + : cur + if (existingIdentifiers.has(trimmed)) { + return [text, set] + } + return [ + text.length > 0 ? `${text},${curWithType}` : curWithType, + set.add(trimmed), + ] + }, + ['', existingIdentifiers], + ) return [ needsComma && !specifier.isEmpty && specifierText.length > 0 @@ -139,50 +168,65 @@ function getFix(first, rest, sourceCode, context) { : `${result}${specifierText}`, specifier.isEmpty ? needsComma : true, updatedExistingIdentifiers, - ]; + ] }, ['', !firstHasTrailingComma && !firstIsEmpty, firstExistingIdentifiers], - ); + ) - const fixes = []; + const fixes = [] if (shouldAddDefault && openBrace == null && shouldAddSpecifiers) { // `import './foo'` → `import def, {...} from './foo'` fixes.push( - fixer.insertTextAfter(firstToken, ` ${defaultImportName}, {${specifiersText}} from`), - ); + fixer.insertTextAfter( + firstToken, + ` ${defaultImportName}, {${specifiersText}} from`, + ), + ) } else if (shouldAddDefault && openBrace == null && !shouldAddSpecifiers) { // `import './foo'` → `import def from './foo'` - fixes.push(fixer.insertTextAfter(firstToken, ` ${defaultImportName} from`)); + fixes.push( + fixer.insertTextAfter(firstToken, ` ${defaultImportName} from`), + ) } else if (shouldAddDefault && openBrace != null && closeBrace != null) { // `import {...} from './foo'` → `import def, {...} from './foo'` - fixes.push(fixer.insertTextAfter(firstToken, ` ${defaultImportName},`)); + fixes.push(fixer.insertTextAfter(firstToken, ` ${defaultImportName},`)) if (shouldAddSpecifiers) { // `import def, {...} from './foo'` → `import def, {..., ...} from './foo'` - fixes.push(fixer.insertTextBefore(closeBrace, specifiersText)); + fixes.push(fixer.insertTextBefore(closeBrace, specifiersText)) } } else if (!shouldAddDefault && openBrace == null && shouldAddSpecifiers) { if (first.specifiers.length === 0) { // `import './foo'` → `import {...} from './foo'` - fixes.push(fixer.insertTextAfter(firstToken, ` {${specifiersText}} from`)); + fixes.push( + fixer.insertTextAfter(firstToken, ` {${specifiersText}} from`), + ) } else { // `import def from './foo'` → `import def, {...} from './foo'` - fixes.push(fixer.insertTextAfter(first.specifiers[0], `, {${specifiersText}}`)); + fixes.push( + fixer.insertTextAfter(first.specifiers[0], `, {${specifiersText}}`), + ) } } else if (!shouldAddDefault && openBrace != null && closeBrace != null) { // `import {...} './foo'` → `import {..., ...} from './foo'` - fixes.push(fixer.insertTextBefore(closeBrace, specifiersText)); + fixes.push(fixer.insertTextBefore(closeBrace, specifiersText)) } // Remove imports whose specifiers have been moved into the first import. for (const specifier of specifiers) { - const importNode = specifier.importNode; - fixes.push(fixer.remove(importNode)); - - const charAfterImportRange = [importNode.range[1], importNode.range[1] + 1]; - const charAfterImport = sourceCode.text.substring(charAfterImportRange[0], charAfterImportRange[1]); + const importNode = specifier.importNode + fixes.push(fixer.remove(importNode)) + + const charAfterImportRange = [ + importNode.range[1], + importNode.range[1] + 1, + ] + const charAfterImport = sourceCode.text.substring( + charAfterImportRange[0], + charAfterImportRange[1], + ) if (charAfterImport === '\n') { - fixes.push(fixer.removeRange(charAfterImportRange)); + fixes.push(fixer.removeRange(charAfterImportRange)) } } @@ -190,81 +234,94 @@ function getFix(first, rest, sourceCode, context) { // and side-effect-only imports that are unnecessary due to the first // import. for (const node of unnecessaryImports) { - fixes.push(fixer.remove(node)); + fixes.push(fixer.remove(node)) - const charAfterImportRange = [node.range[1], node.range[1] + 1]; - const charAfterImport = sourceCode.text.substring(charAfterImportRange[0], charAfterImportRange[1]); + const charAfterImportRange = [node.range[1], node.range[1] + 1] + const charAfterImport = sourceCode.text.substring( + charAfterImportRange[0], + charAfterImportRange[1], + ) if (charAfterImport === '\n') { - fixes.push(fixer.removeRange(charAfterImportRange)); + fixes.push(fixer.removeRange(charAfterImportRange)) } } - return fixes; - }; + return fixes + } } function isPunctuator(node, value) { - return node.type === 'Punctuator' && node.value === value; + return node.type === 'Punctuator' && node.value === value } // Get the name of the default import of `node`, if any. function getDefaultImportName(node) { - const defaultSpecifier = node.specifiers - .find((specifier) => specifier.type === 'ImportDefaultSpecifier'); - return defaultSpecifier != null ? defaultSpecifier.local.name : undefined; + const defaultSpecifier = node.specifiers.find( + specifier => specifier.type === 'ImportDefaultSpecifier', + ) + return defaultSpecifier != null ? defaultSpecifier.local.name : undefined } // Checks whether `node` has a namespace import. function hasNamespace(node) { - const specifiers = node.specifiers - .filter((specifier) => specifier.type === 'ImportNamespaceSpecifier'); - return specifiers.length > 0; + const specifiers = node.specifiers.filter( + specifier => specifier.type === 'ImportNamespaceSpecifier', + ) + return specifiers.length > 0 } // Checks whether `node` has any non-default specifiers. function hasSpecifiers(node) { - const specifiers = node.specifiers - .filter((specifier) => specifier.type === 'ImportSpecifier'); - return specifiers.length > 0; + const specifiers = node.specifiers.filter( + specifier => specifier.type === 'ImportSpecifier', + ) + return specifiers.length > 0 } // It's not obvious what the user wants to do with comments associated with // duplicate imports, so skip imports with comments when autofixing. function hasProblematicComments(node, sourceCode) { return ( - hasCommentBefore(node, sourceCode) - || hasCommentAfter(node, sourceCode) - || hasCommentInsideNonSpecifiers(node, sourceCode) - ); + hasCommentBefore(node, sourceCode) || + hasCommentAfter(node, sourceCode) || + hasCommentInsideNonSpecifiers(node, sourceCode) + ) } // Checks whether `node` has a comment (that ends) on the previous line or on // the same line as `node` (starts). function hasCommentBefore(node, sourceCode) { - return sourceCode.getCommentsBefore(node) - .some((comment) => comment.loc.end.line >= node.loc.start.line - 1); + return sourceCode + .getCommentsBefore(node) + .some(comment => comment.loc.end.line >= node.loc.start.line - 1) } // Checks whether `node` has a comment (that starts) on the same line as `node` // (ends). function hasCommentAfter(node, sourceCode) { - return sourceCode.getCommentsAfter(node) - .some((comment) => comment.loc.start.line === node.loc.end.line); + return sourceCode + .getCommentsAfter(node) + .some(comment => comment.loc.start.line === node.loc.end.line) } // Checks whether `node` has any comments _inside,_ except inside the `{...}` // part (if any). function hasCommentInsideNonSpecifiers(node, sourceCode) { - const tokens = sourceCode.getTokens(node); - const openBraceIndex = tokens.findIndex((token) => isPunctuator(token, '{')); - const closeBraceIndex = tokens.findIndex((token) => isPunctuator(token, '}')); + const tokens = sourceCode.getTokens(node) + const openBraceIndex = tokens.findIndex(token => isPunctuator(token, '{')) + const closeBraceIndex = tokens.findIndex(token => isPunctuator(token, '}')) // Slice away the first token, since we're no looking for comments _before_ // `node` (only inside). If there's a `{...}` part, look for comments before // the `{`, but not before the `}` (hence the `+1`s). - const someTokens = openBraceIndex >= 0 && closeBraceIndex >= 0 - ? tokens.slice(1, openBraceIndex + 1).concat(tokens.slice(closeBraceIndex + 1)) - : tokens.slice(1); - return someTokens.some((token) => sourceCode.getCommentsBefore(token).length > 0); + const someTokens = + openBraceIndex >= 0 && closeBraceIndex >= 0 + ? tokens + .slice(1, openBraceIndex + 1) + .concat(tokens.slice(closeBraceIndex + 1)) + : tokens.slice(1) + return someTokens.some( + token => sourceCode.getCommentsBefore(token).length > 0, + ) } module.exports = { @@ -272,7 +329,8 @@ module.exports = { type: 'problem', docs: { category: 'Style guide', - description: 'Forbid repeated import of the same module in multiple places.', + description: + 'Forbid repeated import of the same module in multiple places.', url: docsUrl('no-duplicates'), }, fixable: 'code', @@ -294,18 +352,21 @@ module.exports = { create(context) { // Prepare the resolver from options. - const considerQueryStringOption = context.options[0] - && context.options[0].considerQueryString; - const defaultResolver = (sourcePath) => resolve(sourcePath, context) || sourcePath; - const resolver = considerQueryStringOption ? (sourcePath) => { - const parts = sourcePath.match(/^([^?]*)\?(.*)$/); - if (!parts) { - return defaultResolver(sourcePath); - } - return `${defaultResolver(parts[1])}?${parts[2]}`; - } : defaultResolver; + const considerQueryStringOption = + context.options[0] && context.options[0].considerQueryString + const defaultResolver = sourcePath => + resolve(sourcePath, context) || sourcePath + const resolver = considerQueryStringOption + ? sourcePath => { + const parts = sourcePath.match(/^([^?]*)\?(.*)$/) + if (!parts) { + return defaultResolver(sourcePath) + } + return `${defaultResolver(parts[1])}?${parts[2]}` + } + : defaultResolver - const moduleMaps = new Map(); + const moduleMaps = new Map() function getImportMap(n) { if (!moduleMaps.has(n.parent)) { @@ -314,41 +375,48 @@ module.exports = { nsImported: new Map(), defaultTypesImported: new Map(), namedTypesImported: new Map(), - }); + }) } - const map = moduleMaps.get(n.parent); - const preferInline = context.options[0] && context.options[0]['prefer-inline']; + const map = moduleMaps.get(n.parent) + const preferInline = + context.options[0] && context.options[0]['prefer-inline'] if (!preferInline && n.importKind === 'type') { - return n.specifiers.length > 0 && n.specifiers[0].type === 'ImportDefaultSpecifier' ? map.defaultTypesImported : map.namedTypesImported; + return n.specifiers.length > 0 && + n.specifiers[0].type === 'ImportDefaultSpecifier' + ? map.defaultTypesImported + : map.namedTypesImported } - if (!preferInline && n.specifiers.some((spec) => spec.importKind === 'type')) { - return map.namedTypesImported; + if ( + !preferInline && + n.specifiers.some(spec => spec.importKind === 'type') + ) { + return map.namedTypesImported } - return hasNamespace(n) ? map.nsImported : map.imported; + return hasNamespace(n) ? map.nsImported : map.imported } return { ImportDeclaration(n) { // resolved path will cover aliased duplicates - const resolvedPath = resolver(n.source.value); - const importMap = getImportMap(n); + const resolvedPath = resolver(n.source.value) + const importMap = getImportMap(n) if (importMap.has(resolvedPath)) { - importMap.get(resolvedPath).push(n); + importMap.get(resolvedPath).push(n) } else { - importMap.set(resolvedPath, [n]); + importMap.set(resolvedPath, [n]) } }, 'Program:exit'() { for (const map of moduleMaps.values()) { - checkImports(map.imported, context); - checkImports(map.nsImported, context); - checkImports(map.defaultTypesImported, context); - checkImports(map.namedTypesImported, context); + checkImports(map.imported, context) + checkImports(map.nsImported, context) + checkImports(map.defaultTypesImported, context) + checkImports(map.namedTypesImported, context) } }, - }; + } }, -}; +} diff --git a/src/rules/no-dynamic-require.js b/src/rules/no-dynamic-require.js index f8b369a70..2b752e34d 100644 --- a/src/rules/no-dynamic-require.js +++ b/src/rules/no-dynamic-require.js @@ -1,25 +1,27 @@ -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' function isRequire(node) { - return node - && node.callee - && node.callee.type === 'Identifier' - && node.callee.name === 'require' - && node.arguments.length >= 1; + return ( + node && + node.callee && + node.callee.type === 'Identifier' && + node.callee.name === 'require' && + node.arguments.length >= 1 + ) } function isDynamicImport(node) { - return node - && node.callee - && node.callee.type === 'Import'; + return node && node.callee && node.callee.type === 'Import' } function isStaticValue(arg) { - return arg.type === 'Literal' - || arg.type === 'TemplateLiteral' && arg.expressions.length === 0; + return ( + arg.type === 'Literal' || + (arg.type === 'TemplateLiteral' && arg.expressions.length === 0) + ) } -const dynamicImportErrorMessage = 'Calls to import() should use string literals'; +const dynamicImportErrorMessage = 'Calls to import() should use string literals' module.exports = { meta: { @@ -43,35 +45,35 @@ module.exports = { }, create(context) { - const options = context.options[0] || {}; + const options = context.options[0] || {} return { CallExpression(node) { if (!node.arguments[0] || isStaticValue(node.arguments[0])) { - return; + return } if (isRequire(node)) { return context.report({ node, message: 'Calls to require() should use string literals', - }); + }) } if (options.esmodule && isDynamicImport(node)) { return context.report({ node, message: dynamicImportErrorMessage, - }); + }) } }, ImportExpression(node) { if (!options.esmodule || isStaticValue(node.source)) { - return; + return } return context.report({ node, message: dynamicImportErrorMessage, - }); + }) }, - }; + } }, -}; +} diff --git a/src/rules/no-empty-named-blocks.js b/src/rules/no-empty-named-blocks.js index 3ec1501b8..a9324c5bb 100644 --- a/src/rules/no-empty-named-blocks.js +++ b/src/rules/no-empty-named-blocks.js @@ -1,18 +1,22 @@ -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' function getEmptyBlockRange(tokens, index) { - const token = tokens[index]; - const nextToken = tokens[index + 1]; - const prevToken = tokens[index - 1]; - let start = token.range[0]; - const end = nextToken.range[1]; + const token = tokens[index] + const nextToken = tokens[index + 1] + const prevToken = tokens[index - 1] + let start = token.range[0] + const end = nextToken.range[1] // Remove block tokens and the previous comma - if (prevToken.value === ',' || prevToken.value === 'type' || prevToken.value === 'typeof') { - start = prevToken.range[0]; + if ( + prevToken.value === ',' || + prevToken.value === 'type' || + prevToken.value === 'typeof' + ) { + start = prevToken.range[0] } - return [start, end]; + return [start, end] } module.exports = { @@ -29,29 +33,36 @@ module.exports = { }, create(context) { - const importsWithoutNameds = []; + const importsWithoutNameds = [] return { ImportDeclaration(node) { - if (!node.specifiers.some((x) => x.type === 'ImportSpecifier')) { - importsWithoutNameds.push(node); + if (!node.specifiers.some(x => x.type === 'ImportSpecifier')) { + importsWithoutNameds.push(node) } }, 'Program:exit'(program) { - const importsTokens = importsWithoutNameds.map((node) => [node, program.tokens.filter((x) => x.range[0] >= node.range[0] && x.range[1] <= node.range[1])]); + const importsTokens = importsWithoutNameds.map(node => [ + node, + program.tokens.filter( + x => x.range[0] >= node.range[0] && x.range[1] <= node.range[1], + ), + ]) importsTokens.forEach(([node, tokens]) => { - tokens.forEach((token) => { - const idx = program.tokens.indexOf(token); - const nextToken = program.tokens[idx + 1]; + tokens.forEach(token => { + const idx = program.tokens.indexOf(token) + const nextToken = program.tokens[idx + 1] if (nextToken && token.value === '{' && nextToken.value === '}') { - const hasOtherIdentifiers = tokens.some((token) => token.type === 'Identifier' - && token.value !== 'from' - && token.value !== 'type' - && token.value !== 'typeof', - ); + const hasOtherIdentifiers = tokens.some( + token => + token.type === 'Identifier' && + token.value !== 'from' && + token.value !== 'type' && + token.value !== 'typeof', + ) // If it has no other identifiers it's the only thing in the import, so we can either remove the import // completely or transform it in a side-effects only import @@ -64,7 +75,7 @@ module.exports = { desc: 'Remove unused import', fix(fixer) { // Remove the whole import - return fixer.remove(node); + return fixer.remove(node) }, }, { @@ -72,34 +83,49 @@ module.exports = { fix(fixer) { // Remove the empty block and the 'from' token, leaving the import only for its side // effects, e.g. `import 'mod'` - const sourceCode = context.getSourceCode(); - const fromToken = program.tokens.find((t) => t.value === 'from'); - const importToken = program.tokens.find((t) => t.value === 'import'); - const hasSpaceAfterFrom = sourceCode.isSpaceBetween(fromToken, sourceCode.getTokenAfter(fromToken)); - const hasSpaceAfterImport = sourceCode.isSpaceBetween(importToken, sourceCode.getTokenAfter(fromToken)); + const sourceCode = context.getSourceCode() + const fromToken = program.tokens.find( + t => t.value === 'from', + ) + const importToken = program.tokens.find( + t => t.value === 'import', + ) + const hasSpaceAfterFrom = sourceCode.isSpaceBetween( + fromToken, + sourceCode.getTokenAfter(fromToken), + ) + const hasSpaceAfterImport = sourceCode.isSpaceBetween( + importToken, + sourceCode.getTokenAfter(fromToken), + ) - const [start] = getEmptyBlockRange(program.tokens, idx); - const [, end] = fromToken.range; - const range = [start, hasSpaceAfterFrom ? end + 1 : end]; + const [start] = getEmptyBlockRange(program.tokens, idx) + const [, end] = fromToken.range + const range = [start, hasSpaceAfterFrom ? end + 1 : end] - return fixer.replaceTextRange(range, hasSpaceAfterImport ? '' : ' '); + return fixer.replaceTextRange( + range, + hasSpaceAfterImport ? '' : ' ', + ) }, }, ], - }); + }) } else { context.report({ node, message: 'Unexpected empty named import block', fix(fixer) { - return fixer.removeRange(getEmptyBlockRange(program.tokens, idx)); + return fixer.removeRange( + getEmptyBlockRange(program.tokens, idx), + ) }, - }); + }) } } - }); - }); + }) + }) }, - }; + } }, -}; +} diff --git a/src/rules/no-extraneous-dependencies.js b/src/rules/no-extraneous-dependencies.js index df9798790..184c97bd8 100644 --- a/src/rules/no-extraneous-dependencies.js +++ b/src/rules/no-extraneous-dependencies.js @@ -1,29 +1,31 @@ -import path from 'path'; -import fs from 'fs'; -import pkgUp from 'eslint-module-utils/pkgUp'; -import minimatch from 'minimatch'; -import resolve from 'eslint-module-utils/resolve'; -import moduleVisitor from 'eslint-module-utils/moduleVisitor'; -import importType from '../core/importType'; -import { getFilePackageName } from '../core/packagePath'; -import docsUrl from '../docsUrl'; - -const depFieldCache = new Map(); +import path from 'path' +import fs from 'fs' +import pkgUp from '../utils/pkgUp' +import minimatch from 'minimatch' +import resolve from '../utils/resolve' +import moduleVisitor from '../utils/moduleVisitor' +import importType from '../core/importType' +import { getFilePackageName } from '../core/packagePath' +import docsUrl from '../docsUrl' + +const depFieldCache = new Map() function hasKeys(obj = {}) { - return Object.keys(obj).length > 0; + return Object.keys(obj).length > 0 } function arrayOrKeys(arrayOrObject) { - return Array.isArray(arrayOrObject) ? arrayOrObject : Object.keys(arrayOrObject); + return Array.isArray(arrayOrObject) + ? arrayOrObject + : Object.keys(arrayOrObject) } function readJSON(jsonPath, throwException) { try { - return JSON.parse(fs.readFileSync(jsonPath, 'utf8')); + return JSON.parse(fs.readFileSync(jsonPath, 'utf8')) } catch (err) { if (throwException) { - throw err; + throw err } } } @@ -36,21 +38,23 @@ function extractDepFields(pkg) { peerDependencies: pkg.peerDependencies || {}, // BundledDeps should be in the form of an array, but object notation is also supported by // `npm`, so we convert it to an array if it is an object - bundledDependencies: arrayOrKeys(pkg.bundleDependencies || pkg.bundledDependencies || []), - }; + bundledDependencies: arrayOrKeys( + pkg.bundleDependencies || pkg.bundledDependencies || [], + ), + } } function getPackageDepFields(packageJsonPath, throwAtRead) { if (!depFieldCache.has(packageJsonPath)) { - const depFields = extractDepFields(readJSON(packageJsonPath, throwAtRead)); - depFieldCache.set(packageJsonPath, depFields); + const depFields = extractDepFields(readJSON(packageJsonPath, throwAtRead)) + depFieldCache.set(packageJsonPath, depFields) } - return depFieldCache.get(packageJsonPath); + return depFieldCache.get(packageJsonPath) } function getDependencies(context, packageDir) { - let paths = []; + let paths = [] try { const packageContent = { dependencies: {}, @@ -58,86 +62,87 @@ function getDependencies(context, packageDir) { optionalDependencies: {}, peerDependencies: {}, bundledDependencies: [], - }; + } if (packageDir && packageDir.length > 0) { if (!Array.isArray(packageDir)) { - paths = [path.resolve(packageDir)]; + paths = [path.resolve(packageDir)] } else { - paths = packageDir.map((dir) => path.resolve(dir)); + paths = packageDir.map(dir => path.resolve(dir)) } } if (paths.length > 0) { // use rule config to find package.json - paths.forEach((dir) => { - const packageJsonPath = path.join(dir, 'package.json'); - const _packageContent = getPackageDepFields(packageJsonPath, true); - Object.keys(packageContent).forEach((depsKey) => { - Object.assign(packageContent[depsKey], _packageContent[depsKey]); - }); - }); + paths.forEach(dir => { + const packageJsonPath = path.join(dir, 'package.json') + const _packageContent = getPackageDepFields(packageJsonPath, true) + Object.keys(packageContent).forEach(depsKey => { + Object.assign(packageContent[depsKey], _packageContent[depsKey]) + }) + }) } else { const packageJsonPath = pkgUp({ - cwd: context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(), + cwd: context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename(), normalize: false, - }); + }) // use closest package.json - Object.assign( - packageContent, - getPackageDepFields(packageJsonPath, false), - ); + Object.assign(packageContent, getPackageDepFields(packageJsonPath, false)) } - if (![ - packageContent.dependencies, - packageContent.devDependencies, - packageContent.optionalDependencies, - packageContent.peerDependencies, - packageContent.bundledDependencies, - ].some(hasKeys)) { - return null; + if ( + ![ + packageContent.dependencies, + packageContent.devDependencies, + packageContent.optionalDependencies, + packageContent.peerDependencies, + packageContent.bundledDependencies, + ].some(hasKeys) + ) { + return null } - return packageContent; + return packageContent } catch (e) { if (paths.length > 0 && e.code === 'ENOENT') { context.report({ message: 'The package.json file could not be found.', loc: { line: 0, column: 0 }, - }); + }) } if (e.name === 'JSONError' || e instanceof SyntaxError) { context.report({ message: `The package.json file could not be parsed: ${e.message}`, loc: { line: 0, column: 0 }, - }); + }) } - return null; + return null } } function missingErrorMessage(packageName) { - return `'${packageName}' should be listed in the project's dependencies. Run 'npm i -S ${packageName}' to add it`; + return `'${packageName}' should be listed in the project's dependencies. Run 'npm i -S ${packageName}' to add it` } function devDepErrorMessage(packageName) { - return `'${packageName}' should be listed in the project's dependencies, not devDependencies.`; + return `'${packageName}' should be listed in the project's dependencies, not devDependencies.` } function optDepErrorMessage(packageName) { - return `'${packageName}' should be listed in the project's dependencies, not optionalDependencies.`; + return `'${packageName}' should be listed in the project's dependencies, not optionalDependencies.` } function getModuleOriginalName(name) { - const [first, second] = name.split('/'); - return first.startsWith('@') ? `${first}/${second}` : first; + const [first, second] = name.split('/') + return first.startsWith('@') ? `${first}/${second}` : first } function getModuleRealName(resolved) { - return getFilePackageName(resolved); + return getFilePackageName(resolved) } function checkDependencyDeclaration(deps, packageName, declarationStatus) { @@ -147,107 +152,138 @@ function checkDependencyDeclaration(deps, packageName, declarationStatus) { isInOptDeps: false, isInPeerDeps: false, isInBundledDeps: false, - }; + } // in case of sub package.json inside a module // check the dependencies on all hierarchy - const packageHierarchy = []; - const packageNameParts = packageName ? packageName.split('/') : []; + const packageHierarchy = [] + const packageNameParts = packageName ? packageName.split('/') : [] packageNameParts.forEach((namePart, index) => { if (!namePart.startsWith('@')) { - const ancestor = packageNameParts.slice(0, index + 1).join('/'); - packageHierarchy.push(ancestor); + const ancestor = packageNameParts.slice(0, index + 1).join('/') + packageHierarchy.push(ancestor) } - }); - - return packageHierarchy.reduce((result, ancestorName) => ({ - isInDeps: result.isInDeps || deps.dependencies[ancestorName] !== undefined, - isInDevDeps: result.isInDevDeps || deps.devDependencies[ancestorName] !== undefined, - isInOptDeps: result.isInOptDeps || deps.optionalDependencies[ancestorName] !== undefined, - isInPeerDeps: result.isInPeerDeps || deps.peerDependencies[ancestorName] !== undefined, - isInBundledDeps: - result.isInBundledDeps || deps.bundledDependencies.indexOf(ancestorName) !== -1, - }), newDeclarationStatus); + }) + + return packageHierarchy.reduce( + (result, ancestorName) => ({ + isInDeps: + result.isInDeps || deps.dependencies[ancestorName] !== undefined, + isInDevDeps: + result.isInDevDeps || deps.devDependencies[ancestorName] !== undefined, + isInOptDeps: + result.isInOptDeps || + deps.optionalDependencies[ancestorName] !== undefined, + isInPeerDeps: + result.isInPeerDeps || + deps.peerDependencies[ancestorName] !== undefined, + isInBundledDeps: + result.isInBundledDeps || + deps.bundledDependencies.indexOf(ancestorName) !== -1, + }), + newDeclarationStatus, + ) } function reportIfMissing(context, deps, depsOptions, node, name) { // Do not report when importing types unless option is enabled if ( - !depsOptions.verifyTypeImports - && ( - node.importKind === 'type' - || node.importKind === 'typeof' - || node.exportKind === 'type' - || Array.isArray(node.specifiers) && node.specifiers.length && node.specifiers.every((specifier) => specifier.importKind === 'type' || specifier.importKind === 'typeof') - ) + !depsOptions.verifyTypeImports && + (node.importKind === 'type' || + node.importKind === 'typeof' || + node.exportKind === 'type' || + (Array.isArray(node.specifiers) && + node.specifiers.length && + node.specifiers.every( + specifier => + specifier.importKind === 'type' || + specifier.importKind === 'typeof', + ))) ) { - return; + return } - const typeOfImport = importType(name, context); + const typeOfImport = importType(name, context) if ( - typeOfImport !== 'external' - && (typeOfImport !== 'internal' || !depsOptions.verifyInternalDeps) + typeOfImport !== 'external' && + (typeOfImport !== 'internal' || !depsOptions.verifyInternalDeps) ) { - return; + return } - const resolved = resolve(name, context); - if (!resolved) { return; } + const resolved = resolve(name, context) + if (!resolved) { + return + } - const importPackageName = getModuleOriginalName(name); - let declarationStatus = checkDependencyDeclaration(deps, importPackageName); + const importPackageName = getModuleOriginalName(name) + let declarationStatus = checkDependencyDeclaration(deps, importPackageName) if ( - declarationStatus.isInDeps - || depsOptions.allowDevDeps && declarationStatus.isInDevDeps - || depsOptions.allowPeerDeps && declarationStatus.isInPeerDeps - || depsOptions.allowOptDeps && declarationStatus.isInOptDeps - || depsOptions.allowBundledDeps && declarationStatus.isInBundledDeps + declarationStatus.isInDeps || + (depsOptions.allowDevDeps && declarationStatus.isInDevDeps) || + (depsOptions.allowPeerDeps && declarationStatus.isInPeerDeps) || + (depsOptions.allowOptDeps && declarationStatus.isInOptDeps) || + (depsOptions.allowBundledDeps && declarationStatus.isInBundledDeps) ) { - return; + return } // test the real name from the resolved package.json // if not aliased imports (alias/react for example), importPackageName can be misinterpreted - const realPackageName = getModuleRealName(resolved); + const realPackageName = getModuleRealName(resolved) if (realPackageName && realPackageName !== importPackageName) { - declarationStatus = checkDependencyDeclaration(deps, realPackageName, declarationStatus); + declarationStatus = checkDependencyDeclaration( + deps, + realPackageName, + declarationStatus, + ) if ( - declarationStatus.isInDeps - || depsOptions.allowDevDeps && declarationStatus.isInDevDeps - || depsOptions.allowPeerDeps && declarationStatus.isInPeerDeps - || depsOptions.allowOptDeps && declarationStatus.isInOptDeps - || depsOptions.allowBundledDeps && declarationStatus.isInBundledDeps + declarationStatus.isInDeps || + (depsOptions.allowDevDeps && declarationStatus.isInDevDeps) || + (depsOptions.allowPeerDeps && declarationStatus.isInPeerDeps) || + (depsOptions.allowOptDeps && declarationStatus.isInOptDeps) || + (depsOptions.allowBundledDeps && declarationStatus.isInBundledDeps) ) { - return; + return } } if (declarationStatus.isInDevDeps && !depsOptions.allowDevDeps) { - context.report(node, devDepErrorMessage(realPackageName || importPackageName)); - return; + context.report( + node, + devDepErrorMessage(realPackageName || importPackageName), + ) + return } if (declarationStatus.isInOptDeps && !depsOptions.allowOptDeps) { - context.report(node, optDepErrorMessage(realPackageName || importPackageName)); - return; + context.report( + node, + optDepErrorMessage(realPackageName || importPackageName), + ) + return } - context.report(node, missingErrorMessage(realPackageName || importPackageName)); + context.report( + node, + missingErrorMessage(realPackageName || importPackageName), + ) } function testConfig(config, filename) { // Simplest configuration first, either a boolean or nothing. if (typeof config === 'boolean' || typeof config === 'undefined') { - return config; + return config } // Array of globs. - return config.some((c) => minimatch(filename, c) - || minimatch(filename, path.join(process.cwd(), c)), - ); + return config.some( + c => + minimatch(filename, c) || + minimatch(filename, path.join(process.cwd(), c)), + ) } module.exports = { @@ -277,25 +313,33 @@ module.exports = { }, create(context) { - const options = context.options[0] || {}; - const filename = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); - const deps = getDependencies(context, options.packageDir) || extractDepFields({}); + const options = context.options[0] || {} + const filename = context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename() + const deps = + getDependencies(context, options.packageDir) || extractDepFields({}) const depsOptions = { allowDevDeps: testConfig(options.devDependencies, filename) !== false, - allowOptDeps: testConfig(options.optionalDependencies, filename) !== false, + allowOptDeps: + testConfig(options.optionalDependencies, filename) !== false, allowPeerDeps: testConfig(options.peerDependencies, filename) !== false, - allowBundledDeps: testConfig(options.bundledDependencies, filename) !== false, + allowBundledDeps: + testConfig(options.bundledDependencies, filename) !== false, verifyInternalDeps: !!options.includeInternal, verifyTypeImports: !!options.includeTypes, - }; + } - return moduleVisitor((source, node) => { - reportIfMissing(context, deps, depsOptions, node, source.value); - }, { commonjs: true }); + return moduleVisitor( + (source, node) => { + reportIfMissing(context, deps, depsOptions, node, source.value) + }, + { commonjs: true }, + ) }, 'Program:exit'() { - depFieldCache.clear(); + depFieldCache.clear() }, -}; +} diff --git a/src/rules/no-import-module-exports.js b/src/rules/no-import-module-exports.js index bc4605c39..5583cf5d8 100644 --- a/src/rules/no-import-module-exports.js +++ b/src/rules/no-import-module-exports.js @@ -1,27 +1,43 @@ -import minimatch from 'minimatch'; -import path from 'path'; -import pkgUp from 'eslint-module-utils/pkgUp'; +import minimatch from 'minimatch' +import path from 'path' +import pkgUp from '../utils/pkgUp' function getEntryPoint(context) { - const pkgPath = pkgUp({ cwd: context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename() }); + const pkgPath = pkgUp({ + cwd: context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename(), + }) try { - return require.resolve(path.dirname(pkgPath)); + return require.resolve(path.dirname(pkgPath)) } catch (error) { // Assume the package has no entrypoint (e.g. CLI packages) // in which case require.resolve would throw. - return null; + return null } } function findScope(context, identifier) { - const { scopeManager } = context.getSourceCode(); + const { scopeManager } = context.getSourceCode() - return scopeManager && scopeManager.scopes.slice().reverse().find((scope) => scope.variables.some((variable) => variable.identifiers.some((node) => node.name === identifier))); + return ( + scopeManager && + scopeManager.scopes + .slice() + .reverse() + .find(scope => + scope.variables.some(variable => + variable.identifiers.some(node => node.name === identifier), + ), + ) + ) } function findDefinition(objectScope, identifier) { - const variable = objectScope.variables.find((variable) => variable.name === identifier); - return variable.defs.find((def) => def.name.name === identifier); + const variable = objectScope.variables.find( + variable => variable.name === identifier, + ) + return variable.defs.find(def => def.name.name === identifier) } module.exports = { @@ -44,42 +60,55 @@ module.exports = { ], }, create(context) { - const importDeclarations = []; - const entryPoint = getEntryPoint(context); - const options = context.options[0] || {}; - let alreadyReported = false; + const importDeclarations = [] + const entryPoint = getEntryPoint(context) + const options = context.options[0] || {} + let alreadyReported = false function report(node) { - const fileName = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); - const isEntryPoint = entryPoint === fileName; - const isIdentifier = node.object.type === 'Identifier'; - const hasKeywords = (/^(module|exports)$/).test(node.object.name); - const objectScope = hasKeywords && findScope(context, node.object.name); - const variableDefinition = objectScope && findDefinition(objectScope, node.object.name); - const isImportBinding = variableDefinition && variableDefinition.type === 'ImportBinding'; - const hasCJSExportReference = hasKeywords && (!objectScope || objectScope.type === 'module'); - const isException = !!options.exceptions && options.exceptions.some((glob) => minimatch(fileName, glob)); + const fileName = context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename() + const isEntryPoint = entryPoint === fileName + const isIdentifier = node.object.type === 'Identifier' + const hasKeywords = /^(module|exports)$/.test(node.object.name) + const objectScope = hasKeywords && findScope(context, node.object.name) + const variableDefinition = + objectScope && findDefinition(objectScope, node.object.name) + const isImportBinding = + variableDefinition && variableDefinition.type === 'ImportBinding' + const hasCJSExportReference = + hasKeywords && (!objectScope || objectScope.type === 'module') + const isException = + !!options.exceptions && + options.exceptions.some(glob => minimatch(fileName, glob)) - if (isIdentifier && hasCJSExportReference && !isEntryPoint && !isException && !isImportBinding) { - importDeclarations.forEach((importDeclaration) => { + if ( + isIdentifier && + hasCJSExportReference && + !isEntryPoint && + !isException && + !isImportBinding + ) { + importDeclarations.forEach(importDeclaration => { context.report({ node: importDeclaration, message: `Cannot use import declarations in modules that export using CommonJS (module.exports = 'foo' or exports.bar = 'hi')`, - }); - }); - alreadyReported = true; + }) + }) + alreadyReported = true } } return { ImportDeclaration(node) { - importDeclarations.push(node); + importDeclarations.push(node) }, MemberExpression(node) { if (!alreadyReported) { - report(node); + report(node) } }, - }; + } }, -}; +} diff --git a/src/rules/no-internal-modules.js b/src/rules/no-internal-modules.js index 5ed456547..5cf978b45 100644 --- a/src/rules/no-internal-modules.js +++ b/src/rules/no-internal-modules.js @@ -1,9 +1,9 @@ -import minimatch from 'minimatch'; +import minimatch from 'minimatch' -import resolve from 'eslint-module-utils/resolve'; -import importType from '../core/importType'; -import moduleVisitor from 'eslint-module-utils/moduleVisitor'; -import docsUrl from '../docsUrl'; +import resolve from '../utils/resolve' +import importType from '../core/importType' +import moduleVisitor from '../utils/moduleVisitor' +import docsUrl from '../docsUrl' module.exports = { meta: { @@ -47,98 +47,117 @@ module.exports = { }, create: function noReachingInside(context) { - const options = context.options[0] || {}; - const allowRegexps = (options.allow || []).map((p) => minimatch.makeRe(p)); - const forbidRegexps = (options.forbid || []).map((p) => minimatch.makeRe(p)); + const options = context.options[0] || {} + const allowRegexps = (options.allow || []).map(p => minimatch.makeRe(p)) + const forbidRegexps = (options.forbid || []).map(p => minimatch.makeRe(p)) // minimatch patterns are expected to use / path separators, like import // statements, so normalize paths to use the same function normalizeSep(somePath) { - return somePath.split('\\').join('/'); + return somePath.split('\\').join('/') } function toSteps(somePath) { return normalizeSep(somePath) .split('/') - .filter((step) => step && step !== '.') + .filter(step => step && step !== '.') .reduce((acc, step) => { if (step === '..') { - return acc.slice(0, -1); + return acc.slice(0, -1) } - return acc.concat(step); - }, []); + return acc.concat(step) + }, []) } // test if reaching to this destination is allowed function reachingAllowed(importPath) { - return allowRegexps.some((re) => re.test(importPath)); + return allowRegexps.some(re => re.test(importPath)) } // test if reaching to this destination is forbidden function reachingForbidden(importPath) { - return forbidRegexps.some((re) => re.test(importPath)); + return forbidRegexps.some(re => re.test(importPath)) } function isAllowViolation(importPath) { - const steps = toSteps(importPath); + const steps = toSteps(importPath) - const nonScopeSteps = steps.filter((step) => step.indexOf('@') !== 0); - if (nonScopeSteps.length <= 1) { return false; } + const nonScopeSteps = steps.filter(step => step.indexOf('@') !== 0) + if (nonScopeSteps.length <= 1) { + return false + } // before trying to resolve, see if the raw import (with relative // segments resolved) matches an allowed pattern - const justSteps = steps.join('/'); - if (reachingAllowed(justSteps) || reachingAllowed(`/${justSteps}`)) { return false; } + const justSteps = steps.join('/') + if (reachingAllowed(justSteps) || reachingAllowed(`/${justSteps}`)) { + return false + } // if the import statement doesn't match directly, try to match the // resolved path if the import is resolvable - const resolved = resolve(importPath, context); - if (!resolved || reachingAllowed(normalizeSep(resolved))) { return false; } + const resolved = resolve(importPath, context) + if (!resolved || reachingAllowed(normalizeSep(resolved))) { + return false + } // this import was not allowed by the allowed paths, and reaches // so it is a violation - return true; + return true } function isForbidViolation(importPath) { - const steps = toSteps(importPath); + const steps = toSteps(importPath) // before trying to resolve, see if the raw import (with relative // segments resolved) matches a forbidden pattern - const justSteps = steps.join('/'); + const justSteps = steps.join('/') - if (reachingForbidden(justSteps) || reachingForbidden(`/${justSteps}`)) { return true; } + if (reachingForbidden(justSteps) || reachingForbidden(`/${justSteps}`)) { + return true + } // if the import statement doesn't match directly, try to match the // resolved path if the import is resolvable - const resolved = resolve(importPath, context); - if (resolved && reachingForbidden(normalizeSep(resolved))) { return true; } + const resolved = resolve(importPath, context) + if (resolved && reachingForbidden(normalizeSep(resolved))) { + return true + } // this import was not forbidden by the forbidden paths so it is not a violation - return false; + return false } // find a directory that is being reached into, but which shouldn't be - const isReachViolation = options.forbid ? isForbidViolation : isAllowViolation; + const isReachViolation = options.forbid + ? isForbidViolation + : isAllowViolation function checkImportForReaching(importPath, node) { - const potentialViolationTypes = ['parent', 'index', 'sibling', 'external', 'internal']; + const potentialViolationTypes = [ + 'parent', + 'index', + 'sibling', + 'external', + 'internal', + ] if ( - potentialViolationTypes.indexOf(importType(importPath, context)) !== -1 - && isReachViolation(importPath) + potentialViolationTypes.indexOf(importType(importPath, context)) !== + -1 && + isReachViolation(importPath) ) { context.report({ node, message: `Reaching to "${importPath}" is not allowed.`, - }); + }) } } return moduleVisitor( - (source) => { - checkImportForReaching(source.value, source); + source => { + checkImportForReaching(source.value, source) }, { commonjs: true }, - ); + ) }, -}; +} diff --git a/src/rules/no-mutable-exports.js b/src/rules/no-mutable-exports.js index 433d64e16..f7c7e4300 100644 --- a/src/rules/no-mutable-exports.js +++ b/src/rules/no-mutable-exports.js @@ -1,4 +1,4 @@ -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' module.exports = { meta: { @@ -13,9 +13,12 @@ module.exports = { create(context) { function checkDeclaration(node) { - const { kind } = node; + const { kind } = node if (kind === 'var' || kind === 'let') { - context.report(node, `Exporting mutable '${kind}' binding, use 'const' instead.`); + context.report( + node, + `Exporting mutable '${kind}' binding, use 'const' instead.`, + ) } } @@ -24,7 +27,7 @@ module.exports = { if (variable.name === name) { for (const def of variable.defs) { if (def.type === 'Variable' && def.parent) { - checkDeclaration(def.parent); + checkDeclaration(def.parent) } } } @@ -32,21 +35,21 @@ module.exports = { } function handleExportDefault(node) { - const scope = context.getScope(); + const scope = context.getScope() if (node.declaration.name) { - checkDeclarationsInScope(scope, node.declaration.name); + checkDeclarationsInScope(scope, node.declaration.name) } } function handleExportNamed(node) { - const scope = context.getScope(); + const scope = context.getScope() - if (node.declaration) { - checkDeclaration(node.declaration); + if (node.declaration) { + checkDeclaration(node.declaration) } else if (!node.source) { for (const specifier of node.specifiers) { - checkDeclarationsInScope(scope, specifier.local.name); + checkDeclarationsInScope(scope, specifier.local.name) } } } @@ -54,6 +57,6 @@ module.exports = { return { ExportDefaultDeclaration: handleExportDefault, ExportNamedDeclaration: handleExportNamed, - }; + } }, -}; +} diff --git a/src/rules/no-named-as-default-member.js b/src/rules/no-named-as-default-member.js index e00a4cbc8..3d4d32a71 100644 --- a/src/rules/no-named-as-default-member.js +++ b/src/rules/no-named-as-default-member.js @@ -4,9 +4,9 @@ * @copyright 2016 Desmond Brand. All rights reserved. * See LICENSE in root directory for full license. */ -import Exports from '../ExportMap'; -import importDeclaration from '../importDeclaration'; -import docsUrl from '../docsUrl'; +import Exports from '../ExportMap' +import importDeclaration from '../importDeclaration' +import docsUrl from '../docsUrl' //------------------------------------------------------------------------------ // Rule Definition @@ -24,68 +24,81 @@ module.exports = { }, create(context) { - const fileImports = new Map(); - const allPropertyLookups = new Map(); + const fileImports = new Map() + const allPropertyLookups = new Map() function storePropertyLookup(objectName, propName, node) { - const lookups = allPropertyLookups.get(objectName) || []; - lookups.push({ node, propName }); - allPropertyLookups.set(objectName, lookups); + const lookups = allPropertyLookups.get(objectName) || [] + lookups.push({ node, propName }) + allPropertyLookups.set(objectName, lookups) } return { ImportDefaultSpecifier(node) { - const declaration = importDeclaration(context); - const exportMap = Exports.get(declaration.source.value, context); - if (exportMap == null) { return; } + const declaration = importDeclaration(context) + const exportMap = Exports.get(declaration.source.value, context) + if (exportMap == null) { + return + } if (exportMap.errors.length) { - exportMap.reportErrors(context, declaration); - return; + exportMap.reportErrors(context, declaration) + return } fileImports.set(node.local.name, { exportMap, sourcePath: declaration.source.value, - }); + }) }, MemberExpression(node) { - const objectName = node.object.name; - const propName = node.property.name; - storePropertyLookup(objectName, propName, node); + const objectName = node.object.name + const propName = node.property.name + storePropertyLookup(objectName, propName, node) }, VariableDeclarator(node) { - const isDestructure = node.id.type === 'ObjectPattern' - && node.init != null - && node.init.type === 'Identifier'; - if (!isDestructure) { return; } + const isDestructure = + node.id.type === 'ObjectPattern' && + node.init != null && + node.init.type === 'Identifier' + if (!isDestructure) { + return + } - const objectName = node.init.name; + const objectName = node.init.name for (const { key } of node.id.properties) { - if (key == null) { continue; } // true for rest properties - storePropertyLookup(objectName, key.name, key); + if (key == null) { + continue + } // true for rest properties + storePropertyLookup(objectName, key.name, key) } }, 'Program:exit'() { allPropertyLookups.forEach((lookups, objectName) => { - const fileImport = fileImports.get(objectName); - if (fileImport == null) { return; } + const fileImport = fileImports.get(objectName) + if (fileImport == null) { + return + } for (const { propName, node } of lookups) { // the default import can have a "default" property - if (propName === 'default') { continue; } - if (!fileImport.exportMap.namespace.has(propName)) { continue; } + if (propName === 'default') { + continue + } + if (!fileImport.exportMap.namespace.has(propName)) { + continue + } context.report({ node, message: `Caution: \`${objectName}\` also has a named export \`${propName}\`. Check if you meant to write \`import {${propName}} from '${fileImport.sourcePath}'\` instead.`, - }); + }) } - }); + }) }, - }; + } }, -}; +} diff --git a/src/rules/no-named-as-default.js b/src/rules/no-named-as-default.js index 40b1e175b..95876496e 100644 --- a/src/rules/no-named-as-default.js +++ b/src/rules/no-named-as-default.js @@ -1,13 +1,14 @@ -import Exports from '../ExportMap'; -import importDeclaration from '../importDeclaration'; -import docsUrl from '../docsUrl'; +import Exports from '../ExportMap' +import importDeclaration from '../importDeclaration' +import docsUrl from '../docsUrl' module.exports = { meta: { type: 'problem', docs: { category: 'Helpful warnings', - description: 'Forbid use of exported name as identifier of default export.', + description: + 'Forbid use of exported name as identifier of default export.', url: docsUrl('no-named-as-default'), }, schema: [], @@ -16,30 +17,35 @@ module.exports = { create(context) { function checkDefault(nameKey, defaultSpecifier) { // #566: default is a valid specifier - if (defaultSpecifier[nameKey].name === 'default') { return; } + if (defaultSpecifier[nameKey].name === 'default') { + return + } - const declaration = importDeclaration(context); + const declaration = importDeclaration(context) - const imports = Exports.get(declaration.source.value, context); - if (imports == null) { return; } + const imports = Exports.get(declaration.source.value, context) + if (imports == null) { + return + } if (imports.errors.length) { - imports.reportErrors(context, declaration); - return; + imports.reportErrors(context, declaration) + return } - if (imports.has('default') && imports.has(defaultSpecifier[nameKey].name)) { - + if ( + imports.has('default') && + imports.has(defaultSpecifier[nameKey].name) + ) { context.report( defaultSpecifier, `Using exported name '${defaultSpecifier[nameKey].name}' as identifier for default export.`, - ); - + ) } } return { ImportDefaultSpecifier: checkDefault.bind(null, 'local'), ExportDefaultSpecifier: checkDefault.bind(null, 'exported'), - }; + } }, -}; +} diff --git a/src/rules/no-named-default.js b/src/rules/no-named-default.js index 1ed0e31df..5948eadd8 100644 --- a/src/rules/no-named-default.js +++ b/src/rules/no-named-default.js @@ -1,4 +1,4 @@ -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' module.exports = { meta: { @@ -16,16 +16,20 @@ module.exports = { ImportDeclaration(node) { node.specifiers.forEach(function (im) { if (im.importKind === 'type' || im.importKind === 'typeof') { - return; + return } - if (im.type === 'ImportSpecifier' && (im.imported.name || im.imported.value) === 'default') { + if ( + im.type === 'ImportSpecifier' && + (im.imported.name || im.imported.value) === 'default' + ) { context.report({ node: im.local, - message: `Use default import syntax to import '${im.local.name}'.` }); + message: `Use default import syntax to import '${im.local.name}'.`, + }) } - }); + }) }, - }; + } }, -}; +} diff --git a/src/rules/no-named-export.js b/src/rules/no-named-export.js index efaf9dc4c..ceb1abfb5 100644 --- a/src/rules/no-named-export.js +++ b/src/rules/no-named-export.js @@ -1,4 +1,4 @@ -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' module.exports = { meta: { @@ -14,26 +14,29 @@ module.exports = { create(context) { // ignore non-modules if (context.parserOptions.sourceType !== 'module') { - return {}; + return {} } - const message = 'Named exports are not allowed.'; + const message = 'Named exports are not allowed.' return { ExportAllDeclaration(node) { - context.report({ node, message }); + context.report({ node, message }) }, ExportNamedDeclaration(node) { if (node.specifiers.length === 0) { - return context.report({ node, message }); + return context.report({ node, message }) } - const someNamed = node.specifiers.some((specifier) => (specifier.exported.name || specifier.exported.value) !== 'default'); + const someNamed = node.specifiers.some( + specifier => + (specifier.exported.name || specifier.exported.value) !== 'default', + ) if (someNamed) { - context.report({ node, message }); + context.report({ node, message }) } }, - }; + } }, -}; +} diff --git a/src/rules/no-namespace.js b/src/rules/no-namespace.js index d3e591876..b988b5175 100644 --- a/src/rules/no-namespace.js +++ b/src/rules/no-namespace.js @@ -3,8 +3,8 @@ * @author Radek Benkel */ -import minimatch from 'minimatch'; -import docsUrl from '../docsUrl'; +import minimatch from 'minimatch' +import docsUrl from '../docsUrl' //------------------------------------------------------------------------------ // Rule Definition @@ -19,106 +19,135 @@ module.exports = { url: docsUrl('no-namespace'), }, fixable: 'code', - schema: [{ - type: 'object', - properties: { - ignore: { - type: 'array', - items: { - type: 'string', + schema: [ + { + type: 'object', + properties: { + ignore: { + type: 'array', + items: { + type: 'string', + }, + uniqueItems: true, }, - uniqueItems: true, }, }, - }], + ], }, create(context) { - const firstOption = context.options[0] || {}; - const ignoreGlobs = firstOption.ignore; + const firstOption = context.options[0] || {} + const ignoreGlobs = firstOption.ignore return { ImportNamespaceSpecifier(node) { - if (ignoreGlobs && ignoreGlobs.find((glob) => minimatch(node.parent.source.value, glob, { matchBase: true }))) { - return; + if ( + ignoreGlobs && + ignoreGlobs.find(glob => + minimatch(node.parent.source.value, glob, { matchBase: true }), + ) + ) { + return } - const scopeVariables = context.getScope().variables; - const namespaceVariable = scopeVariables.find((variable) => variable.defs[0].node === node); - const namespaceReferences = namespaceVariable.references; - const namespaceIdentifiers = namespaceReferences.map((reference) => reference.identifier); - const canFix = namespaceIdentifiers.length > 0 && !usesNamespaceAsObject(namespaceIdentifiers); + const scopeVariables = context.getScope().variables + const namespaceVariable = scopeVariables.find( + variable => variable.defs[0].node === node, + ) + const namespaceReferences = namespaceVariable.references + const namespaceIdentifiers = namespaceReferences.map( + reference => reference.identifier, + ) + const canFix = + namespaceIdentifiers.length > 0 && + !usesNamespaceAsObject(namespaceIdentifiers) context.report({ node, message: `Unexpected namespace import.`, - fix: canFix && ((fixer) => { - const scopeManager = context.getSourceCode().scopeManager; - const fixes = []; - - // Pass 1: Collect variable names that are already in scope for each reference we want - // to transform, so that we can be sure that we choose non-conflicting import names - const importNameConflicts = {}; - namespaceIdentifiers.forEach((identifier) => { - const parent = identifier.parent; - if (parent && parent.type === 'MemberExpression') { - const importName = getMemberPropertyName(parent); - const localConflicts = getVariableNamesInScope(scopeManager, parent); - if (!importNameConflicts[importName]) { - importNameConflicts[importName] = localConflicts; - } else { - localConflicts.forEach((c) => importNameConflicts[importName].add(c)); + fix: + canFix && + (fixer => { + const scopeManager = context.getSourceCode().scopeManager + const fixes = [] + + // Pass 1: Collect variable names that are already in scope for each reference we want + // to transform, so that we can be sure that we choose non-conflicting import names + const importNameConflicts = {} + namespaceIdentifiers.forEach(identifier => { + const parent = identifier.parent + if (parent && parent.type === 'MemberExpression') { + const importName = getMemberPropertyName(parent) + const localConflicts = getVariableNamesInScope( + scopeManager, + parent, + ) + if (!importNameConflicts[importName]) { + importNameConflicts[importName] = localConflicts + } else { + localConflicts.forEach(c => + importNameConflicts[importName].add(c), + ) + } } - } - }); - - // Choose new names for each import - const importNames = Object.keys(importNameConflicts); - const importLocalNames = generateLocalNames( - importNames, - importNameConflicts, - namespaceVariable.name, - ); - - // Replace the ImportNamespaceSpecifier with a list of ImportSpecifiers - const namedImportSpecifiers = importNames.map((importName) => importName === importLocalNames[importName] - ? importName - : `${importName} as ${importLocalNames[importName]}`, - ); - fixes.push(fixer.replaceText(node, `{ ${namedImportSpecifiers.join(', ')} }`)); - - // Pass 2: Replace references to the namespace with references to the named imports - namespaceIdentifiers.forEach((identifier) => { - const parent = identifier.parent; - if (parent && parent.type === 'MemberExpression') { - const importName = getMemberPropertyName(parent); - fixes.push(fixer.replaceText(parent, importLocalNames[importName])); - } - }); - - return fixes; - }), - }); + }) + + // Choose new names for each import + const importNames = Object.keys(importNameConflicts) + const importLocalNames = generateLocalNames( + importNames, + importNameConflicts, + namespaceVariable.name, + ) + + // Replace the ImportNamespaceSpecifier with a list of ImportSpecifiers + const namedImportSpecifiers = importNames.map(importName => + importName === importLocalNames[importName] + ? importName + : `${importName} as ${importLocalNames[importName]}`, + ) + fixes.push( + fixer.replaceText( + node, + `{ ${namedImportSpecifiers.join(', ')} }`, + ), + ) + + // Pass 2: Replace references to the namespace with references to the named imports + namespaceIdentifiers.forEach(identifier => { + const parent = identifier.parent + if (parent && parent.type === 'MemberExpression') { + const importName = getMemberPropertyName(parent) + fixes.push( + fixer.replaceText(parent, importLocalNames[importName]), + ) + } + }) + + return fixes + }), + }) }, - }; + } }, -}; +} /** * @param {Identifier[]} namespaceIdentifiers * @returns {boolean} `true` if the namespace variable is more than just a glorified constant */ function usesNamespaceAsObject(namespaceIdentifiers) { - return !namespaceIdentifiers.every((identifier) => { - const parent = identifier.parent; + return !namespaceIdentifiers.every(identifier => { + const parent = identifier.parent // `namespace.x` or `namespace['x']` return ( - parent - && parent.type === 'MemberExpression' - && (parent.property.type === 'Identifier' || parent.property.type === 'Literal') - ); - }); + parent && + parent.type === 'MemberExpression' && + (parent.property.type === 'Identifier' || + parent.property.type === 'Literal') + ) + }) } /** @@ -128,7 +157,7 @@ function usesNamespaceAsObject(namespaceIdentifiers) { function getMemberPropertyName(memberExpression) { return memberExpression.property.type === 'Identifier' ? memberExpression.property.name - : memberExpression.property.value; + : memberExpression.property.value } /** @@ -137,13 +166,17 @@ function getMemberPropertyName(memberExpression) { * @return {Set} */ function getVariableNamesInScope(scopeManager, node) { - let currentNode = node; - let scope = scopeManager.acquire(currentNode); + let currentNode = node + let scope = scopeManager.acquire(currentNode) while (scope == null) { - currentNode = currentNode.parent; - scope = scopeManager.acquire(currentNode, true); + currentNode = currentNode.parent + scope = scopeManager.acquire(currentNode, true) } - return new Set(scope.variables.concat(scope.upper.variables).map((variable) => variable.name)); + return new Set( + scope.variables + .concat(scope.upper.variables) + .map(variable => variable.name), + ) } /** @@ -153,22 +186,22 @@ function getVariableNamesInScope(scopeManager, node) { * @param {*} namespaceName */ function generateLocalNames(names, nameConflicts, namespaceName) { - const localNames = {}; - names.forEach((name) => { - let localName; + const localNames = {} + names.forEach(name => { + let localName if (!nameConflicts[name].has(name)) { - localName = name; + localName = name } else if (!nameConflicts[name].has(`${namespaceName}_${name}`)) { - localName = `${namespaceName}_${name}`; + localName = `${namespaceName}_${name}` } else { for (let i = 1; i < Infinity; i++) { if (!nameConflicts[name].has(`${namespaceName}_${name}_${i}`)) { - localName = `${namespaceName}_${name}_${i}`; - break; + localName = `${namespaceName}_${name}_${i}` + break } } } - localNames[name] = localName; - }); - return localNames; + localNames[name] = localName + }) + return localNames } diff --git a/src/rules/no-nodejs-modules.js b/src/rules/no-nodejs-modules.js index 82594bb60..c8256b07b 100644 --- a/src/rules/no-nodejs-modules.js +++ b/src/rules/no-nodejs-modules.js @@ -1,10 +1,10 @@ -import importType from '../core/importType'; -import moduleVisitor from 'eslint-module-utils/moduleVisitor'; -import docsUrl from '../docsUrl'; +import importType from '../core/importType' +import moduleVisitor from '../utils/moduleVisitor' +import docsUrl from '../docsUrl' function reportIfMissing(context, node, allowed, name) { if (allowed.indexOf(name) === -1 && importType(name, context) === 'builtin') { - context.report(node, `Do not import Node.js builtin module "${name}"`); + context.report(node, `Do not import Node.js builtin module "${name}"`) } } @@ -34,11 +34,14 @@ module.exports = { }, create(context) { - const options = context.options[0] || {}; - const allowed = options.allow || []; + const options = context.options[0] || {} + const allowed = options.allow || [] - return moduleVisitor((source, node) => { - reportIfMissing(context, node, allowed, source.value); - }, { commonjs: true }); + return moduleVisitor( + (source, node) => { + reportIfMissing(context, node, allowed, source.value) + }, + { commonjs: true }, + ) }, -}; +} diff --git a/src/rules/no-relative-packages.js b/src/rules/no-relative-packages.js index 1d215519f..5cddc0bd7 100644 --- a/src/rules/no-relative-packages.js +++ b/src/rules/no-relative-packages.js @@ -1,55 +1,61 @@ -import path from 'path'; -import readPkgUp from 'eslint-module-utils/readPkgUp'; +import path from 'path' +import readPkgUp from '../utils/readPkgUp' -import resolve from 'eslint-module-utils/resolve'; -import moduleVisitor, { makeOptionsSchema } from 'eslint-module-utils/moduleVisitor'; -import importType from '../core/importType'; -import docsUrl from '../docsUrl'; +import resolve from '../utils/resolve' +import moduleVisitor, { makeOptionsSchema } from '../utils/moduleVisitor' +import importType from '../core/importType' +import docsUrl from '../docsUrl' /** @param {string} filePath */ function toPosixPath(filePath) { - return filePath.replace(/\\/g, '/'); + return filePath.replace(/\\/g, '/') } function findNamedPackage(filePath) { - const found = readPkgUp({ cwd: filePath }); + const found = readPkgUp({ cwd: filePath }) if (found.pkg && !found.pkg.name) { - return findNamedPackage(path.join(found.path, '../..')); + return findNamedPackage(path.join(found.path, '../..')) } - return found; + return found } function checkImportForRelativePackage(context, importPath, node) { - const potentialViolationTypes = ['parent', 'index', 'sibling']; + const potentialViolationTypes = ['parent', 'index', 'sibling'] if (potentialViolationTypes.indexOf(importType(importPath, context)) === -1) { - return; + return } - const resolvedImport = resolve(importPath, context); - const resolvedContext = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); + const resolvedImport = resolve(importPath, context) + const resolvedContext = context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename() if (!resolvedImport || !resolvedContext) { - return; + return } - const importPkg = findNamedPackage(resolvedImport); - const contextPkg = findNamedPackage(resolvedContext); + const importPkg = findNamedPackage(resolvedImport) + const contextPkg = findNamedPackage(resolvedContext) - if (importPkg.pkg && contextPkg.pkg && importPkg.pkg.name !== contextPkg.pkg.name) { - const importBaseName = path.basename(importPath); - const importRoot = path.dirname(importPkg.path); - const properPath = path.relative(importRoot, resolvedImport); + if ( + importPkg.pkg && + contextPkg.pkg && + importPkg.pkg.name !== contextPkg.pkg.name + ) { + const importBaseName = path.basename(importPath) + const importRoot = path.dirname(importPkg.path) + const properPath = path.relative(importRoot, resolvedImport) const properImport = path.join( importPkg.pkg.name, path.dirname(properPath), importBaseName === path.basename(importRoot) ? '' : importBaseName, - ); + ) context.report({ node, message: `Relative import from another package is not allowed. Use \`${properImport}\` instead of \`${importPath}\``, - fix: (fixer) => fixer.replaceText(node, JSON.stringify(toPosixPath(properImport))) - , - }); + fix: fixer => + fixer.replaceText(node, JSON.stringify(toPosixPath(properImport))), + }) } } @@ -66,6 +72,9 @@ module.exports = { }, create(context) { - return moduleVisitor((source) => checkImportForRelativePackage(context, source.value, source), context.options[0]); + return moduleVisitor( + source => checkImportForRelativePackage(context, source.value, source), + context.options[0], + ) }, -}; +} diff --git a/src/rules/no-relative-parent-imports.js b/src/rules/no-relative-parent-imports.js index decd2ef7d..db4f03d82 100644 --- a/src/rules/no-relative-parent-imports.js +++ b/src/rules/no-relative-parent-imports.js @@ -1,9 +1,9 @@ -import moduleVisitor, { makeOptionsSchema } from 'eslint-module-utils/moduleVisitor'; -import docsUrl from '../docsUrl'; -import { basename, dirname, relative } from 'path'; -import resolve from 'eslint-module-utils/resolve'; +import moduleVisitor, { makeOptionsSchema } from '../utils/moduleVisitor' +import docsUrl from '../docsUrl' +import { basename, dirname, relative } from 'path' +import resolve from '../utils/resolve' -import importType from '../core/importType'; +import importType from '../core/importType' module.exports = { meta: { @@ -17,32 +17,38 @@ module.exports = { }, create: function noRelativePackages(context) { - const myPath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); - if (myPath === '') { return {}; } // can't check a non-file + const myPath = context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename() + if (myPath === '') { + return {} + } // can't check a non-file function checkSourceValue(sourceNode) { - const depPath = sourceNode.value; + const depPath = sourceNode.value - if (importType(depPath, context) === 'external') { // ignore packages - return; + if (importType(depPath, context) === 'external') { + // ignore packages + return } - const absDepPath = resolve(depPath, context); + const absDepPath = resolve(depPath, context) - if (!absDepPath) { // unable to resolve path - return; + if (!absDepPath) { + // unable to resolve path + return } - const relDepPath = relative(dirname(myPath), absDepPath); + const relDepPath = relative(dirname(myPath), absDepPath) if (importType(relDepPath, context) === 'parent') { context.report({ node: sourceNode, message: `Relative imports from parent directories are not allowed. Please either pass what you're importing through at runtime (dependency injection), move \`${basename(myPath)}\` to same directory as \`${depPath}\` or consider making \`${depPath}\` a package.`, - }); + }) } } - return moduleVisitor(checkSourceValue, context.options[0]); + return moduleVisitor(checkSourceValue, context.options[0]) }, -}; +} diff --git a/src/rules/no-restricted-paths.js b/src/rules/no-restricted-paths.js index cd680a194..1c1c2f0a1 100644 --- a/src/rules/no-restricted-paths.js +++ b/src/rules/no-restricted-paths.js @@ -1,16 +1,16 @@ -import path from 'path'; +import path from 'path' -import resolve from 'eslint-module-utils/resolve'; -import moduleVisitor from 'eslint-module-utils/moduleVisitor'; -import isGlob from 'is-glob'; -import { Minimatch } from 'minimatch'; -import docsUrl from '../docsUrl'; -import importType from '../core/importType'; +import resolve from '../utils/resolve' +import moduleVisitor from '../utils/moduleVisitor' +import isGlob from 'is-glob' +import { Minimatch } from 'minimatch' +import docsUrl from '../docsUrl' +import importType from '../core/importType' const containsPath = (filepath, target) => { - const relative = path.relative(target, filepath); - return relative === '' || !relative.startsWith('..'); -}; + const relative = path.relative(target, filepath) + return relative === '' || !relative.startsWith('..') +} module.exports = { meta: { @@ -73,54 +73,66 @@ module.exports = { }, create: function noRestrictedPaths(context) { - const options = context.options[0] || {}; - const restrictedPaths = options.zones || []; - const basePath = options.basePath || process.cwd(); - const currentFilename = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); - const matchingZones = restrictedPaths.filter( - (zone) => [].concat(zone.target) - .map((target) => path.resolve(basePath, target)) - .some((targetPath) => isMatchingTargetPath(currentFilename, targetPath)), - ); + const options = context.options[0] || {} + const restrictedPaths = options.zones || [] + const basePath = options.basePath || process.cwd() + const currentFilename = context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename() + const matchingZones = restrictedPaths.filter(zone => + [] + .concat(zone.target) + .map(target => path.resolve(basePath, target)) + .some(targetPath => isMatchingTargetPath(currentFilename, targetPath)), + ) function isMatchingTargetPath(filename, targetPath) { if (isGlob(targetPath)) { - const mm = new Minimatch(targetPath); - return mm.match(filename); + const mm = new Minimatch(targetPath) + return mm.match(filename) } - return containsPath(filename, targetPath); + return containsPath(filename, targetPath) } function isValidExceptionPath(absoluteFromPath, absoluteExceptionPath) { - const relativeExceptionPath = path.relative(absoluteFromPath, absoluteExceptionPath); + const relativeExceptionPath = path.relative( + absoluteFromPath, + absoluteExceptionPath, + ) - return importType(relativeExceptionPath, context) !== 'parent'; + return importType(relativeExceptionPath, context) !== 'parent' } function areBothGlobPatternAndAbsolutePath(areGlobPatterns) { - return areGlobPatterns.some((isGlob) => isGlob) && areGlobPatterns.some((isGlob) => !isGlob); + return ( + areGlobPatterns.some(isGlob => isGlob) && + areGlobPatterns.some(isGlob => !isGlob) + ) } function reportInvalidExceptionPath(node) { context.report({ node, - message: 'Restricted path exceptions must be descendants of the configured `from` path for that zone.', - }); + message: + 'Restricted path exceptions must be descendants of the configured `from` path for that zone.', + }) } function reportInvalidExceptionMixedGlobAndNonGlob(node) { context.report({ node, - message: 'Restricted path `from` must contain either only glob patterns or none', - }); + message: + 'Restricted path `from` must contain either only glob patterns or none', + }) } function reportInvalidExceptionGlob(node) { context.report({ node, - message: 'Restricted path exceptions must be glob patterns when `from` contains glob patterns', - }); + message: + 'Restricted path exceptions must be glob patterns when `from` contains glob patterns', + }) } function computeMixedGlobAndAbsolutePathValidator() { @@ -128,118 +140,147 @@ module.exports = { isPathRestricted: () => true, hasValidExceptions: false, reportInvalidException: reportInvalidExceptionMixedGlobAndNonGlob, - }; + } } function computeGlobPatternPathValidator(absoluteFrom, zoneExcept) { - let isPathException; + let isPathException - const mm = new Minimatch(absoluteFrom); - const isPathRestricted = (absoluteImportPath) => mm.match(absoluteImportPath); - const hasValidExceptions = zoneExcept.every(isGlob); + const mm = new Minimatch(absoluteFrom) + const isPathRestricted = absoluteImportPath => + mm.match(absoluteImportPath) + const hasValidExceptions = zoneExcept.every(isGlob) if (hasValidExceptions) { - const exceptionsMm = zoneExcept.map((except) => new Minimatch(except)); - isPathException = (absoluteImportPath) => exceptionsMm.some((mm) => mm.match(absoluteImportPath)); + const exceptionsMm = zoneExcept.map(except => new Minimatch(except)) + isPathException = absoluteImportPath => + exceptionsMm.some(mm => mm.match(absoluteImportPath)) } - const reportInvalidException = reportInvalidExceptionGlob; + const reportInvalidException = reportInvalidExceptionGlob return { isPathRestricted, hasValidExceptions, isPathException, reportInvalidException, - }; + } } function computeAbsolutePathValidator(absoluteFrom, zoneExcept) { - let isPathException; + let isPathException - const isPathRestricted = (absoluteImportPath) => containsPath(absoluteImportPath, absoluteFrom); + const isPathRestricted = absoluteImportPath => + containsPath(absoluteImportPath, absoluteFrom) - const absoluteExceptionPaths = zoneExcept - .map((exceptionPath) => path.resolve(absoluteFrom, exceptionPath)); - const hasValidExceptions = absoluteExceptionPaths - .every((absoluteExceptionPath) => isValidExceptionPath(absoluteFrom, absoluteExceptionPath)); + const absoluteExceptionPaths = zoneExcept.map(exceptionPath => + path.resolve(absoluteFrom, exceptionPath), + ) + const hasValidExceptions = absoluteExceptionPaths.every( + absoluteExceptionPath => + isValidExceptionPath(absoluteFrom, absoluteExceptionPath), + ) if (hasValidExceptions) { - isPathException = (absoluteImportPath) => absoluteExceptionPaths.some( - (absoluteExceptionPath) => containsPath(absoluteImportPath, absoluteExceptionPath), - ); + isPathException = absoluteImportPath => + absoluteExceptionPaths.some(absoluteExceptionPath => + containsPath(absoluteImportPath, absoluteExceptionPath), + ) } - const reportInvalidException = reportInvalidExceptionPath; + const reportInvalidException = reportInvalidExceptionPath return { isPathRestricted, hasValidExceptions, isPathException, reportInvalidException, - }; + } } function reportInvalidExceptions(validators, node) { - validators.forEach((validator) => validator.reportInvalidException(node)); + validators.forEach(validator => validator.reportInvalidException(node)) } - function reportImportsInRestrictedZone(validators, node, importPath, customMessage) { + function reportImportsInRestrictedZone( + validators, + node, + importPath, + customMessage, + ) { validators.forEach(() => { context.report({ node, message: `Unexpected path "{{importPath}}" imported in restricted zone.${customMessage ? ` ${customMessage}` : ''}`, data: { importPath }, - }); - }); + }) + }) } const makePathValidators = (zoneFrom, zoneExcept = []) => { - const allZoneFrom = [].concat(zoneFrom); - const areGlobPatterns = allZoneFrom.map(isGlob); + const allZoneFrom = [].concat(zoneFrom) + const areGlobPatterns = allZoneFrom.map(isGlob) if (areBothGlobPatternAndAbsolutePath(areGlobPatterns)) { - return [computeMixedGlobAndAbsolutePathValidator()]; + return [computeMixedGlobAndAbsolutePathValidator()] } - const isGlobPattern = areGlobPatterns.every((isGlob) => isGlob); + const isGlobPattern = areGlobPatterns.every(isGlob => isGlob) - return allZoneFrom.map((singleZoneFrom) => { - const absoluteFrom = path.resolve(basePath, singleZoneFrom); + return allZoneFrom.map(singleZoneFrom => { + const absoluteFrom = path.resolve(basePath, singleZoneFrom) if (isGlobPattern) { - return computeGlobPatternPathValidator(absoluteFrom, zoneExcept); + return computeGlobPatternPathValidator(absoluteFrom, zoneExcept) } - return computeAbsolutePathValidator(absoluteFrom, zoneExcept); - }); - }; + return computeAbsolutePathValidator(absoluteFrom, zoneExcept) + }) + } - const validators = []; + const validators = [] function checkForRestrictedImportPath(importPath, node) { - const absoluteImportPath = resolve(importPath, context); + const absoluteImportPath = resolve(importPath, context) if (!absoluteImportPath) { - return; + return } matchingZones.forEach((zone, index) => { if (!validators[index]) { - validators[index] = makePathValidators(zone.from, zone.except); + validators[index] = makePathValidators(zone.from, zone.except) } - const applicableValidatorsForImportPath = validators[index].filter((validator) => validator.isPathRestricted(absoluteImportPath)); - - const validatorsWithInvalidExceptions = applicableValidatorsForImportPath.filter((validator) => !validator.hasValidExceptions); - reportInvalidExceptions(validatorsWithInvalidExceptions, node); - - const applicableValidatorsForImportPathExcludingExceptions = applicableValidatorsForImportPath - .filter((validator) => validator.hasValidExceptions && !validator.isPathException(absoluteImportPath)); - reportImportsInRestrictedZone(applicableValidatorsForImportPathExcludingExceptions, node, importPath, zone.message); - }); + const applicableValidatorsForImportPath = validators[index].filter( + validator => validator.isPathRestricted(absoluteImportPath), + ) + + const validatorsWithInvalidExceptions = + applicableValidatorsForImportPath.filter( + validator => !validator.hasValidExceptions, + ) + reportInvalidExceptions(validatorsWithInvalidExceptions, node) + + const applicableValidatorsForImportPathExcludingExceptions = + applicableValidatorsForImportPath.filter( + validator => + validator.hasValidExceptions && + !validator.isPathException(absoluteImportPath), + ) + reportImportsInRestrictedZone( + applicableValidatorsForImportPathExcludingExceptions, + node, + importPath, + zone.message, + ) + }) } - return moduleVisitor((source) => { - checkForRestrictedImportPath(source.value, source); - }, { commonjs: true }); + return moduleVisitor( + source => { + checkForRestrictedImportPath(source.value, source) + }, + { commonjs: true }, + ) }, -}; +} diff --git a/src/rules/no-self-import.js b/src/rules/no-self-import.js index 0ba0f6669..9178359a5 100644 --- a/src/rules/no-self-import.js +++ b/src/rules/no-self-import.js @@ -3,19 +3,21 @@ * @author Gio d'Amelio */ -import resolve from 'eslint-module-utils/resolve'; -import moduleVisitor from 'eslint-module-utils/moduleVisitor'; -import docsUrl from '../docsUrl'; +import resolve from '../utils/resolve' +import moduleVisitor from '../utils/moduleVisitor' +import docsUrl from '../docsUrl' function isImportingSelf(context, node, requireName) { - const filePath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); + const filePath = context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename() // If the input is from stdin, this test can't fail if (filePath !== '' && filePath === resolve(requireName, context)) { context.report({ node, message: 'Module imports itself.', - }); + }) } } @@ -32,8 +34,11 @@ module.exports = { schema: [], }, create(context) { - return moduleVisitor((source, node) => { - isImportingSelf(context, node, source.value); - }, { commonjs: true }); + return moduleVisitor( + (source, node) => { + isImportingSelf(context, node, source.value) + }, + { commonjs: true }, + ) }, -}; +} diff --git a/src/rules/no-unassigned-import.js b/src/rules/no-unassigned-import.js index 0af9f2e9f..216ca3feb 100644 --- a/src/rules/no-unassigned-import.js +++ b/src/rules/no-unassigned-import.js @@ -1,55 +1,62 @@ -import path from 'path'; -import minimatch from 'minimatch'; +import path from 'path' +import minimatch from 'minimatch' -import isStaticRequire from '../core/staticRequire'; -import docsUrl from '../docsUrl'; +import isStaticRequire from '../core/staticRequire' +import docsUrl from '../docsUrl' function report(context, node) { context.report({ node, message: 'Imported module should be assigned', - }); + }) } function testIsAllow(globs, filename, source) { if (!Array.isArray(globs)) { - return false; // default doesn't allow any patterns + return false // default doesn't allow any patterns } - let filePath; + let filePath - if (source[0] !== '.' && source[0] !== '/') { // a node module - filePath = source; + if (source[0] !== '.' && source[0] !== '/') { + // a node module + filePath = source } else { - filePath = path.resolve(path.dirname(filename), source); // get source absolute path + filePath = path.resolve(path.dirname(filename), source) // get source absolute path } - return globs.find((glob) => minimatch(filePath, glob) - || minimatch(filePath, path.join(process.cwd(), glob)), - ) !== undefined; + return ( + globs.find( + glob => + minimatch(filePath, glob) || + minimatch(filePath, path.join(process.cwd(), glob)), + ) !== undefined + ) } function create(context) { - const options = context.options[0] || {}; - const filename = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); - const isAllow = (source) => testIsAllow(options.allow, filename, source); + const options = context.options[0] || {} + const filename = context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename() + const isAllow = source => testIsAllow(options.allow, filename, source) return { ImportDeclaration(node) { if (node.specifiers.length === 0 && !isAllow(node.source.value)) { - report(context, node); + report(context, node) } }, ExpressionStatement(node) { if ( - node.expression.type === 'CallExpression' - && isStaticRequire(node.expression) - && !isAllow(node.expression.arguments[0].value) + node.expression.type === 'CallExpression' && + isStaticRequire(node.expression) && + !isAllow(node.expression.arguments[0].value) ) { - report(context, node.expression); + report(context, node.expression) } }, - }; + } } module.exports = { @@ -79,4 +86,4 @@ module.exports = { }, ], }, -}; +} diff --git a/src/rules/no-unresolved.js b/src/rules/no-unresolved.js index 8216cdf1f..ba11ecf13 100644 --- a/src/rules/no-unresolved.js +++ b/src/rules/no-unresolved.js @@ -3,17 +3,21 @@ * @author Ben Mosher */ -import resolve, { CASE_SENSITIVE_FS, fileExistsWithCaseSync } from 'eslint-module-utils/resolve'; -import ModuleCache from 'eslint-module-utils/ModuleCache'; -import moduleVisitor, { makeOptionsSchema } from 'eslint-module-utils/moduleVisitor'; -import docsUrl from '../docsUrl'; +import resolve, { + CASE_SENSITIVE_FS, + fileExistsWithCaseSync, +} from '../utils/resolve' +import ModuleCache from '../utils/ModuleCache' +import moduleVisitor, { makeOptionsSchema } from '../utils/moduleVisitor' +import docsUrl from '../docsUrl' module.exports = { meta: { type: 'problem', docs: { category: 'Static analysis', - description: 'Ensure imports point to a file/module that can be resolved.', + description: + 'Ensure imports point to a file/module that can be resolved.', url: docsUrl('no-unresolved'), }, @@ -26,35 +30,43 @@ module.exports = { }, create(context) { - const options = context.options[0] || {}; + const options = context.options[0] || {} function checkSourceValue(source, node) { // ignore type-only imports and exports if (node.importKind === 'type' || node.exportKind === 'type') { - return; + return } - const caseSensitive = !CASE_SENSITIVE_FS && options.caseSensitive !== false; - const caseSensitiveStrict = !CASE_SENSITIVE_FS && options.caseSensitiveStrict; + const caseSensitive = + !CASE_SENSITIVE_FS && options.caseSensitive !== false + const caseSensitiveStrict = + !CASE_SENSITIVE_FS && options.caseSensitiveStrict - const resolvedPath = resolve(source.value, context); + const resolvedPath = resolve(source.value, context) if (resolvedPath === undefined) { context.report( source, `Unable to resolve path to module '${source.value}'.`, - ); + ) } else if (caseSensitive || caseSensitiveStrict) { - const cacheSettings = ModuleCache.getSettings(context.settings); - if (!fileExistsWithCaseSync(resolvedPath, cacheSettings, caseSensitiveStrict)) { + const cacheSettings = ModuleCache.getSettings(context.settings) + if ( + !fileExistsWithCaseSync( + resolvedPath, + cacheSettings, + caseSensitiveStrict, + ) + ) { context.report( source, `Casing of ${source.value} does not match the underlying filesystem.`, - ); + ) } } } - return moduleVisitor(checkSourceValue, options); + return moduleVisitor(checkSourceValue, options) }, -}; +} diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index ec3425dac..2f2fbfea6 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -4,31 +4,33 @@ * @author René Fermann */ -import { getFileExtensions } from 'eslint-module-utils/ignore'; -import resolve from 'eslint-module-utils/resolve'; -import visit from 'eslint-module-utils/visit'; -import { dirname, join } from 'path'; -import readPkgUp from 'eslint-module-utils/readPkgUp'; -import values from 'object.values'; -import includes from 'array-includes'; -import flatMap from 'array.prototype.flatmap'; +import { getFileExtensions } from '../utils/ignore' +import resolve from '../utils/resolve' +import visit from '../utils/visit' +import { dirname, join } from 'path' +import readPkgUp from '../utils/readPkgUp' +import values from 'object.values' +import includes from 'array-includes' +import flatMap from 'array.prototype.flatmap' -import Exports, { recursivePatternCapture } from '../ExportMap'; -import docsUrl from '../docsUrl'; +import Exports, { recursivePatternCapture } from '../ExportMap' +import docsUrl from '../docsUrl' -let FileEnumerator; -let listFilesToProcess; +let FileEnumerator +let listFilesToProcess try { - ({ FileEnumerator } = require('eslint/use-at-your-own-risk')); + ;({ FileEnumerator } = require('eslint/use-at-your-own-risk')) } catch (e) { try { // has been moved to eslint/lib/cli-engine/file-enumerator in version 6 - ({ FileEnumerator } = require('eslint/lib/cli-engine/file-enumerator')); + ;({ FileEnumerator } = require('eslint/lib/cli-engine/file-enumerator')) } catch (e) { try { // eslint/lib/util/glob-util has been moved to eslint/lib/util/glob-utils with version 5.3 - const { listFilesToProcess: originalListFilesToProcess } = require('eslint/lib/util/glob-utils'); + const { + listFilesToProcess: originalListFilesToProcess, + } = require('eslint/lib/util/glob-utils') // Prevent passing invalid options (extensions array) to old versions of the function. // https://github.com/eslint/eslint/blob/v5.16.0/lib/util/glob-utils.js#L178-L280 @@ -36,16 +38,26 @@ try { listFilesToProcess = function (src, extensions) { return originalListFilesToProcess(src, { extensions, - }); - }; + }) + } } catch (e) { - const { listFilesToProcess: originalListFilesToProcess } = require('eslint/lib/util/glob-util'); + const { + listFilesToProcess: originalListFilesToProcess, + } = require('eslint/lib/util/glob-util') listFilesToProcess = function (src, extensions) { - const patterns = src.concat(flatMap(src, (pattern) => extensions.map((extension) => (/\*\*|\*\./).test(pattern) ? pattern : `${pattern}/**/*${extension}`))); - - return originalListFilesToProcess(patterns); - }; + const patterns = src.concat( + flatMap(src, pattern => + extensions.map(extension => + /\*\*|\*\./.test(pattern) + ? pattern + : `${pattern}/**/*${extension}`, + ), + ), + ) + + return originalListFilesToProcess(patterns) + } } } } @@ -54,58 +66,58 @@ if (FileEnumerator) { listFilesToProcess = function (src, extensions) { const e = new FileEnumerator({ extensions, - }); + }) return Array.from(e.iterateFiles(src), ({ filePath, ignored }) => ({ ignored, filename: filePath, - })); - }; + })) + } } -const EXPORT_DEFAULT_DECLARATION = 'ExportDefaultDeclaration'; -const EXPORT_NAMED_DECLARATION = 'ExportNamedDeclaration'; -const EXPORT_ALL_DECLARATION = 'ExportAllDeclaration'; -const IMPORT_DECLARATION = 'ImportDeclaration'; -const IMPORT_NAMESPACE_SPECIFIER = 'ImportNamespaceSpecifier'; -const IMPORT_DEFAULT_SPECIFIER = 'ImportDefaultSpecifier'; -const VARIABLE_DECLARATION = 'VariableDeclaration'; -const FUNCTION_DECLARATION = 'FunctionDeclaration'; -const CLASS_DECLARATION = 'ClassDeclaration'; -const IDENTIFIER = 'Identifier'; -const OBJECT_PATTERN = 'ObjectPattern'; -const ARRAY_PATTERN = 'ArrayPattern'; -const TS_INTERFACE_DECLARATION = 'TSInterfaceDeclaration'; -const TS_TYPE_ALIAS_DECLARATION = 'TSTypeAliasDeclaration'; -const TS_ENUM_DECLARATION = 'TSEnumDeclaration'; -const DEFAULT = 'default'; +const EXPORT_DEFAULT_DECLARATION = 'ExportDefaultDeclaration' +const EXPORT_NAMED_DECLARATION = 'ExportNamedDeclaration' +const EXPORT_ALL_DECLARATION = 'ExportAllDeclaration' +const IMPORT_DECLARATION = 'ImportDeclaration' +const IMPORT_NAMESPACE_SPECIFIER = 'ImportNamespaceSpecifier' +const IMPORT_DEFAULT_SPECIFIER = 'ImportDefaultSpecifier' +const VARIABLE_DECLARATION = 'VariableDeclaration' +const FUNCTION_DECLARATION = 'FunctionDeclaration' +const CLASS_DECLARATION = 'ClassDeclaration' +const IDENTIFIER = 'Identifier' +const OBJECT_PATTERN = 'ObjectPattern' +const ARRAY_PATTERN = 'ArrayPattern' +const TS_INTERFACE_DECLARATION = 'TSInterfaceDeclaration' +const TS_TYPE_ALIAS_DECLARATION = 'TSTypeAliasDeclaration' +const TS_ENUM_DECLARATION = 'TSEnumDeclaration' +const DEFAULT = 'default' function forEachDeclarationIdentifier(declaration, cb) { if (declaration) { if ( - declaration.type === FUNCTION_DECLARATION - || declaration.type === CLASS_DECLARATION - || declaration.type === TS_INTERFACE_DECLARATION - || declaration.type === TS_TYPE_ALIAS_DECLARATION - || declaration.type === TS_ENUM_DECLARATION + declaration.type === FUNCTION_DECLARATION || + declaration.type === CLASS_DECLARATION || + declaration.type === TS_INTERFACE_DECLARATION || + declaration.type === TS_TYPE_ALIAS_DECLARATION || + declaration.type === TS_ENUM_DECLARATION ) { - cb(declaration.id.name); + cb(declaration.id.name) } else if (declaration.type === VARIABLE_DECLARATION) { declaration.declarations.forEach(({ id }) => { if (id.type === OBJECT_PATTERN) { - recursivePatternCapture(id, (pattern) => { + recursivePatternCapture(id, pattern => { if (pattern.type === IDENTIFIER) { - cb(pattern.name); + cb(pattern.name) } - }); + }) } else if (id.type === ARRAY_PATTERN) { id.elements.forEach(({ name }) => { - cb(name); - }); + cb(name) + }) } else { - cb(id.name); + cb(id.name) } - }); + }) } } } @@ -129,7 +141,7 @@ function forEachDeclarationIdentifier(declaration, cb) { * * @type {Map>>} */ -const importList = new Map(); +const importList = new Map() /** * List of exports per file. @@ -156,14 +168,14 @@ const importList = new Map(); * * @type {Map>} */ -const exportList = new Map(); +const exportList = new Map() -const visitorKeyMap = new Map(); +const visitorKeyMap = new Map() -const ignoredFiles = new Set(); -const filesOutsideSrc = new Set(); +const ignoredFiles = new Set() +const filesOutsideSrc = new Set() -const isNodeModule = (path) => (/\/(node_modules)\//).test(path); +const isNodeModule = path => /\/(node_modules)\//.test(path) /** * read all files matching the patterns in src and ignoreExports @@ -171,30 +183,32 @@ const isNodeModule = (path) => (/\/(node_modules)\//).test(path); * return all files matching src pattern, which are not matching the ignoreExports pattern */ const resolveFiles = (src, ignoreExports, context) => { - const extensions = Array.from(getFileExtensions(context.settings)); + const extensions = Array.from(getFileExtensions(context.settings)) - const srcFileList = listFilesToProcess(src, extensions); + const srcFileList = listFilesToProcess(src, extensions) // prepare list of ignored files - const ignoredFilesList = listFilesToProcess(ignoreExports, extensions); - ignoredFilesList.forEach(({ filename }) => ignoredFiles.add(filename)); + const ignoredFilesList = listFilesToProcess(ignoreExports, extensions) + ignoredFilesList.forEach(({ filename }) => ignoredFiles.add(filename)) // prepare list of source files, don't consider files from node_modules return new Set( - flatMap(srcFileList, ({ filename }) => isNodeModule(filename) ? [] : filename), - ); -}; + flatMap(srcFileList, ({ filename }) => + isNodeModule(filename) ? [] : filename, + ), + ) +} /** * parse all source files and build up 2 maps containing the existing imports and exports */ const prepareImportsAndExports = (srcFiles, context) => { - const exportAll = new Map(); - srcFiles.forEach((file) => { - const exports = new Map(); - const imports = new Map(); - const currentExports = Exports.get(file, context); + const exportAll = new Map() + srcFiles.forEach(file => { + const exports = new Map() + const imports = new Map() + const currentExports = Exports.get(file, context) if (currentExports) { const { dependencies, @@ -202,86 +216,86 @@ const prepareImportsAndExports = (srcFiles, context) => { imports: localImportList, namespace, visitorKeys, - } = currentExports; + } = currentExports - visitorKeyMap.set(file, visitorKeys); + visitorKeyMap.set(file, visitorKeys) // dependencies === export * from - const currentExportAll = new Set(); - dependencies.forEach((getDependency) => { - const dependency = getDependency(); + const currentExportAll = new Set() + dependencies.forEach(getDependency => { + const dependency = getDependency() if (dependency === null) { - return; + return } - currentExportAll.add(dependency.path); - }); - exportAll.set(file, currentExportAll); + currentExportAll.add(dependency.path) + }) + exportAll.set(file, currentExportAll) reexports.forEach((value, key) => { if (key === DEFAULT) { - exports.set(IMPORT_DEFAULT_SPECIFIER, { whereUsed: new Set() }); + exports.set(IMPORT_DEFAULT_SPECIFIER, { whereUsed: new Set() }) } else { - exports.set(key, { whereUsed: new Set() }); + exports.set(key, { whereUsed: new Set() }) } - const reexport = value.getImport(); + const reexport = value.getImport() if (!reexport) { - return; + return } - let localImport = imports.get(reexport.path); - let currentValue; + let localImport = imports.get(reexport.path) + let currentValue if (value.local === DEFAULT) { - currentValue = IMPORT_DEFAULT_SPECIFIER; + currentValue = IMPORT_DEFAULT_SPECIFIER } else { - currentValue = value.local; + currentValue = value.local } if (typeof localImport !== 'undefined') { - localImport = new Set([...localImport, currentValue]); + localImport = new Set([...localImport, currentValue]) } else { - localImport = new Set([currentValue]); + localImport = new Set([currentValue]) } - imports.set(reexport.path, localImport); - }); + imports.set(reexport.path, localImport) + }) localImportList.forEach((value, key) => { if (isNodeModule(key)) { - return; + return } - const localImport = imports.get(key) || new Set(); + const localImport = imports.get(key) || new Set() value.declarations.forEach(({ importedSpecifiers }) => { - importedSpecifiers.forEach((specifier) => { - localImport.add(specifier); - }); - }); - imports.set(key, localImport); - }); - importList.set(file, imports); + importedSpecifiers.forEach(specifier => { + localImport.add(specifier) + }) + }) + imports.set(key, localImport) + }) + importList.set(file, imports) // build up export list only, if file is not ignored if (ignoredFiles.has(file)) { - return; + return } namespace.forEach((value, key) => { if (key === DEFAULT) { - exports.set(IMPORT_DEFAULT_SPECIFIER, { whereUsed: new Set() }); + exports.set(IMPORT_DEFAULT_SPECIFIER, { whereUsed: new Set() }) } else { - exports.set(key, { whereUsed: new Set() }); + exports.set(key, { whereUsed: new Set() }) } - }); + }) } - exports.set(EXPORT_ALL_DECLARATION, { whereUsed: new Set() }); - exports.set(IMPORT_NAMESPACE_SPECIFIER, { whereUsed: new Set() }); - exportList.set(file, exports); - }); + exports.set(EXPORT_ALL_DECLARATION, { whereUsed: new Set() }) + exports.set(IMPORT_NAMESPACE_SPECIFIER, { whereUsed: new Set() }) + exportList.set(file, exports) + }) exportAll.forEach((value, key) => { - value.forEach((val) => { - const currentExports = exportList.get(val); + value.forEach(val => { + const currentExports = exportList.get(val) if (currentExports) { - const currentExport = currentExports.get(EXPORT_ALL_DECLARATION); - currentExport.whereUsed.add(key); + const currentExport = currentExports.get(EXPORT_ALL_DECLARATION) + currentExport.whereUsed.add(key) } - }); - }); -}; + }) + }) +} /** * traverse through all imports and add the respective path to the whereUsed-list @@ -290,177 +304,185 @@ const prepareImportsAndExports = (srcFiles, context) => { const determineUsage = () => { importList.forEach((listValue, listKey) => { listValue.forEach((value, key) => { - const exports = exportList.get(key); + const exports = exportList.get(key) if (typeof exports !== 'undefined') { - value.forEach((currentImport) => { - let specifier; + value.forEach(currentImport => { + let specifier if (currentImport === IMPORT_NAMESPACE_SPECIFIER) { - specifier = IMPORT_NAMESPACE_SPECIFIER; + specifier = IMPORT_NAMESPACE_SPECIFIER } else if (currentImport === IMPORT_DEFAULT_SPECIFIER) { - specifier = IMPORT_DEFAULT_SPECIFIER; + specifier = IMPORT_DEFAULT_SPECIFIER } else { - specifier = currentImport; + specifier = currentImport } if (typeof specifier !== 'undefined') { - const exportStatement = exports.get(specifier); + const exportStatement = exports.get(specifier) if (typeof exportStatement !== 'undefined') { - const { whereUsed } = exportStatement; - whereUsed.add(listKey); - exports.set(specifier, { whereUsed }); + const { whereUsed } = exportStatement + whereUsed.add(listKey) + exports.set(specifier, { whereUsed }) } } - }); + }) } - }); - }); -}; + }) + }) +} -const getSrc = (src) => { +const getSrc = src => { if (src) { - return src; + return src } - return [process.cwd()]; -}; + return [process.cwd()] +} /** * prepare the lists of existing imports and exports - should only be executed once at * the start of a new eslint run */ -let srcFiles; -let lastPrepareKey; +let srcFiles +let lastPrepareKey const doPreparation = (src, ignoreExports, context) => { const prepareKey = JSON.stringify({ src: (src || []).sort(), ignoreExports: (ignoreExports || []).sort(), extensions: Array.from(getFileExtensions(context.settings)).sort(), - }); + }) if (prepareKey === lastPrepareKey) { - return; + return } - importList.clear(); - exportList.clear(); - ignoredFiles.clear(); - filesOutsideSrc.clear(); + importList.clear() + exportList.clear() + ignoredFiles.clear() + filesOutsideSrc.clear() - srcFiles = resolveFiles(getSrc(src), ignoreExports, context); - prepareImportsAndExports(srcFiles, context); - determineUsage(); - lastPrepareKey = prepareKey; -}; + srcFiles = resolveFiles(getSrc(src), ignoreExports, context) + prepareImportsAndExports(srcFiles, context) + determineUsage() + lastPrepareKey = prepareKey +} -const newNamespaceImportExists = (specifiers) => specifiers.some(({ type }) => type === IMPORT_NAMESPACE_SPECIFIER); +const newNamespaceImportExists = specifiers => + specifiers.some(({ type }) => type === IMPORT_NAMESPACE_SPECIFIER) -const newDefaultImportExists = (specifiers) => specifiers.some(({ type }) => type === IMPORT_DEFAULT_SPECIFIER); +const newDefaultImportExists = specifiers => + specifiers.some(({ type }) => type === IMPORT_DEFAULT_SPECIFIER) -const fileIsInPkg = (file) => { - const { path, pkg } = readPkgUp({ cwd: file }); - const basePath = dirname(path); +const fileIsInPkg = file => { + const { path, pkg } = readPkgUp({ cwd: file }) + const basePath = dirname(path) - const checkPkgFieldString = (pkgField) => { + const checkPkgFieldString = pkgField => { if (join(basePath, pkgField) === file) { - return true; + return true } - }; + } - const checkPkgFieldObject = (pkgField) => { - const pkgFieldFiles = flatMap(values(pkgField), (value) => typeof value === 'boolean' ? [] : join(basePath, value)); + const checkPkgFieldObject = pkgField => { + const pkgFieldFiles = flatMap(values(pkgField), value => + typeof value === 'boolean' ? [] : join(basePath, value), + ) if (includes(pkgFieldFiles, file)) { - return true; + return true } - }; + } - const checkPkgField = (pkgField) => { + const checkPkgField = pkgField => { if (typeof pkgField === 'string') { - return checkPkgFieldString(pkgField); + return checkPkgFieldString(pkgField) } if (typeof pkgField === 'object') { - return checkPkgFieldObject(pkgField); + return checkPkgFieldObject(pkgField) } - }; + } if (pkg.private === true) { - return false; + return false } if (pkg.bin) { if (checkPkgField(pkg.bin)) { - return true; + return true } } if (pkg.browser) { if (checkPkgField(pkg.browser)) { - return true; + return true } } if (pkg.main) { if (checkPkgFieldString(pkg.main)) { - return true; + return true } } - return false; -}; + return false +} module.exports = { meta: { type: 'suggestion', docs: { category: 'Helpful warnings', - description: 'Forbid modules without exports, or exports without matching import in another module.', + description: + 'Forbid modules without exports, or exports without matching import in another module.', url: docsUrl('no-unused-modules'), }, - schema: [{ - properties: { - src: { - description: 'files/paths to be analyzed (only for unused exports)', - type: 'array', - uniqueItems: true, - items: { - type: 'string', - minLength: 1, + schema: [ + { + properties: { + src: { + description: 'files/paths to be analyzed (only for unused exports)', + type: 'array', + uniqueItems: true, + items: { + type: 'string', + minLength: 1, + }, }, - }, - ignoreExports: { - description: 'files/paths for which unused exports will not be reported (e.g module entry points)', - type: 'array', - uniqueItems: true, - items: { - type: 'string', - minLength: 1, + ignoreExports: { + description: + 'files/paths for which unused exports will not be reported (e.g module entry points)', + type: 'array', + uniqueItems: true, + items: { + type: 'string', + minLength: 1, + }, + }, + missingExports: { + description: 'report modules without any exports', + type: 'boolean', + }, + unusedExports: { + description: 'report exports without any usage', + type: 'boolean', }, }, - missingExports: { - description: 'report modules without any exports', - type: 'boolean', - }, - unusedExports: { - description: 'report exports without any usage', - type: 'boolean', - }, - }, - anyOf: [ - { - properties: { - unusedExports: { enum: [true] }, - src: { - minItems: 1, + anyOf: [ + { + properties: { + unusedExports: { enum: [true] }, + src: { + minItems: 1, + }, }, + required: ['unusedExports'], }, - required: ['unusedExports'], - }, - { - properties: { - missingExports: { enum: [true] }, + { + properties: { + missingExports: { enum: [true] }, + }, + required: ['missingExports'], }, - required: ['missingExports'], - }, - ], - }], + ], + }, + ], }, create(context) { @@ -469,481 +491,511 @@ module.exports = { ignoreExports = [], missingExports, unusedExports, - } = context.options[0] || {}; + } = context.options[0] || {} if (unusedExports) { - doPreparation(src, ignoreExports, context); + doPreparation(src, ignoreExports, context) } - const file = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); + const file = context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename() - const checkExportPresence = (node) => { + const checkExportPresence = node => { if (!missingExports) { - return; + return } if (ignoredFiles.has(file)) { - return; + return } - const exportCount = exportList.get(file); - const exportAll = exportCount.get(EXPORT_ALL_DECLARATION); - const namespaceImports = exportCount.get(IMPORT_NAMESPACE_SPECIFIER); + const exportCount = exportList.get(file) + const exportAll = exportCount.get(EXPORT_ALL_DECLARATION) + const namespaceImports = exportCount.get(IMPORT_NAMESPACE_SPECIFIER) - exportCount.delete(EXPORT_ALL_DECLARATION); - exportCount.delete(IMPORT_NAMESPACE_SPECIFIER); + exportCount.delete(EXPORT_ALL_DECLARATION) + exportCount.delete(IMPORT_NAMESPACE_SPECIFIER) if (exportCount.size < 1) { // node.body[0] === 'undefined' only happens, if everything is commented out in the file // being linted - context.report(node.body[0] ? node.body[0] : node, 'No exports found'); + context.report(node.body[0] ? node.body[0] : node, 'No exports found') } - exportCount.set(EXPORT_ALL_DECLARATION, exportAll); - exportCount.set(IMPORT_NAMESPACE_SPECIFIER, namespaceImports); - }; + exportCount.set(EXPORT_ALL_DECLARATION, exportAll) + exportCount.set(IMPORT_NAMESPACE_SPECIFIER, namespaceImports) + } const checkUsage = (node, exportedValue) => { if (!unusedExports) { - return; + return } if (ignoredFiles.has(file)) { - return; + return } if (fileIsInPkg(file)) { - return; + return } if (filesOutsideSrc.has(file)) { - return; + return } // make sure file to be linted is included in source files if (!srcFiles.has(file)) { - srcFiles = resolveFiles(getSrc(src), ignoreExports, context); + srcFiles = resolveFiles(getSrc(src), ignoreExports, context) if (!srcFiles.has(file)) { - filesOutsideSrc.add(file); - return; + filesOutsideSrc.add(file) + return } } - exports = exportList.get(file); + exports = exportList.get(file) if (!exports) { - console.error(`file \`${file}\` has no exports. Please update to the latest, and if it still happens, report this on https://github.com/import-js/eslint-plugin-import/issues/2866!`); + console.error( + `file \`${file}\` has no exports. Please update to the latest, and if it still happens, report this on https://github.com/import-js/eslint-plugin-import/issues/2866!`, + ) } // special case: export * from - const exportAll = exports.get(EXPORT_ALL_DECLARATION); - if (typeof exportAll !== 'undefined' && exportedValue !== IMPORT_DEFAULT_SPECIFIER) { + const exportAll = exports.get(EXPORT_ALL_DECLARATION) + if ( + typeof exportAll !== 'undefined' && + exportedValue !== IMPORT_DEFAULT_SPECIFIER + ) { if (exportAll.whereUsed.size > 0) { - return; + return } } // special case: namespace import - const namespaceImports = exports.get(IMPORT_NAMESPACE_SPECIFIER); + const namespaceImports = exports.get(IMPORT_NAMESPACE_SPECIFIER) if (typeof namespaceImports !== 'undefined') { if (namespaceImports.whereUsed.size > 0) { - return; + return } } // exportsList will always map any imported value of 'default' to 'ImportDefaultSpecifier' - const exportsKey = exportedValue === DEFAULT ? IMPORT_DEFAULT_SPECIFIER : exportedValue; + const exportsKey = + exportedValue === DEFAULT ? IMPORT_DEFAULT_SPECIFIER : exportedValue - const exportStatement = exports.get(exportsKey); + const exportStatement = exports.get(exportsKey) - const value = exportsKey === IMPORT_DEFAULT_SPECIFIER ? DEFAULT : exportsKey; + const value = + exportsKey === IMPORT_DEFAULT_SPECIFIER ? DEFAULT : exportsKey if (typeof exportStatement !== 'undefined') { if (exportStatement.whereUsed.size < 1) { context.report( node, `exported declaration '${value}' not used within other modules`, - ); + ) } } else { context.report( node, `exported declaration '${value}' not used within other modules`, - ); + ) } - }; + } /** * only useful for tools like vscode-eslint * * update lists of existing exports during runtime */ - const updateExportUsage = (node) => { + const updateExportUsage = node => { if (ignoredFiles.has(file)) { - return; + return } - let exports = exportList.get(file); + let exports = exportList.get(file) // new module has been created during runtime // include it in further processing if (typeof exports === 'undefined') { - exports = new Map(); + exports = new Map() } - const newExports = new Map(); - const newExportIdentifiers = new Set(); + const newExports = new Map() + const newExportIdentifiers = new Set() node.body.forEach(({ type, declaration, specifiers }) => { if (type === EXPORT_DEFAULT_DECLARATION) { - newExportIdentifiers.add(IMPORT_DEFAULT_SPECIFIER); + newExportIdentifiers.add(IMPORT_DEFAULT_SPECIFIER) } if (type === EXPORT_NAMED_DECLARATION) { if (specifiers.length > 0) { - specifiers.forEach((specifier) => { + specifiers.forEach(specifier => { if (specifier.exported) { - newExportIdentifiers.add(specifier.exported.name || specifier.exported.value); + newExportIdentifiers.add( + specifier.exported.name || specifier.exported.value, + ) } - }); + }) } - forEachDeclarationIdentifier(declaration, (name) => { - newExportIdentifiers.add(name); - }); + forEachDeclarationIdentifier(declaration, name => { + newExportIdentifiers.add(name) + }) } - }); + }) // old exports exist within list of new exports identifiers: add to map of new exports exports.forEach((value, key) => { if (newExportIdentifiers.has(key)) { - newExports.set(key, value); + newExports.set(key, value) } - }); + }) // new export identifiers added: add to map of new exports - newExportIdentifiers.forEach((key) => { + newExportIdentifiers.forEach(key => { if (!exports.has(key)) { - newExports.set(key, { whereUsed: new Set() }); + newExports.set(key, { whereUsed: new Set() }) } - }); + }) // preserve information about namespace imports - const exportAll = exports.get(EXPORT_ALL_DECLARATION); - let namespaceImports = exports.get(IMPORT_NAMESPACE_SPECIFIER); + const exportAll = exports.get(EXPORT_ALL_DECLARATION) + let namespaceImports = exports.get(IMPORT_NAMESPACE_SPECIFIER) if (typeof namespaceImports === 'undefined') { - namespaceImports = { whereUsed: new Set() }; + namespaceImports = { whereUsed: new Set() } } - newExports.set(EXPORT_ALL_DECLARATION, exportAll); - newExports.set(IMPORT_NAMESPACE_SPECIFIER, namespaceImports); - exportList.set(file, newExports); - }; + newExports.set(EXPORT_ALL_DECLARATION, exportAll) + newExports.set(IMPORT_NAMESPACE_SPECIFIER, namespaceImports) + exportList.set(file, newExports) + } /** * only useful for tools like vscode-eslint * * update lists of existing imports during runtime */ - const updateImportUsage = (node) => { + const updateImportUsage = node => { if (!unusedExports) { - return; + return } - let oldImportPaths = importList.get(file); + let oldImportPaths = importList.get(file) if (typeof oldImportPaths === 'undefined') { - oldImportPaths = new Map(); + oldImportPaths = new Map() } - const oldNamespaceImports = new Set(); - const newNamespaceImports = new Set(); + const oldNamespaceImports = new Set() + const newNamespaceImports = new Set() - const oldExportAll = new Set(); - const newExportAll = new Set(); + const oldExportAll = new Set() + const newExportAll = new Set() - const oldDefaultImports = new Set(); - const newDefaultImports = new Set(); + const oldDefaultImports = new Set() + const newDefaultImports = new Set() - const oldImports = new Map(); - const newImports = new Map(); + const oldImports = new Map() + const newImports = new Map() oldImportPaths.forEach((value, key) => { if (value.has(EXPORT_ALL_DECLARATION)) { - oldExportAll.add(key); + oldExportAll.add(key) } if (value.has(IMPORT_NAMESPACE_SPECIFIER)) { - oldNamespaceImports.add(key); + oldNamespaceImports.add(key) } if (value.has(IMPORT_DEFAULT_SPECIFIER)) { - oldDefaultImports.add(key); + oldDefaultImports.add(key) } - value.forEach((val) => { + value.forEach(val => { if ( - val !== IMPORT_NAMESPACE_SPECIFIER - && val !== IMPORT_DEFAULT_SPECIFIER + val !== IMPORT_NAMESPACE_SPECIFIER && + val !== IMPORT_DEFAULT_SPECIFIER ) { - oldImports.set(val, key); + oldImports.set(val, key) } - }); - }); + }) + }) function processDynamicImport(source) { if (source.type !== 'Literal') { - return null; + return null } - const p = resolve(source.value, context); + const p = resolve(source.value, context) if (p == null) { - return null; + return null } - newNamespaceImports.add(p); + newNamespaceImports.add(p) } visit(node, visitorKeyMap.get(file), { ImportExpression(child) { - processDynamicImport(child.source); + processDynamicImport(child.source) }, CallExpression(child) { if (child.callee.type === 'Import') { - processDynamicImport(child.arguments[0]); + processDynamicImport(child.arguments[0]) } }, - }); + }) - node.body.forEach((astNode) => { - let resolvedPath; + node.body.forEach(astNode => { + let resolvedPath // support for export { value } from 'module' if (astNode.type === EXPORT_NAMED_DECLARATION) { if (astNode.source) { - resolvedPath = resolve(astNode.source.raw.replace(/('|")/g, ''), context); - astNode.specifiers.forEach((specifier) => { - const name = specifier.local.name || specifier.local.value; + resolvedPath = resolve( + astNode.source.raw.replace(/('|")/g, ''), + context, + ) + astNode.specifiers.forEach(specifier => { + const name = specifier.local.name || specifier.local.value if (name === DEFAULT) { - newDefaultImports.add(resolvedPath); + newDefaultImports.add(resolvedPath) } else { - newImports.set(name, resolvedPath); + newImports.set(name, resolvedPath) } - }); + }) } } if (astNode.type === EXPORT_ALL_DECLARATION) { - resolvedPath = resolve(astNode.source.raw.replace(/('|")/g, ''), context); - newExportAll.add(resolvedPath); + resolvedPath = resolve( + astNode.source.raw.replace(/('|")/g, ''), + context, + ) + newExportAll.add(resolvedPath) } if (astNode.type === IMPORT_DECLARATION) { - resolvedPath = resolve(astNode.source.raw.replace(/('|")/g, ''), context); + resolvedPath = resolve( + astNode.source.raw.replace(/('|")/g, ''), + context, + ) if (!resolvedPath) { - return; + return } if (isNodeModule(resolvedPath)) { - return; + return } if (newNamespaceImportExists(astNode.specifiers)) { - newNamespaceImports.add(resolvedPath); + newNamespaceImports.add(resolvedPath) } if (newDefaultImportExists(astNode.specifiers)) { - newDefaultImports.add(resolvedPath); + newDefaultImports.add(resolvedPath) } astNode.specifiers - .filter((specifier) => specifier.type !== IMPORT_DEFAULT_SPECIFIER && specifier.type !== IMPORT_NAMESPACE_SPECIFIER) - .forEach((specifier) => { - newImports.set(specifier.imported.name || specifier.imported.value, resolvedPath); - }); - } - }); - - newExportAll.forEach((value) => { + .filter( + specifier => + specifier.type !== IMPORT_DEFAULT_SPECIFIER && + specifier.type !== IMPORT_NAMESPACE_SPECIFIER, + ) + .forEach(specifier => { + newImports.set( + specifier.imported.name || specifier.imported.value, + resolvedPath, + ) + }) + } + }) + + newExportAll.forEach(value => { if (!oldExportAll.has(value)) { - let imports = oldImportPaths.get(value); + let imports = oldImportPaths.get(value) if (typeof imports === 'undefined') { - imports = new Set(); + imports = new Set() } - imports.add(EXPORT_ALL_DECLARATION); - oldImportPaths.set(value, imports); + imports.add(EXPORT_ALL_DECLARATION) + oldImportPaths.set(value, imports) - let exports = exportList.get(value); - let currentExport; + let exports = exportList.get(value) + let currentExport if (typeof exports !== 'undefined') { - currentExport = exports.get(EXPORT_ALL_DECLARATION); + currentExport = exports.get(EXPORT_ALL_DECLARATION) } else { - exports = new Map(); - exportList.set(value, exports); + exports = new Map() + exportList.set(value, exports) } if (typeof currentExport !== 'undefined') { - currentExport.whereUsed.add(file); + currentExport.whereUsed.add(file) } else { - const whereUsed = new Set(); - whereUsed.add(file); - exports.set(EXPORT_ALL_DECLARATION, { whereUsed }); + const whereUsed = new Set() + whereUsed.add(file) + exports.set(EXPORT_ALL_DECLARATION, { whereUsed }) } } - }); + }) - oldExportAll.forEach((value) => { + oldExportAll.forEach(value => { if (!newExportAll.has(value)) { - const imports = oldImportPaths.get(value); - imports.delete(EXPORT_ALL_DECLARATION); + const imports = oldImportPaths.get(value) + imports.delete(EXPORT_ALL_DECLARATION) - const exports = exportList.get(value); + const exports = exportList.get(value) if (typeof exports !== 'undefined') { - const currentExport = exports.get(EXPORT_ALL_DECLARATION); + const currentExport = exports.get(EXPORT_ALL_DECLARATION) if (typeof currentExport !== 'undefined') { - currentExport.whereUsed.delete(file); + currentExport.whereUsed.delete(file) } } } - }); + }) - newDefaultImports.forEach((value) => { + newDefaultImports.forEach(value => { if (!oldDefaultImports.has(value)) { - let imports = oldImportPaths.get(value); + let imports = oldImportPaths.get(value) if (typeof imports === 'undefined') { - imports = new Set(); + imports = new Set() } - imports.add(IMPORT_DEFAULT_SPECIFIER); - oldImportPaths.set(value, imports); + imports.add(IMPORT_DEFAULT_SPECIFIER) + oldImportPaths.set(value, imports) - let exports = exportList.get(value); - let currentExport; + let exports = exportList.get(value) + let currentExport if (typeof exports !== 'undefined') { - currentExport = exports.get(IMPORT_DEFAULT_SPECIFIER); + currentExport = exports.get(IMPORT_DEFAULT_SPECIFIER) } else { - exports = new Map(); - exportList.set(value, exports); + exports = new Map() + exportList.set(value, exports) } if (typeof currentExport !== 'undefined') { - currentExport.whereUsed.add(file); + currentExport.whereUsed.add(file) } else { - const whereUsed = new Set(); - whereUsed.add(file); - exports.set(IMPORT_DEFAULT_SPECIFIER, { whereUsed }); + const whereUsed = new Set() + whereUsed.add(file) + exports.set(IMPORT_DEFAULT_SPECIFIER, { whereUsed }) } } - }); + }) - oldDefaultImports.forEach((value) => { + oldDefaultImports.forEach(value => { if (!newDefaultImports.has(value)) { - const imports = oldImportPaths.get(value); - imports.delete(IMPORT_DEFAULT_SPECIFIER); + const imports = oldImportPaths.get(value) + imports.delete(IMPORT_DEFAULT_SPECIFIER) - const exports = exportList.get(value); + const exports = exportList.get(value) if (typeof exports !== 'undefined') { - const currentExport = exports.get(IMPORT_DEFAULT_SPECIFIER); + const currentExport = exports.get(IMPORT_DEFAULT_SPECIFIER) if (typeof currentExport !== 'undefined') { - currentExport.whereUsed.delete(file); + currentExport.whereUsed.delete(file) } } } - }); + }) - newNamespaceImports.forEach((value) => { + newNamespaceImports.forEach(value => { if (!oldNamespaceImports.has(value)) { - let imports = oldImportPaths.get(value); + let imports = oldImportPaths.get(value) if (typeof imports === 'undefined') { - imports = new Set(); + imports = new Set() } - imports.add(IMPORT_NAMESPACE_SPECIFIER); - oldImportPaths.set(value, imports); + imports.add(IMPORT_NAMESPACE_SPECIFIER) + oldImportPaths.set(value, imports) - let exports = exportList.get(value); - let currentExport; + let exports = exportList.get(value) + let currentExport if (typeof exports !== 'undefined') { - currentExport = exports.get(IMPORT_NAMESPACE_SPECIFIER); + currentExport = exports.get(IMPORT_NAMESPACE_SPECIFIER) } else { - exports = new Map(); - exportList.set(value, exports); + exports = new Map() + exportList.set(value, exports) } if (typeof currentExport !== 'undefined') { - currentExport.whereUsed.add(file); + currentExport.whereUsed.add(file) } else { - const whereUsed = new Set(); - whereUsed.add(file); - exports.set(IMPORT_NAMESPACE_SPECIFIER, { whereUsed }); + const whereUsed = new Set() + whereUsed.add(file) + exports.set(IMPORT_NAMESPACE_SPECIFIER, { whereUsed }) } } - }); + }) - oldNamespaceImports.forEach((value) => { + oldNamespaceImports.forEach(value => { if (!newNamespaceImports.has(value)) { - const imports = oldImportPaths.get(value); - imports.delete(IMPORT_NAMESPACE_SPECIFIER); + const imports = oldImportPaths.get(value) + imports.delete(IMPORT_NAMESPACE_SPECIFIER) - const exports = exportList.get(value); + const exports = exportList.get(value) if (typeof exports !== 'undefined') { - const currentExport = exports.get(IMPORT_NAMESPACE_SPECIFIER); + const currentExport = exports.get(IMPORT_NAMESPACE_SPECIFIER) if (typeof currentExport !== 'undefined') { - currentExport.whereUsed.delete(file); + currentExport.whereUsed.delete(file) } } } - }); + }) newImports.forEach((value, key) => { if (!oldImports.has(key)) { - let imports = oldImportPaths.get(value); + let imports = oldImportPaths.get(value) if (typeof imports === 'undefined') { - imports = new Set(); + imports = new Set() } - imports.add(key); - oldImportPaths.set(value, imports); + imports.add(key) + oldImportPaths.set(value, imports) - let exports = exportList.get(value); - let currentExport; + let exports = exportList.get(value) + let currentExport if (typeof exports !== 'undefined') { - currentExport = exports.get(key); + currentExport = exports.get(key) } else { - exports = new Map(); - exportList.set(value, exports); + exports = new Map() + exportList.set(value, exports) } if (typeof currentExport !== 'undefined') { - currentExport.whereUsed.add(file); + currentExport.whereUsed.add(file) } else { - const whereUsed = new Set(); - whereUsed.add(file); - exports.set(key, { whereUsed }); + const whereUsed = new Set() + whereUsed.add(file) + exports.set(key, { whereUsed }) } } - }); + }) oldImports.forEach((value, key) => { if (!newImports.has(key)) { - const imports = oldImportPaths.get(value); - imports.delete(key); + const imports = oldImportPaths.get(value) + imports.delete(key) - const exports = exportList.get(value); + const exports = exportList.get(value) if (typeof exports !== 'undefined') { - const currentExport = exports.get(key); + const currentExport = exports.get(key) if (typeof currentExport !== 'undefined') { - currentExport.whereUsed.delete(file); + currentExport.whereUsed.delete(file) } } } - }); - }; + }) + } return { 'Program:exit'(node) { - updateExportUsage(node); - updateImportUsage(node); - checkExportPresence(node); + updateExportUsage(node) + updateImportUsage(node) + checkExportPresence(node) }, ExportDefaultDeclaration(node) { - checkUsage(node, IMPORT_DEFAULT_SPECIFIER); + checkUsage(node, IMPORT_DEFAULT_SPECIFIER) }, ExportNamedDeclaration(node) { - node.specifiers.forEach((specifier) => { - checkUsage(specifier, specifier.exported.name || specifier.exported.value); - }); - forEachDeclarationIdentifier(node.declaration, (name) => { - checkUsage(node, name); - }); + node.specifiers.forEach(specifier => { + checkUsage( + specifier, + specifier.exported.name || specifier.exported.value, + ) + }) + forEachDeclarationIdentifier(node.declaration, name => { + checkUsage(node, name) + }) }, - }; + } }, -}; +} diff --git a/src/rules/no-useless-path-segments.js b/src/rules/no-useless-path-segments.js index 390a7546d..0f68401e7 100644 --- a/src/rules/no-useless-path-segments.js +++ b/src/rules/no-useless-path-segments.js @@ -3,11 +3,11 @@ * @author Thomas Grainger */ -import { getFileExtensions } from 'eslint-module-utils/ignore'; -import moduleVisitor from 'eslint-module-utils/moduleVisitor'; -import resolve from 'eslint-module-utils/resolve'; -import path from 'path'; -import docsUrl from '../docsUrl'; +import { getFileExtensions } from '../utils/ignore' +import moduleVisitor from '../utils/moduleVisitor' +import resolve from '../utils/resolve' +import path from 'path' +import docsUrl from '../docsUrl' /** * convert a potentially relative path from node utils into a true @@ -23,17 +23,17 @@ import docsUrl from '../docsUrl'; * @returns {string} relative posix path that always starts with a ./ **/ function toRelativePath(relativePath) { - const stripped = relativePath.replace(/\/$/g, ''); // Remove trailing / + const stripped = relativePath.replace(/\/$/g, '') // Remove trailing / - return (/^((\.\.)|(\.))($|\/)/).test(stripped) ? stripped : `./${stripped}`; + return /^((\.\.)|(\.))($|\/)/.test(stripped) ? stripped : `./${stripped}` } function normalize(fn) { - return toRelativePath(path.posix.normalize(fn)); + return toRelativePath(path.posix.normalize(fn)) } function countRelativeParents(pathSegments) { - return pathSegments.filter((x) => x === '..').length; + return pathSegments.filter(x => x === '..').length } module.exports = { @@ -41,7 +41,8 @@ module.exports = { type: 'suggestion', docs: { category: 'Static analysis', - description: 'Forbid unnecessary path segments in import and require statements.', + description: + 'Forbid unnecessary path segments in import and require statements.', url: docsUrl('no-useless-path-segments'), }, @@ -60,75 +61,86 @@ module.exports = { }, create(context) { - const currentDir = path.dirname(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()); - const options = context.options[0]; + const currentDir = path.dirname( + context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename(), + ) + const options = context.options[0] function checkSourceValue(source) { - const { value: importPath } = source; + const { value: importPath } = source function reportWithProposedPath(proposedPath) { context.report({ node: source, // Note: Using messageIds is not possible due to the support for ESLint 2 and 3 message: `Useless path segments for "${importPath}", should be "${proposedPath}"`, - fix: (fixer) => proposedPath && fixer.replaceText(source, JSON.stringify(proposedPath)), - }); + fix: fixer => + proposedPath && + fixer.replaceText(source, JSON.stringify(proposedPath)), + }) } // Only relative imports are relevant for this rule --> Skip checking if (!importPath.startsWith('.')) { - return; + return } // Report rule violation if path is not the shortest possible - const resolvedPath = resolve(importPath, context); - const normedPath = normalize(importPath); - const resolvedNormedPath = resolve(normedPath, context); + const resolvedPath = resolve(importPath, context) + const normedPath = normalize(importPath) + const resolvedNormedPath = resolve(normedPath, context) if (normedPath !== importPath && resolvedPath === resolvedNormedPath) { - return reportWithProposedPath(normedPath); + return reportWithProposedPath(normedPath) } - const fileExtensions = getFileExtensions(context.settings); + const fileExtensions = getFileExtensions(context.settings) const regexUnnecessaryIndex = new RegExp( `.*\\/index(\\${Array.from(fileExtensions).join('|\\')})?$`, - ); + ) // Check if path contains unnecessary index (including a configured extension) - if (options && options.noUselessIndex && regexUnnecessaryIndex.test(importPath)) { - const parentDirectory = path.dirname(importPath); + if ( + options && + options.noUselessIndex && + regexUnnecessaryIndex.test(importPath) + ) { + const parentDirectory = path.dirname(importPath) // Try to find ambiguous imports if (parentDirectory !== '.' && parentDirectory !== '..') { for (const fileExtension of fileExtensions) { if (resolve(`${parentDirectory}${fileExtension}`, context)) { - return reportWithProposedPath(`${parentDirectory}/`); + return reportWithProposedPath(`${parentDirectory}/`) } } } - return reportWithProposedPath(parentDirectory); + return reportWithProposedPath(parentDirectory) } // Path is shortest possible + starts from the current directory --> Return directly if (importPath.startsWith('./')) { - return; + return } // Path is not existing --> Return directly (following code requires path to be defined) if (resolvedPath === undefined) { - return; + return } - const expected = path.relative(currentDir, resolvedPath); // Expected import path - const expectedSplit = expected.split(path.sep); // Split by / or \ (depending on OS) - const importPathSplit = importPath.replace(/^\.\//, '').split('/'); - const countImportPathRelativeParents = countRelativeParents(importPathSplit); - const countExpectedRelativeParents = countRelativeParents(expectedSplit); - const diff = countImportPathRelativeParents - countExpectedRelativeParents; + const expected = path.relative(currentDir, resolvedPath) // Expected import path + const expectedSplit = expected.split(path.sep) // Split by / or \ (depending on OS) + const importPathSplit = importPath.replace(/^\.\//, '').split('/') + const countImportPathRelativeParents = + countRelativeParents(importPathSplit) + const countExpectedRelativeParents = countRelativeParents(expectedSplit) + const diff = countImportPathRelativeParents - countExpectedRelativeParents // Same number of relative parents --> Paths are the same --> Return directly if (diff <= 0) { - return; + return } // Report and propose minimal number of required relative parents @@ -136,12 +148,14 @@ module.exports = { toRelativePath( importPathSplit .slice(0, countExpectedRelativeParents) - .concat(importPathSplit.slice(countImportPathRelativeParents + diff)) + .concat( + importPathSplit.slice(countImportPathRelativeParents + diff), + ) .join('/'), ), - ); + ) } - return moduleVisitor(checkSourceValue, options); + return moduleVisitor(checkSourceValue, options) }, -}; +} diff --git a/src/rules/no-webpack-loader-syntax.js b/src/rules/no-webpack-loader-syntax.js index 6ca7d603d..9f489c113 100644 --- a/src/rules/no-webpack-loader-syntax.js +++ b/src/rules/no-webpack-loader-syntax.js @@ -1,9 +1,12 @@ -import moduleVisitor from 'eslint-module-utils/moduleVisitor'; -import docsUrl from '../docsUrl'; +import moduleVisitor from '../utils/moduleVisitor' +import docsUrl from '../docsUrl' function reportIfNonStandard(context, node, name) { if (name && name.indexOf('!') !== -1) { - context.report(node, `Unexpected '!' in '${name}'. Do not use import syntax to configure webpack loaders.`); + context.report( + node, + `Unexpected '!' in '${name}'. Do not use import syntax to configure webpack loaders.`, + ) } } @@ -19,8 +22,11 @@ module.exports = { }, create(context) { - return moduleVisitor((source, node) => { - reportIfNonStandard(context, node, source.value); - }, { commonjs: true }); + return moduleVisitor( + (source, node) => { + reportIfNonStandard(context, node, source.value) + }, + { commonjs: true }, + ) }, -}; +} diff --git a/src/rules/order.js b/src/rules/order.js index 44d25be63..3121ccdc3 100644 --- a/src/rules/order.js +++ b/src/rules/order.js @@ -1,555 +1,673 @@ -'use strict'; +'use strict' -import minimatch from 'minimatch'; -import includes from 'array-includes'; -import groupBy from 'object.groupby'; +import minimatch from 'minimatch' +import includes from 'array-includes' +import groupBy from 'object.groupby' -import importType from '../core/importType'; -import isStaticRequire from '../core/staticRequire'; -import docsUrl from '../docsUrl'; +import importType from '../core/importType' +import isStaticRequire from '../core/staticRequire' +import docsUrl from '../docsUrl' -const defaultGroups = ['builtin', 'external', 'parent', 'sibling', 'index']; +const defaultGroups = ['builtin', 'external', 'parent', 'sibling', 'index'] // REPORTING AND FIXING function reverse(array) { - return array.map(function (v) { - return { ...v, rank: -v.rank }; - }).reverse(); + return array + .map(function (v) { + return { ...v, rank: -v.rank } + }) + .reverse() } function getTokensOrCommentsAfter(sourceCode, node, count) { - let currentNodeOrToken = node; - const result = []; + let currentNodeOrToken = node + const result = [] for (let i = 0; i < count; i++) { - currentNodeOrToken = sourceCode.getTokenOrCommentAfter(currentNodeOrToken); + currentNodeOrToken = sourceCode.getTokenOrCommentAfter(currentNodeOrToken) if (currentNodeOrToken == null) { - break; + break } - result.push(currentNodeOrToken); + result.push(currentNodeOrToken) } - return result; + return result } function getTokensOrCommentsBefore(sourceCode, node, count) { - let currentNodeOrToken = node; - const result = []; + let currentNodeOrToken = node + const result = [] for (let i = 0; i < count; i++) { - currentNodeOrToken = sourceCode.getTokenOrCommentBefore(currentNodeOrToken); + currentNodeOrToken = sourceCode.getTokenOrCommentBefore(currentNodeOrToken) if (currentNodeOrToken == null) { - break; + break } - result.push(currentNodeOrToken); + result.push(currentNodeOrToken) } - return result.reverse(); + return result.reverse() } function takeTokensAfterWhile(sourceCode, node, condition) { - const tokens = getTokensOrCommentsAfter(sourceCode, node, 100); - const result = []; + const tokens = getTokensOrCommentsAfter(sourceCode, node, 100) + const result = [] for (let i = 0; i < tokens.length; i++) { if (condition(tokens[i])) { - result.push(tokens[i]); + result.push(tokens[i]) } else { - break; + break } } - return result; + return result } function takeTokensBeforeWhile(sourceCode, node, condition) { - const tokens = getTokensOrCommentsBefore(sourceCode, node, 100); - const result = []; + const tokens = getTokensOrCommentsBefore(sourceCode, node, 100) + const result = [] for (let i = tokens.length - 1; i >= 0; i--) { if (condition(tokens[i])) { - result.push(tokens[i]); + result.push(tokens[i]) } else { - break; + break } } - return result.reverse(); + return result.reverse() } function findOutOfOrder(imported) { if (imported.length === 0) { - return []; + return [] } - let maxSeenRankNode = imported[0]; + let maxSeenRankNode = imported[0] return imported.filter(function (importedModule) { - const res = importedModule.rank < maxSeenRankNode.rank; + const res = importedModule.rank < maxSeenRankNode.rank if (maxSeenRankNode.rank < importedModule.rank) { - maxSeenRankNode = importedModule; + maxSeenRankNode = importedModule } - return res; - }); + return res + }) } function findRootNode(node) { - let parent = node; + let parent = node while (parent.parent != null && parent.parent.body == null) { - parent = parent.parent; + parent = parent.parent } - return parent; + return parent } function findEndOfLineWithComments(sourceCode, node) { - const tokensToEndOfLine = takeTokensAfterWhile(sourceCode, node, commentOnSameLineAs(node)); - const endOfTokens = tokensToEndOfLine.length > 0 - ? tokensToEndOfLine[tokensToEndOfLine.length - 1].range[1] - : node.range[1]; - let result = endOfTokens; + const tokensToEndOfLine = takeTokensAfterWhile( + sourceCode, + node, + commentOnSameLineAs(node), + ) + const endOfTokens = + tokensToEndOfLine.length > 0 + ? tokensToEndOfLine[tokensToEndOfLine.length - 1].range[1] + : node.range[1] + let result = endOfTokens for (let i = endOfTokens; i < sourceCode.text.length; i++) { if (sourceCode.text[i] === '\n') { - result = i + 1; - break; + result = i + 1 + break } - if (sourceCode.text[i] !== ' ' && sourceCode.text[i] !== '\t' && sourceCode.text[i] !== '\r') { - break; + if ( + sourceCode.text[i] !== ' ' && + sourceCode.text[i] !== '\t' && + sourceCode.text[i] !== '\r' + ) { + break } - result = i + 1; + result = i + 1 } - return result; + return result } function commentOnSameLineAs(node) { - return (token) => (token.type === 'Block' || token.type === 'Line') - && token.loc.start.line === token.loc.end.line - && token.loc.end.line === node.loc.end.line; + return token => + (token.type === 'Block' || token.type === 'Line') && + token.loc.start.line === token.loc.end.line && + token.loc.end.line === node.loc.end.line } function findStartOfLineWithComments(sourceCode, node) { - const tokensToEndOfLine = takeTokensBeforeWhile(sourceCode, node, commentOnSameLineAs(node)); - const startOfTokens = tokensToEndOfLine.length > 0 ? tokensToEndOfLine[0].range[0] : node.range[0]; - let result = startOfTokens; + const tokensToEndOfLine = takeTokensBeforeWhile( + sourceCode, + node, + commentOnSameLineAs(node), + ) + const startOfTokens = + tokensToEndOfLine.length > 0 ? tokensToEndOfLine[0].range[0] : node.range[0] + let result = startOfTokens for (let i = startOfTokens - 1; i > 0; i--) { if (sourceCode.text[i] !== ' ' && sourceCode.text[i] !== '\t') { - break; + break } - result = i; + result = i } - return result; + return result } function isRequireExpression(expr) { - return expr != null - && expr.type === 'CallExpression' - && expr.callee != null - && expr.callee.name === 'require' - && expr.arguments != null - && expr.arguments.length === 1 - && expr.arguments[0].type === 'Literal'; + return ( + expr != null && + expr.type === 'CallExpression' && + expr.callee != null && + expr.callee.name === 'require' && + expr.arguments != null && + expr.arguments.length === 1 && + expr.arguments[0].type === 'Literal' + ) } function isSupportedRequireModule(node) { if (node.type !== 'VariableDeclaration') { - return false; + return false } if (node.declarations.length !== 1) { - return false; - } - const decl = node.declarations[0]; - const isPlainRequire = decl.id - && (decl.id.type === 'Identifier' || decl.id.type === 'ObjectPattern') - && isRequireExpression(decl.init); - const isRequireWithMemberExpression = decl.id - && (decl.id.type === 'Identifier' || decl.id.type === 'ObjectPattern') - && decl.init != null - && decl.init.type === 'CallExpression' - && decl.init.callee != null - && decl.init.callee.type === 'MemberExpression' - && isRequireExpression(decl.init.callee.object); - return isPlainRequire || isRequireWithMemberExpression; + return false + } + const decl = node.declarations[0] + const isPlainRequire = + decl.id && + (decl.id.type === 'Identifier' || decl.id.type === 'ObjectPattern') && + isRequireExpression(decl.init) + const isRequireWithMemberExpression = + decl.id && + (decl.id.type === 'Identifier' || decl.id.type === 'ObjectPattern') && + decl.init != null && + decl.init.type === 'CallExpression' && + decl.init.callee != null && + decl.init.callee.type === 'MemberExpression' && + isRequireExpression(decl.init.callee.object) + return isPlainRequire || isRequireWithMemberExpression } function isPlainImportModule(node) { - return node.type === 'ImportDeclaration' && node.specifiers != null && node.specifiers.length > 0; + return ( + node.type === 'ImportDeclaration' && + node.specifiers != null && + node.specifiers.length > 0 + ) } function isPlainImportEquals(node) { - return node.type === 'TSImportEqualsDeclaration' && node.moduleReference.expression; + return ( + node.type === 'TSImportEqualsDeclaration' && node.moduleReference.expression + ) } function canCrossNodeWhileReorder(node) { - return isSupportedRequireModule(node) || isPlainImportModule(node) || isPlainImportEquals(node); + return ( + isSupportedRequireModule(node) || + isPlainImportModule(node) || + isPlainImportEquals(node) + ) } function canReorderItems(firstNode, secondNode) { - const parent = firstNode.parent; + const parent = firstNode.parent const [firstIndex, secondIndex] = [ parent.body.indexOf(firstNode), parent.body.indexOf(secondNode), - ].sort(); - const nodesBetween = parent.body.slice(firstIndex, secondIndex + 1); + ].sort() + const nodesBetween = parent.body.slice(firstIndex, secondIndex + 1) for (const nodeBetween of nodesBetween) { if (!canCrossNodeWhileReorder(nodeBetween)) { - return false; + return false } } - return true; + return true } function makeImportDescription(node) { if (node.node.importKind === 'type') { - return 'type import'; + return 'type import' } if (node.node.importKind === 'typeof') { - return 'typeof import'; + return 'typeof import' } - return 'import'; + return 'import' } function fixOutOfOrder(context, firstNode, secondNode, order) { - const sourceCode = context.getSourceCode(); + const sourceCode = context.getSourceCode() - const firstRoot = findRootNode(firstNode.node); - const firstRootStart = findStartOfLineWithComments(sourceCode, firstRoot); - const firstRootEnd = findEndOfLineWithComments(sourceCode, firstRoot); + const firstRoot = findRootNode(firstNode.node) + const firstRootStart = findStartOfLineWithComments(sourceCode, firstRoot) + const firstRootEnd = findEndOfLineWithComments(sourceCode, firstRoot) - const secondRoot = findRootNode(secondNode.node); - const secondRootStart = findStartOfLineWithComments(sourceCode, secondRoot); - const secondRootEnd = findEndOfLineWithComments(sourceCode, secondRoot); - const canFix = canReorderItems(firstRoot, secondRoot); + const secondRoot = findRootNode(secondNode.node) + const secondRootStart = findStartOfLineWithComments(sourceCode, secondRoot) + const secondRootEnd = findEndOfLineWithComments(sourceCode, secondRoot) + const canFix = canReorderItems(firstRoot, secondRoot) - let newCode = sourceCode.text.substring(secondRootStart, secondRootEnd); + let newCode = sourceCode.text.substring(secondRootStart, secondRootEnd) if (newCode[newCode.length - 1] !== '\n') { - newCode = `${newCode}\n`; + newCode = `${newCode}\n` } - const firstImport = `${makeImportDescription(firstNode)} of \`${firstNode.displayName}\``; - const secondImport = `\`${secondNode.displayName}\` ${makeImportDescription(secondNode)}`; - const message = `${secondImport} should occur ${order} ${firstImport}`; + const firstImport = `${makeImportDescription(firstNode)} of \`${firstNode.displayName}\`` + const secondImport = `\`${secondNode.displayName}\` ${makeImportDescription(secondNode)}` + const message = `${secondImport} should occur ${order} ${firstImport}` if (order === 'before') { context.report({ node: secondNode.node, message, - fix: canFix && ((fixer) => fixer.replaceTextRange( - [firstRootStart, secondRootEnd], - newCode + sourceCode.text.substring(firstRootStart, secondRootStart), - )), - }); + fix: + canFix && + (fixer => + fixer.replaceTextRange( + [firstRootStart, secondRootEnd], + newCode + + sourceCode.text.substring(firstRootStart, secondRootStart), + )), + }) } else if (order === 'after') { context.report({ node: secondNode.node, message, - fix: canFix && ((fixer) => fixer.replaceTextRange( - [secondRootStart, firstRootEnd], - sourceCode.text.substring(secondRootEnd, firstRootEnd) + newCode, - )), - }); + fix: + canFix && + (fixer => + fixer.replaceTextRange( + [secondRootStart, firstRootEnd], + sourceCode.text.substring(secondRootEnd, firstRootEnd) + newCode, + )), + }) } } function reportOutOfOrder(context, imported, outOfOrder, order) { outOfOrder.forEach(function (imp) { const found = imported.find(function hasHigherRank(importedItem) { - return importedItem.rank > imp.rank; - }); - fixOutOfOrder(context, found, imp, order); - }); + return importedItem.rank > imp.rank + }) + fixOutOfOrder(context, found, imp, order) + }) } function makeOutOfOrderReport(context, imported) { - const outOfOrder = findOutOfOrder(imported); + const outOfOrder = findOutOfOrder(imported) if (!outOfOrder.length) { - return; + return } // There are things to report. Try to minimize the number of reported errors. - const reversedImported = reverse(imported); - const reversedOrder = findOutOfOrder(reversedImported); + const reversedImported = reverse(imported) + const reversedOrder = findOutOfOrder(reversedImported) if (reversedOrder.length < outOfOrder.length) { - reportOutOfOrder(context, reversedImported, reversedOrder, 'after'); - return; + reportOutOfOrder(context, reversedImported, reversedOrder, 'after') + return } - reportOutOfOrder(context, imported, outOfOrder, 'before'); + reportOutOfOrder(context, imported, outOfOrder, 'before') } const compareString = (a, b) => { if (a < b) { - return -1; + return -1 } if (a > b) { - return 1; + return 1 } - return 0; -}; + return 0 +} /** Some parsers (languages without types) don't provide ImportKind */ -const DEAFULT_IMPORT_KIND = 'value'; +const DEAFULT_IMPORT_KIND = 'value' const getNormalizedValue = (node, toLowerCase) => { - const value = node.value; - return toLowerCase ? String(value).toLowerCase() : value; -}; + const value = node.value + return toLowerCase ? String(value).toLowerCase() : value +} function getSorter(alphabetizeOptions) { - const multiplier = alphabetizeOptions.order === 'asc' ? 1 : -1; - const orderImportKind = alphabetizeOptions.orderImportKind; - const multiplierImportKind = orderImportKind !== 'ignore' - && (alphabetizeOptions.orderImportKind === 'asc' ? 1 : -1); + const multiplier = alphabetizeOptions.order === 'asc' ? 1 : -1 + const orderImportKind = alphabetizeOptions.orderImportKind + const multiplierImportKind = + orderImportKind !== 'ignore' && + (alphabetizeOptions.orderImportKind === 'asc' ? 1 : -1) return function importsSorter(nodeA, nodeB) { - const importA = getNormalizedValue(nodeA, alphabetizeOptions.caseInsensitive); - const importB = getNormalizedValue(nodeB, alphabetizeOptions.caseInsensitive); - let result = 0; + const importA = getNormalizedValue( + nodeA, + alphabetizeOptions.caseInsensitive, + ) + const importB = getNormalizedValue( + nodeB, + alphabetizeOptions.caseInsensitive, + ) + let result = 0 if (!includes(importA, '/') && !includes(importB, '/')) { - result = compareString(importA, importB); + result = compareString(importA, importB) } else { - const A = importA.split('/'); - const B = importB.split('/'); - const a = A.length; - const b = B.length; + const A = importA.split('/') + const B = importB.split('/') + const a = A.length + const b = B.length for (let i = 0; i < Math.min(a, b); i++) { - result = compareString(A[i], B[i]); - if (result) { break; } + result = compareString(A[i], B[i]) + if (result) { + break + } } if (!result && a !== b) { - result = a < b ? -1 : 1; + result = a < b ? -1 : 1 } } - result = result * multiplier; + result = result * multiplier // In case the paths are equal (result === 0), sort them by importKind if (!result && multiplierImportKind) { - result = multiplierImportKind * compareString( - nodeA.node.importKind || DEAFULT_IMPORT_KIND, - nodeB.node.importKind || DEAFULT_IMPORT_KIND, - ); + result = + multiplierImportKind * + compareString( + nodeA.node.importKind || DEAFULT_IMPORT_KIND, + nodeB.node.importKind || DEAFULT_IMPORT_KIND, + ) } - return result; - }; + return result + } } function mutateRanksToAlphabetize(imported, alphabetizeOptions) { - const groupedByRanks = groupBy(imported, (item) => item.rank); + const groupedByRanks = groupBy(imported, item => item.rank) - const sorterFn = getSorter(alphabetizeOptions); + const sorterFn = getSorter(alphabetizeOptions) // sort group keys so that they can be iterated on in order const groupRanks = Object.keys(groupedByRanks).sort(function (a, b) { - return a - b; - }); + return a - b + }) // sort imports locally within their group groupRanks.forEach(function (groupRank) { - groupedByRanks[groupRank].sort(sorterFn); - }); + groupedByRanks[groupRank].sort(sorterFn) + }) // assign globally unique rank to each import - let newRank = 0; + let newRank = 0 const alphabetizedRanks = groupRanks.reduce(function (acc, groupRank) { groupedByRanks[groupRank].forEach(function (importedItem) { - acc[`${importedItem.value}|${importedItem.node.importKind}`] = parseInt(groupRank, 10) + newRank; - newRank += 1; - }); - return acc; - }, {}); + acc[`${importedItem.value}|${importedItem.node.importKind}`] = + parseInt(groupRank, 10) + newRank + newRank += 1 + }) + return acc + }, {}) // mutate the original group-rank with alphabetized-rank imported.forEach(function (importedItem) { - importedItem.rank = alphabetizedRanks[`${importedItem.value}|${importedItem.node.importKind}`]; - }); + importedItem.rank = + alphabetizedRanks[`${importedItem.value}|${importedItem.node.importKind}`] + }) } // DETECTING function computePathRank(ranks, pathGroups, path, maxPosition) { for (let i = 0, l = pathGroups.length; i < l; i++) { - const { pattern, patternOptions, group, position = 1 } = pathGroups[i]; + const { pattern, patternOptions, group, position = 1 } = pathGroups[i] if (minimatch(path, pattern, patternOptions || { nocomment: true })) { - return ranks[group] + position / maxPosition; + return ranks[group] + position / maxPosition } } } function computeRank(context, ranks, importEntry, excludedImportTypes) { - let impType; - let rank; + let impType + let rank if (importEntry.type === 'import:object') { - impType = 'object'; - } else if (importEntry.node.importKind === 'type' && ranks.omittedTypes.indexOf('type') === -1) { - impType = 'type'; + impType = 'object' + } else if ( + importEntry.node.importKind === 'type' && + ranks.omittedTypes.indexOf('type') === -1 + ) { + impType = 'type' } else { - impType = importType(importEntry.value, context); + impType = importType(importEntry.value, context) } if (!excludedImportTypes.has(impType)) { - rank = computePathRank(ranks.groups, ranks.pathGroups, importEntry.value, ranks.maxPosition); + rank = computePathRank( + ranks.groups, + ranks.pathGroups, + importEntry.value, + ranks.maxPosition, + ) } if (typeof rank === 'undefined') { - rank = ranks.groups[impType]; + rank = ranks.groups[impType] } - if (importEntry.type !== 'import' && !importEntry.type.startsWith('import:')) { - rank += 100; + if ( + importEntry.type !== 'import' && + !importEntry.type.startsWith('import:') + ) { + rank += 100 } - return rank; + return rank } -function registerNode(context, importEntry, ranks, imported, excludedImportTypes) { - const rank = computeRank(context, ranks, importEntry, excludedImportTypes); +function registerNode( + context, + importEntry, + ranks, + imported, + excludedImportTypes, +) { + const rank = computeRank(context, ranks, importEntry, excludedImportTypes) if (rank !== -1) { - imported.push({ ...importEntry, rank }); + imported.push({ ...importEntry, rank }) } } function getRequireBlock(node) { - let n = node; + let n = node // Handle cases like `const baz = require('foo').bar.baz` // and `const foo = require('foo')()` while ( - n.parent.type === 'MemberExpression' && n.parent.object === n - || n.parent.type === 'CallExpression' && n.parent.callee === n + (n.parent.type === 'MemberExpression' && n.parent.object === n) || + (n.parent.type === 'CallExpression' && n.parent.callee === n) ) { - n = n.parent; + n = n.parent } if ( - n.parent.type === 'VariableDeclarator' - && n.parent.parent.type === 'VariableDeclaration' - && n.parent.parent.parent.type === 'Program' + n.parent.type === 'VariableDeclarator' && + n.parent.parent.type === 'VariableDeclaration' && + n.parent.parent.parent.type === 'Program' ) { - return n.parent.parent.parent; + return n.parent.parent.parent } } -const types = ['builtin', 'external', 'internal', 'unknown', 'parent', 'sibling', 'index', 'object', 'type']; +const types = [ + 'builtin', + 'external', + 'internal', + 'unknown', + 'parent', + 'sibling', + 'index', + 'object', + 'type', +] // Creates an object with type-rank pairs. // Example: { index: 0, sibling: 1, parent: 1, external: 1, builtin: 2, internal: 2 } // Will throw an error if it contains a type that does not exist, or has a duplicate function convertGroupsToRanks(groups) { const rankObject = groups.reduce(function (res, group, index) { - [].concat(group).forEach(function (groupItem) { + ;[].concat(group).forEach(function (groupItem) { if (types.indexOf(groupItem) === -1) { - throw new Error(`Incorrect configuration of the rule: Unknown type \`${JSON.stringify(groupItem)}\``); + throw new Error( + `Incorrect configuration of the rule: Unknown type \`${JSON.stringify(groupItem)}\``, + ) } if (res[groupItem] !== undefined) { - throw new Error(`Incorrect configuration of the rule: \`${groupItem}\` is duplicated`); + throw new Error( + `Incorrect configuration of the rule: \`${groupItem}\` is duplicated`, + ) } - res[groupItem] = index * 2; - }); - return res; - }, {}); + res[groupItem] = index * 2 + }) + return res + }, {}) const omittedTypes = types.filter(function (type) { - return typeof rankObject[type] === 'undefined'; - }); + return typeof rankObject[type] === 'undefined' + }) const ranks = omittedTypes.reduce(function (res, type) { - res[type] = groups.length * 2; - return res; - }, rankObject); + res[type] = groups.length * 2 + return res + }, rankObject) - return { groups: ranks, omittedTypes }; + return { groups: ranks, omittedTypes } } function convertPathGroupsForRanks(pathGroups) { - const after = {}; - const before = {}; + const after = {} + const before = {} const transformed = pathGroups.map((pathGroup, index) => { - const { group, position: positionString } = pathGroup; - let position = 0; + const { group, position: positionString } = pathGroup + let position = 0 if (positionString === 'after') { if (!after[group]) { - after[group] = 1; + after[group] = 1 } - position = after[group]++; + position = after[group]++ } else if (positionString === 'before') { if (!before[group]) { - before[group] = []; + before[group] = [] } - before[group].push(index); + before[group].push(index) } - return { ...pathGroup, position }; - }); + return { ...pathGroup, position } + }) - let maxPosition = 1; + let maxPosition = 1 - Object.keys(before).forEach((group) => { - const groupLength = before[group].length; + Object.keys(before).forEach(group => { + const groupLength = before[group].length before[group].forEach((groupIndex, index) => { - transformed[groupIndex].position = -1 * (groupLength - index); - }); - maxPosition = Math.max(maxPosition, groupLength); - }); + transformed[groupIndex].position = -1 * (groupLength - index) + }) + maxPosition = Math.max(maxPosition, groupLength) + }) - Object.keys(after).forEach((key) => { - const groupNextPosition = after[key]; - maxPosition = Math.max(maxPosition, groupNextPosition - 1); - }); + Object.keys(after).forEach(key => { + const groupNextPosition = after[key] + maxPosition = Math.max(maxPosition, groupNextPosition - 1) + }) return { pathGroups: transformed, - maxPosition: maxPosition > 10 ? Math.pow(10, Math.ceil(Math.log10(maxPosition))) : 10, - }; + maxPosition: + maxPosition > 10 ? Math.pow(10, Math.ceil(Math.log10(maxPosition))) : 10, + } } function fixNewLineAfterImport(context, previousImport) { - const prevRoot = findRootNode(previousImport.node); + const prevRoot = findRootNode(previousImport.node) const tokensToEndOfLine = takeTokensAfterWhile( - context.getSourceCode(), prevRoot, commentOnSameLineAs(prevRoot)); + context.getSourceCode(), + prevRoot, + commentOnSameLineAs(prevRoot), + ) - let endOfLine = prevRoot.range[1]; + let endOfLine = prevRoot.range[1] if (tokensToEndOfLine.length > 0) { - endOfLine = tokensToEndOfLine[tokensToEndOfLine.length - 1].range[1]; + endOfLine = tokensToEndOfLine[tokensToEndOfLine.length - 1].range[1] } - return (fixer) => fixer.insertTextAfterRange([prevRoot.range[0], endOfLine], '\n'); + return fixer => + fixer.insertTextAfterRange([prevRoot.range[0], endOfLine], '\n') } function removeNewLineAfterImport(context, currentImport, previousImport) { - const sourceCode = context.getSourceCode(); - const prevRoot = findRootNode(previousImport.node); - const currRoot = findRootNode(currentImport.node); + const sourceCode = context.getSourceCode() + const prevRoot = findRootNode(previousImport.node) + const currRoot = findRootNode(currentImport.node) const rangeToRemove = [ findEndOfLineWithComments(sourceCode, prevRoot), findStartOfLineWithComments(sourceCode, currRoot), - ]; - if ((/^\s*$/).test(sourceCode.text.substring(rangeToRemove[0], rangeToRemove[1]))) { - return (fixer) => fixer.removeRange(rangeToRemove); + ] + if ( + /^\s*$/.test(sourceCode.text.substring(rangeToRemove[0], rangeToRemove[1])) + ) { + return fixer => fixer.removeRange(rangeToRemove) } - return undefined; + return undefined } -function makeNewlinesBetweenReport(context, imported, newlinesBetweenImports, distinctGroup) { +function makeNewlinesBetweenReport( + context, + imported, + newlinesBetweenImports, + distinctGroup, +) { const getNumberOfEmptyLinesBetween = (currentImport, previousImport) => { - const linesBetweenImports = context.getSourceCode().lines.slice( - previousImport.node.loc.end.line, - currentImport.node.loc.start.line - 1, - ); - - return linesBetweenImports.filter((line) => !line.trim().length).length; - }; - const getIsStartOfDistinctGroup = (currentImport, previousImport) => currentImport.rank - 1 >= previousImport.rank; - let previousImport = imported[0]; + const linesBetweenImports = context + .getSourceCode() + .lines.slice( + previousImport.node.loc.end.line, + currentImport.node.loc.start.line - 1, + ) + + return linesBetweenImports.filter(line => !line.trim().length).length + } + const getIsStartOfDistinctGroup = (currentImport, previousImport) => + currentImport.rank - 1 >= previousImport.rank + let previousImport = imported[0] imported.slice(1).forEach(function (currentImport) { - const emptyLinesBetween = getNumberOfEmptyLinesBetween(currentImport, previousImport); - const isStartOfDistinctGroup = getIsStartOfDistinctGroup(currentImport, previousImport); - - if (newlinesBetweenImports === 'always' - || newlinesBetweenImports === 'always-and-inside-groups') { - if (currentImport.rank !== previousImport.rank && emptyLinesBetween === 0) { - if (distinctGroup || !distinctGroup && isStartOfDistinctGroup) { + const emptyLinesBetween = getNumberOfEmptyLinesBetween( + currentImport, + previousImport, + ) + const isStartOfDistinctGroup = getIsStartOfDistinctGroup( + currentImport, + previousImport, + ) + + if ( + newlinesBetweenImports === 'always' || + newlinesBetweenImports === 'always-and-inside-groups' + ) { + if ( + currentImport.rank !== previousImport.rank && + emptyLinesBetween === 0 + ) { + if (distinctGroup || (!distinctGroup && isStartOfDistinctGroup)) { context.report({ node: previousImport.node, - message: 'There should be at least one empty line between import groups', + message: + 'There should be at least one empty line between import groups', fix: fixNewLineAfterImport(context, previousImport), - }); + }) } - } else if (emptyLinesBetween > 0 - && newlinesBetweenImports !== 'always-and-inside-groups') { - if (distinctGroup && currentImport.rank === previousImport.rank || !distinctGroup && !isStartOfDistinctGroup) { + } else if ( + emptyLinesBetween > 0 && + newlinesBetweenImports !== 'always-and-inside-groups' + ) { + if ( + (distinctGroup && currentImport.rank === previousImport.rank) || + (!distinctGroup && !isStartOfDistinctGroup) + ) { context.report({ node: previousImport.node, message: 'There should be no empty line within import group', - fix: removeNewLineAfterImport(context, currentImport, previousImport), - }); + fix: removeNewLineAfterImport( + context, + currentImport, + previousImport, + ), + }) } } } else if (emptyLinesBetween > 0) { @@ -557,24 +675,24 @@ function makeNewlinesBetweenReport(context, imported, newlinesBetweenImports, di node: previousImport.node, message: 'There should be no empty line between import groups', fix: removeNewLineAfterImport(context, currentImport, previousImport), - }); + }) } - previousImport = currentImport; - }); + previousImport = currentImport + }) } function getAlphabetizeConfig(options) { - const alphabetize = options.alphabetize || {}; - const order = alphabetize.order || 'ignore'; - const orderImportKind = alphabetize.orderImportKind || 'ignore'; - const caseInsensitive = alphabetize.caseInsensitive || false; + const alphabetize = options.alphabetize || {} + const order = alphabetize.order || 'ignore' + const orderImportKind = alphabetize.orderImportKind || 'ignore' + const caseInsensitive = alphabetize.caseInsensitive || false - return { order, orderImportKind, caseInsensitive }; + return { order, orderImportKind, caseInsensitive } } // TODO, semver-major: Change the default of "distinctGroup" from true to false -const defaultDistinctGroup = true; +const defaultDistinctGroup = true module.exports = { meta: { @@ -625,12 +743,7 @@ module.exports = { }, }, 'newlines-between': { - enum: [ - 'ignore', - 'always', - 'always-and-inside-groups', - 'never', - ], + enum: ['ignore', 'always', 'always-and-inside-groups', 'never'], }, alphabetize: { type: 'object', @@ -661,44 +774,57 @@ module.exports = { }, create: function importOrderRule(context) { - const options = context.options[0] || {}; - const newlinesBetweenImports = options['newlines-between'] || 'ignore'; - const pathGroupsExcludedImportTypes = new Set(options.pathGroupsExcludedImportTypes || ['builtin', 'external', 'object']); - const alphabetize = getAlphabetizeConfig(options); - const distinctGroup = options.distinctGroup == null ? defaultDistinctGroup : !!options.distinctGroup; - let ranks; + const options = context.options[0] || {} + const newlinesBetweenImports = options['newlines-between'] || 'ignore' + const pathGroupsExcludedImportTypes = new Set( + options.pathGroupsExcludedImportTypes || [ + 'builtin', + 'external', + 'object', + ], + ) + const alphabetize = getAlphabetizeConfig(options) + const distinctGroup = + options.distinctGroup == null + ? defaultDistinctGroup + : !!options.distinctGroup + let ranks try { - const { pathGroups, maxPosition } = convertPathGroupsForRanks(options.pathGroups || []); - const { groups, omittedTypes } = convertGroupsToRanks(options.groups || defaultGroups); + const { pathGroups, maxPosition } = convertPathGroupsForRanks( + options.pathGroups || [], + ) + const { groups, omittedTypes } = convertGroupsToRanks( + options.groups || defaultGroups, + ) ranks = { groups, omittedTypes, pathGroups, maxPosition, - }; + } } catch (error) { // Malformed configuration return { Program(node) { - context.report(node, error.message); + context.report(node, error.message) }, - }; + } } - const importMap = new Map(); + const importMap = new Map() function getBlockImports(node) { if (!importMap.has(node)) { - importMap.set(node, []); + importMap.set(node, []) } - return importMap.get(node); + return importMap.get(node) } return { ImportDeclaration: function handleImports(node) { // Ignoring unassigned imports unless warnOnUnassignedImports is set if (node.specifiers.length || options.warnOnUnassignedImports) { - const name = node.source.value; + const name = node.source.value registerNode( context, { @@ -710,25 +836,25 @@ module.exports = { ranks, getBlockImports(node.parent), pathGroupsExcludedImportTypes, - ); + ) } }, TSImportEqualsDeclaration: function handleImports(node) { - let displayName; - let value; - let type; + let displayName + let value + let type // skip "export import"s if (node.isExport) { - return; + return } if (node.moduleReference.type === 'TSExternalModuleReference') { - value = node.moduleReference.expression.value; - displayName = value; - type = 'import'; + value = node.moduleReference.expression.value + displayName = value + type = 'import' } else { - value = ''; - displayName = context.getSourceCode().getText(node.moduleReference); - type = 'import:object'; + value = '' + displayName = context.getSourceCode().getText(node.moduleReference) + type = 'import:object' } registerNode( context, @@ -741,17 +867,17 @@ module.exports = { ranks, getBlockImports(node.parent), pathGroupsExcludedImportTypes, - ); + ) }, CallExpression: function handleRequires(node) { if (!isStaticRequire(node)) { - return; + return } - const block = getRequireBlock(node); + const block = getRequireBlock(node) if (!block) { - return; + return } - const name = node.arguments[0].value; + const name = node.arguments[0].value registerNode( context, { @@ -763,23 +889,28 @@ module.exports = { ranks, getBlockImports(block), pathGroupsExcludedImportTypes, - ); + ) }, 'Program:exit': function reportAndReset() { - importMap.forEach((imported) => { + importMap.forEach(imported => { if (newlinesBetweenImports !== 'ignore') { - makeNewlinesBetweenReport(context, imported, newlinesBetweenImports, distinctGroup); + makeNewlinesBetweenReport( + context, + imported, + newlinesBetweenImports, + distinctGroup, + ) } if (alphabetize.order !== 'ignore') { - mutateRanksToAlphabetize(imported, alphabetize); + mutateRanksToAlphabetize(imported, alphabetize) } - makeOutOfOrderReport(context, imported); - }); + makeOutOfOrderReport(context, imported) + }) - importMap.clear(); + importMap.clear() }, - }; + } }, -}; +} diff --git a/src/rules/prefer-default-export.js b/src/rules/prefer-default-export.js index 581f02502..0cd148c31 100644 --- a/src/rules/prefer-default-export.js +++ b/src/rules/prefer-default-export.js @@ -1,116 +1,124 @@ -'use strict'; +'use strict' -import docsUrl from '../docsUrl'; +import docsUrl from '../docsUrl' -const SINGLE_EXPORT_ERROR_MESSAGE = 'Prefer default export on a file with single export.'; -const ANY_EXPORT_ERROR_MESSAGE = 'Prefer default export to be present on every file that has export.'; +const SINGLE_EXPORT_ERROR_MESSAGE = + 'Prefer default export on a file with single export.' +const ANY_EXPORT_ERROR_MESSAGE = + 'Prefer default export to be present on every file that has export.' module.exports = { meta: { type: 'suggestion', docs: { category: 'Style guide', - description: 'Prefer a default export if module exports a single name or multiple names.', + description: + 'Prefer a default export if module exports a single name or multiple names.', url: docsUrl('prefer-default-export'), }, - schema: [{ - type: 'object', - properties: { - target: { - type: 'string', - enum: ['single', 'any'], - default: 'single', + schema: [ + { + type: 'object', + properties: { + target: { + type: 'string', + enum: ['single', 'any'], + default: 'single', + }, }, + additionalProperties: false, }, - additionalProperties: false, - }], + ], }, create(context) { - let specifierExportCount = 0; - let hasDefaultExport = false; - let hasStarExport = false; - let hasTypeExport = false; - let namedExportNode = null; + let specifierExportCount = 0 + let hasDefaultExport = false + let hasStarExport = false + let hasTypeExport = false + let namedExportNode = null // get options. by default we look into files with single export - const { target = 'single' } = context.options[0] || {}; + const { target = 'single' } = context.options[0] || {} function captureDeclaration(identifierOrPattern) { if (identifierOrPattern && identifierOrPattern.type === 'ObjectPattern') { // recursively capture - identifierOrPattern.properties - .forEach(function (property) { - captureDeclaration(property.value); - }); - } else if (identifierOrPattern && identifierOrPattern.type === 'ArrayPattern') { - identifierOrPattern.elements - .forEach(captureDeclaration); - } else { - // assume it's a single standard identifier - specifierExportCount++; + identifierOrPattern.properties.forEach(function (property) { + captureDeclaration(property.value) + }) + } else if ( + identifierOrPattern && + identifierOrPattern.type === 'ArrayPattern' + ) { + identifierOrPattern.elements.forEach(captureDeclaration) + } else { + // assume it's a single standard identifier + specifierExportCount++ } } return { ExportDefaultSpecifier() { - hasDefaultExport = true; + hasDefaultExport = true }, ExportSpecifier(node) { if ((node.exported.name || node.exported.value) === 'default') { - hasDefaultExport = true; + hasDefaultExport = true } else { - specifierExportCount++; - namedExportNode = node; + specifierExportCount++ + namedExportNode = node } }, ExportNamedDeclaration(node) { // if there are specifiers, node.declaration should be null - if (!node.declaration) { return; } + if (!node.declaration) { + return + } - const { type } = node.declaration; + const { type } = node.declaration if ( - type === 'TSTypeAliasDeclaration' - || type === 'TypeAlias' - || type === 'TSInterfaceDeclaration' - || type === 'InterfaceDeclaration' + type === 'TSTypeAliasDeclaration' || + type === 'TypeAlias' || + type === 'TSInterfaceDeclaration' || + type === 'InterfaceDeclaration' ) { - specifierExportCount++; - hasTypeExport = true; - return; + specifierExportCount++ + hasTypeExport = true + return } if (node.declaration.declarations) { node.declaration.declarations.forEach(function (declaration) { - captureDeclaration(declaration.id); - }); + captureDeclaration(declaration.id) + }) } else { // captures 'export function foo() {}' syntax - specifierExportCount++; + specifierExportCount++ } - namedExportNode = node; + namedExportNode = node }, ExportDefaultDeclaration() { - hasDefaultExport = true; + hasDefaultExport = true }, ExportAllDeclaration() { - hasStarExport = true; + hasStarExport = true }, 'Program:exit'() { if (hasDefaultExport || hasStarExport || hasTypeExport) { - return; + return } if (target === 'single' && specifierExportCount === 1) { - context.report(namedExportNode, SINGLE_EXPORT_ERROR_MESSAGE); + context.report(namedExportNode, SINGLE_EXPORT_ERROR_MESSAGE) } else if (target === 'any' && specifierExportCount > 0) { - context.report(namedExportNode, ANY_EXPORT_ERROR_MESSAGE); + context.report(namedExportNode, ANY_EXPORT_ERROR_MESSAGE) } }, - }; + } }, -}; +} diff --git a/src/rules/unambiguous.js b/src/rules/unambiguous.js index 91152ea2a..ec1d4d2d4 100644 --- a/src/rules/unambiguous.js +++ b/src/rules/unambiguous.js @@ -3,15 +3,16 @@ * @author Ben Mosher */ -import { isModule } from 'eslint-module-utils/unambiguous'; -import docsUrl from '../docsUrl'; +import { isModule } from '../utils/unambiguous' +import docsUrl from '../docsUrl' module.exports = { meta: { type: 'suggestion', docs: { category: 'Module systems', - description: 'Forbid potentially ambiguous parse goal (`script` vs. `module`).', + description: + 'Forbid potentially ambiguous parse goal (`script` vs. `module`).', url: docsUrl('unambiguous'), }, schema: [], @@ -20,7 +21,7 @@ module.exports = { create(context) { // ignore non-modules if (context.parserOptions.sourceType !== 'module') { - return {}; + return {} } return { @@ -29,10 +30,9 @@ module.exports = { context.report({ node: ast, message: 'This module could be parsed as a valid script.', - }); + }) } }, - }; - + } }, -}; +} diff --git a/src/utils/ModuleCache.d.ts b/src/utils/ModuleCache.d.ts new file mode 100644 index 000000000..4ea4075cb --- /dev/null +++ b/src/utils/ModuleCache.d.ts @@ -0,0 +1,24 @@ +import type { ESLintSettings } from './types' + +export type CacheKey = unknown +export type CacheObject = { + result: unknown + lastSeen: ReturnType +} + +declare class ModuleCache { + map: Map + + constructor(map?: Map) + + get(cacheKey: CacheKey, settings: ESLintSettings): T | undefined + + set(cacheKey: CacheKey, result: T): T + + static getSettings( + settings: ESLintSettings, + ): { lifetime: number } & Omit +} +export default ModuleCache + +export type { ModuleCache } diff --git a/utils/ModuleCache.js b/src/utils/ModuleCache.js similarity index 54% rename from utils/ModuleCache.js rename to src/utils/ModuleCache.js index 24c76849d..851f5d359 100644 --- a/utils/ModuleCache.js +++ b/src/utils/ModuleCache.js @@ -1,52 +1,60 @@ -'use strict'; +'use strict' -exports.__esModule = true; +exports.__esModule = true -const log = require('debug')('eslint-module-utils:ModuleCache'); +const log = require('debug')('eslint-plugin-import-x:utils:ModuleCache') /** @type {import('./ModuleCache').ModuleCache} */ class ModuleCache { /** @param {typeof import('./ModuleCache').ModuleCache.prototype.map} map */ constructor(map) { - this.map = map || /** @type {{typeof import('./ModuleCache').ModuleCache.prototype.map} */ new Map(); + this.map = + map || + /** @type {{typeof import('./ModuleCache').ModuleCache.prototype.map} */ new Map() } /** @type {typeof import('./ModuleCache').ModuleCache.prototype.set} */ set(cacheKey, result) { - this.map.set(cacheKey, { result, lastSeen: process.hrtime() }); - log('setting entry for', cacheKey); - return result; + this.map.set(cacheKey, { result, lastSeen: process.hrtime() }) + log('setting entry for', cacheKey) + return result } /** @type {typeof import('./ModuleCache').ModuleCache.prototype.get} */ get(cacheKey, settings) { if (this.map.has(cacheKey)) { - const f = this.map.get(cacheKey); + const f = this.map.get(cacheKey) // check freshness // @ts-expect-error TS can't narrow properly from `has` and `get` - if (process.hrtime(f.lastSeen)[0] < settings.lifetime) { return f.result; } + if (process.hrtime(f.lastSeen)[0] < settings.lifetime) { + return f.result + } } else { - log('cache miss for', cacheKey); + log('cache miss for', cacheKey) } // cache miss - return undefined; + return undefined } /** @type {typeof import('./ModuleCache').ModuleCache.getSettings} */ static getSettings(settings) { /** @type {ReturnType} */ - const cacheSettings = Object.assign({ - lifetime: 30, // seconds - }, settings['import/cache']); + const cacheSettings = { + lifetime: 30, // seconds + ...settings['import-x/cache'], + } // parse infinity // @ts-expect-error the lack of type overlap is because we're abusing `cacheSettings` as a temporary object - if (cacheSettings.lifetime === '∞' || cacheSettings.lifetime === 'Infinity') { - cacheSettings.lifetime = Infinity; + if ( + cacheSettings.lifetime === '∞' || + cacheSettings.lifetime === 'Infinity' + ) { + cacheSettings.lifetime = Infinity } - return cacheSettings; + return cacheSettings } } -exports.default = ModuleCache; +exports.default = ModuleCache diff --git a/src/utils/declaredScope.d.ts b/src/utils/declaredScope.d.ts new file mode 100644 index 000000000..1ff5aea3a --- /dev/null +++ b/src/utils/declaredScope.d.ts @@ -0,0 +1,8 @@ +import { Rule, Scope } from 'eslint' + +declare function declaredScope( + context: Rule.RuleContext, + name: string, +): Scope.Scope['type'] | undefined + +export default declaredScope diff --git a/src/utils/declaredScope.js b/src/utils/declaredScope.js new file mode 100644 index 000000000..56e689394 --- /dev/null +++ b/src/utils/declaredScope.js @@ -0,0 +1,13 @@ +'use strict' + +exports.__esModule = true + +/** @type {import('./declaredScope').default} */ +exports.default = function declaredScope(context, name) { + const references = context.getScope().references + const reference = references.find(x => x.identifier.name === name) + if (!reference || !reference.resolved) { + return undefined + } + return reference.resolved.scope.type +} diff --git a/src/utils/hash.d.ts b/src/utils/hash.d.ts new file mode 100644 index 000000000..611a33f79 --- /dev/null +++ b/src/utils/hash.d.ts @@ -0,0 +1,14 @@ +import type { Hash } from 'crypto' + +declare function hashArray(value: Array, hash?: Hash): Hash + +declare function hashObject(value: T, hash?: Hash): Hash + +declare function hashify( + value: Array | object | unknown, + hash?: Hash, +): Hash + +export default hashify + +export { hashArray, hashObject } diff --git a/src/utils/hash.js b/src/utils/hash.js new file mode 100644 index 000000000..159e05920 --- /dev/null +++ b/src/utils/hash.js @@ -0,0 +1,69 @@ +/** + * utilities for hashing config objects. + * basically iteratively updates hash with a JSON-like format + */ + +'use strict' + +exports.__esModule = true + +const createHash = require('crypto').createHash + +const stringify = JSON.stringify + +/** @type {import('./hash').default} */ +function hashify(value, hash) { + if (!hash) { + hash = createHash('sha256') + } + + if (Array.isArray(value)) { + hashArray(value, hash) + } else if (value instanceof Object) { + hashObject(value, hash) + } else { + hash.update(stringify(value) || 'undefined') + } + + return hash +} +exports.default = hashify + +/** @type {import('./hash').hashArray} */ +function hashArray(array, hash) { + if (!hash) { + hash = createHash('sha256') + } + + hash.update('[') + for (let i = 0; i < array.length; i++) { + hashify(array[i], hash) + hash.update(',') + } + hash.update(']') + + return hash +} +hashify.array = hashArray +exports.hashArray = hashArray + +/** @type {import('./hash').hashObject} */ +function hashObject(object, optionalHash) { + const hash = optionalHash || createHash('sha256') + + hash.update('{') + Object.keys(object) + .sort() + .forEach(key => { + hash.update(stringify(key)) + hash.update(':') + // @ts-expect-error the key is guaranteed to exist on the object here + hashify(object[key], hash) + hash.update(',') + }) + hash.update('}') + + return hash +} +hashify.object = hashObject +exports.hashObject = hashObject diff --git a/src/utils/ignore.d.ts b/src/utils/ignore.d.ts new file mode 100644 index 000000000..6e97a88e6 --- /dev/null +++ b/src/utils/ignore.d.ts @@ -0,0 +1,15 @@ +import { Rule } from 'eslint' +import type { ESLintSettings, Extension } from './types' + +declare function ignore(path: string, context: Rule.RuleContext): boolean + +declare function getFileExtensions(settings: ESLintSettings): Set + +declare function hasValidExtension( + path: string, + context: Rule.RuleContext, +): path is `${string}${Extension}` + +export default ignore + +export { getFileExtensions, hasValidExtension } diff --git a/src/utils/ignore.js b/src/utils/ignore.js new file mode 100644 index 000000000..507830dd5 --- /dev/null +++ b/src/utils/ignore.js @@ -0,0 +1,75 @@ +'use strict' + +exports.__esModule = true + +const extname = require('path').extname + +const log = require('debug')('eslint-plugin-import-x:utils:ignore') + +// one-shot memoized +/** @type {Set} */ let cachedSet +/** @type {import('./types').ESLintSettings} */ let lastSettings + +/** @type {(context: import('eslint').Rule.RuleContext) => Set} */ +function validExtensions(context) { + if (cachedSet && context.settings === lastSettings) { + return cachedSet + } + + lastSettings = context.settings + cachedSet = makeValidExtensionSet(context.settings) + return cachedSet +} + +/** @type {import('./ignore').getFileExtensions} */ +function makeValidExtensionSet(settings) { + // start with explicit JS-parsed extensions + /** @type {Set} */ + const exts = new Set(settings['import-x/extensions'] || ['.js']) + + // all alternate parser extensions are also valid + if ('import-x/parsers' in settings) { + for (const parser in settings['import-x/parsers']) { + const parserSettings = settings['import-x/parsers'][parser] + if (!Array.isArray(parserSettings)) { + throw new TypeError(`"settings" for ${parser} must be an array`) + } + parserSettings.forEach(ext => exts.add(ext)) + } + } + + return exts +} +exports.getFileExtensions = makeValidExtensionSet + +/** @type {import('./ignore').default} */ +exports.default = function ignore(path, context) { + // check extension whitelist first (cheap) + if (!hasValidExtension(path, context)) { + return true + } + + if (!('import-x/ignore' in context.settings)) { + return false + } + const ignoreStrings = context.settings['import-x/ignore'] + + for (let i = 0; i < ignoreStrings.length; i++) { + const regex = new RegExp(ignoreStrings[i]) + if (regex.test(path)) { + log(`ignoring ${path}, matched pattern /${ignoreStrings[i]}/`) + return true + } + } + + return false +} + +/** @type {import('./ignore').hasValidExtension} */ +function hasValidExtension(path, context) { + // eslint-disable-next-line no-extra-parens + return validExtensions(context).has( + /** @type {import('./types').Extension} */ (extname(path)), + ) +} +exports.hasValidExtension = hasValidExtension diff --git a/src/utils/module-require.d.ts b/src/utils/module-require.d.ts new file mode 100644 index 000000000..a0b74929d --- /dev/null +++ b/src/utils/module-require.d.ts @@ -0,0 +1,3 @@ +declare function moduleRequire(p: string): T + +export default moduleRequire diff --git a/src/utils/module-require.js b/src/utils/module-require.js new file mode 100644 index 000000000..05e2501b4 --- /dev/null +++ b/src/utils/module-require.js @@ -0,0 +1,40 @@ +'use strict' + +exports.__esModule = true + +const Module = require('module') +const path = require('path') + +// borrowed from @babel/eslint-parser +/** @type {(filename: string) => Module} */ +function createModule(filename) { + const mod = new Module(filename) + mod.filename = filename + // @ts-expect-error _nodeModulesPaths are undocumented + mod.paths = Module._nodeModulePaths(path.dirname(filename)) + return mod +} + +/** @type {import('./module-require').default} */ +exports.default = function moduleRequire(p) { + try { + // attempt to get espree relative to eslint + const eslintPath = require.resolve('eslint') + const eslintModule = createModule(eslintPath) + // @ts-expect-error _resolveFilename is undocumented + return require(Module._resolveFilename(p, eslintModule)) + } catch (err) { + /* ignore */ + } + + try { + // try relative to entry point + // @ts-expect-error TODO: figure out what this is + return require.main.require(p) + } catch (err) { + /* ignore */ + } + + // finally, try from here + return require(p) +} diff --git a/src/utils/moduleVisitor.d.ts b/src/utils/moduleVisitor.d.ts new file mode 100644 index 000000000..2ce668135 --- /dev/null +++ b/src/utils/moduleVisitor.d.ts @@ -0,0 +1,26 @@ +import type { Rule } from 'eslint' +import type { Node } from 'estree' + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Visitor = (source: Node, importer: unknown) => any + +type Options = { + amd?: boolean + commonjs?: boolean + esmodule?: boolean + ignore?: string[] +} + +declare function moduleVisitor(visitor: Visitor, options?: Options): object + +export default moduleVisitor + +export type Schema = NonNullable + +declare function makeOptionsSchema( + additionalProperties?: Partial, +): Schema + +declare const optionsSchema: Schema + +export { makeOptionsSchema, optionsSchema } diff --git a/utils/moduleVisitor.js b/src/utils/moduleVisitor.js similarity index 54% rename from utils/moduleVisitor.js rename to src/utils/moduleVisitor.js index acdee6774..1a96c71d2 100644 --- a/utils/moduleVisitor.js +++ b/src/utils/moduleVisitor.js @@ -1,6 +1,6 @@ -'use strict'; +'use strict' -exports.__esModule = true; +exports.__esModule = true /** @typedef {import('estree').Node} Node */ /** @typedef {{ arguments: import('estree').CallExpression['arguments'], callee: Node }} Call */ @@ -13,96 +13,129 @@ exports.__esModule = true; * @type {(import('./moduleVisitor').default)} */ exports.default = function visitModules(visitor, options) { - const ignore = options && options.ignore; - const amd = !!(options && options.amd); - const commonjs = !!(options && options.commonjs); + const ignore = options && options.ignore + const amd = !!(options && options.amd) + const commonjs = !!(options && options.commonjs) // if esmodule is not explicitly disabled, it is assumed to be enabled - const esmodule = !!Object.assign({ esmodule: true }, options).esmodule; + const esmodule = !!{ esmodule: true, ...options }.esmodule - const ignoreRegExps = ignore == null ? [] : ignore.map((p) => new RegExp(p)); + const ignoreRegExps = ignore == null ? [] : ignore.map(p => new RegExp(p)) /** @type {(source: undefined | null | import('estree').Literal, importer: Parameters[1]) => void} */ function checkSourceValue(source, importer) { - if (source == null) { return; } //? + if (source == null) { + return + } //? // handle ignore - if (ignoreRegExps.some((re) => re.test(String(source.value)))) { return; } + if (ignoreRegExps.some(re => re.test(String(source.value)))) { + return + } // fire visitor - visitor(source, importer); + visitor(source, importer) } // for import-y declarations /** @type {(node: Declaration) => void} */ function checkSource(node) { - checkSourceValue(node.source, node); + checkSourceValue(node.source, node) } // for esmodule dynamic `import()` calls /** @type {(node: import('estree').ImportExpression | import('estree').CallExpression) => void} */ function checkImportCall(node) { /** @type {import('estree').Expression | import('estree').Literal | import('estree').CallExpression['arguments'][0]} */ - let modulePath; + let modulePath // refs https://github.com/estree/estree/blob/HEAD/es2020.md#importexpression if (node.type === 'ImportExpression') { - modulePath = node.source; + modulePath = node.source } else if (node.type === 'CallExpression') { // @ts-expect-error this structure is from an older version of eslint - if (node.callee.type !== 'Import') { return; } - if (node.arguments.length !== 1) { return; } + if (node.callee.type !== 'Import') { + return + } + if (node.arguments.length !== 1) { + return + } - modulePath = node.arguments[0]; + modulePath = node.arguments[0] } else { - throw new TypeError('this should be unreachable'); + throw new TypeError('this should be unreachable') } - if (modulePath.type !== 'Literal') { return; } - if (typeof modulePath.value !== 'string') { return; } + if (modulePath.type !== 'Literal') { + return + } + if (typeof modulePath.value !== 'string') { + return + } - checkSourceValue(modulePath, node); + checkSourceValue(modulePath, node) } // for CommonJS `require` calls // adapted from @mctep: https://git.io/v4rAu /** @type {(call: Call) => void} */ function checkCommon(call) { - if (call.callee.type !== 'Identifier') { return; } - if (call.callee.name !== 'require') { return; } - if (call.arguments.length !== 1) { return; } + if (call.callee.type !== 'Identifier') { + return + } + if (call.callee.name !== 'require') { + return + } + if (call.arguments.length !== 1) { + return + } - const modulePath = call.arguments[0]; - if (modulePath.type !== 'Literal') { return; } - if (typeof modulePath.value !== 'string') { return; } + const modulePath = call.arguments[0] + if (modulePath.type !== 'Literal') { + return + } + if (typeof modulePath.value !== 'string') { + return + } - checkSourceValue(modulePath, call); + checkSourceValue(modulePath, call) } /** @type {(call: Call) => void} */ function checkAMD(call) { - if (call.callee.type !== 'Identifier') { return; } - if (call.callee.name !== 'require' && call.callee.name !== 'define') { return; } - if (call.arguments.length !== 2) { return; } + if (call.callee.type !== 'Identifier') { + return + } + if (call.callee.name !== 'require' && call.callee.name !== 'define') { + return + } + if (call.arguments.length !== 2) { + return + } - const modules = call.arguments[0]; - if (modules.type !== 'ArrayExpression') { return; } + const modules = call.arguments[0] + if (modules.type !== 'ArrayExpression') { + return + } for (const element of modules.elements) { - if (!element) { continue; } - if (element.type !== 'Literal') { continue; } - if (typeof element.value !== 'string') { continue; } - - if ( - element.value === 'require' - || element.value === 'exports' - ) { - continue; // magic modules: https://github.com/requirejs/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#magic-modules + if (!element) { + continue + } + if (element.type !== 'Literal') { + continue + } + if (typeof element.value !== 'string') { + continue + } + + if (element.value === 'require' || element.value === 'exports') { + continue // magic modules: https://github.com/requirejs/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#magic-modules } - checkSourceValue(element, element); + checkSourceValue(element, element) } } - const visitors = {}; + const visitors = {} if (esmodule) { Object.assign(visitors, { ImportDeclaration: checkSource, @@ -110,20 +143,28 @@ exports.default = function visitModules(visitor, options) { ExportAllDeclaration: checkSource, CallExpression: checkImportCall, ImportExpression: checkImportCall, - }); + }) } if (commonjs || amd) { - const currentCallExpression = visitors.CallExpression; - visitors.CallExpression = /** @type {(call: Call) => void} */ function (call) { - if (currentCallExpression) { currentCallExpression(call); } - if (commonjs) { checkCommon(call); } - if (amd) { checkAMD(call); } - }; + const currentCallExpression = visitors.CallExpression + visitors.CallExpression = /** @type {(call: Call) => void} */ function ( + call, + ) { + if (currentCallExpression) { + currentCallExpression(call) + } + if (commonjs) { + checkCommon(call) + } + if (amd) { + checkAMD(call) + } + } } - return visitors; -}; + return visitors +} /** * make an options schema for the module visitor, optionally adding extra fields. @@ -131,7 +172,7 @@ exports.default = function visitModules(visitor, options) { */ function makeOptionsSchema(additionalProperties) { /** @type {import('./moduleVisitor').Schema} */ - const base = { + const base = { type: 'object', properties: { commonjs: { type: 'boolean' }, @@ -145,20 +186,20 @@ function makeOptionsSchema(additionalProperties) { }, }, additionalProperties: false, - }; + } if (additionalProperties) { for (const key in additionalProperties) { // @ts-expect-error TS always has trouble with arbitrary object assignment/mutation - base.properties[key] = additionalProperties[key]; + base.properties[key] = additionalProperties[key] } } - return base; + return base } -exports.makeOptionsSchema = makeOptionsSchema; +exports.makeOptionsSchema = makeOptionsSchema /** * json schema object for options parameter. can be used to build rule options schema object. */ -exports.optionsSchema = makeOptionsSchema(); +exports.optionsSchema = makeOptionsSchema() diff --git a/src/utils/parse.d.ts b/src/utils/parse.d.ts new file mode 100644 index 000000000..e116aeb38 --- /dev/null +++ b/src/utils/parse.d.ts @@ -0,0 +1,9 @@ +import { AST, Rule } from 'eslint' + +declare function parse( + path: string, + content: string, + context: Rule.RuleContext, +): AST.Program | null | undefined + +export default parse diff --git a/utils/parse.js b/src/utils/parse.js similarity index 52% rename from utils/parse.js rename to src/utils/parse.js index 804186ca9..4a9877af6 100644 --- a/utils/parse.js +++ b/src/utils/parse.js @@ -1,41 +1,47 @@ -'use strict'; +'use strict' -exports.__esModule = true; +exports.__esModule = true /** @typedef {`.${string}`} Extension */ -/** @typedef {NonNullable & { 'import/extensions'?: Extension[], 'import/parsers'?: { [k: string]: Extension[] }, 'import/cache'?: { lifetime: number | '∞' | 'Infinity' } }} ESLintSettings */ +/** @typedef {NonNullable & { 'import-x/extensions'?: Extension[], 'import-x/parsers'?: { [k: string]: Extension[] }, 'import-x/cache'?: { lifetime: number | '∞' | 'Infinity' } }} ESLintSettings */ -const moduleRequire = require('./module-require').default; -const extname = require('path').extname; -const fs = require('fs'); +const moduleRequire = require('./module-require').default +const extname = require('path').extname +const fs = require('fs') -const log = require('debug')('eslint-plugin-import:parse'); +const log = require('debug')('eslint-plugin-import-x:parse') /** @type {(parserPath: NonNullable) => unknown} */ function getBabelEslintVisitorKeys(parserPath) { if (parserPath.endsWith('index.js')) { - const hypotheticalLocation = parserPath.replace('index.js', 'visitor-keys.js'); + const hypotheticalLocation = parserPath.replace( + 'index.js', + 'visitor-keys.js', + ) if (fs.existsSync(hypotheticalLocation)) { - const keys = moduleRequire(hypotheticalLocation); - return keys.default || keys; + const keys = moduleRequire(hypotheticalLocation) + return keys.default || keys } } - return null; + return null } /** @type {(parserPath: import('eslint').Rule.RuleContext['parserPath'], parserInstance: { VisitorKeys: unknown }, parsedResult?: { visitorKeys?: unknown }) => unknown} */ function keysFromParser(parserPath, parserInstance, parsedResult) { // Exposed by @typescript-eslint/parser and @babel/eslint-parser if (parsedResult && parsedResult.visitorKeys) { - return parsedResult.visitorKeys; + return parsedResult.visitorKeys } - if (typeof parserPath === 'string' && (/.*espree.*/).test(parserPath)) { - return parserInstance.VisitorKeys; + if (typeof parserPath === 'string' && /.*espree.*/.test(parserPath)) { + return parserInstance.VisitorKeys } - if (typeof parserPath === 'string' && (/.*babel-eslint.*/).test(parserPath)) { - return getBabelEslintVisitorKeys(parserPath); + if ( + typeof parserPath === 'string' && + /.*@babel\/eslint-parser.*/.test(parserPath) + ) { + return getBabelEslintVisitorKeys(parserPath) } - return null; + return null } // this exists to smooth over the unintentional breaking change in v2.7. @@ -44,131 +50,144 @@ function keysFromParser(parserPath, parserInstance, parsedResult) { function makeParseReturn(ast, visitorKeys) { if (ast) { // @ts-expect-error see TODO - ast.visitorKeys = visitorKeys; + ast.visitorKeys = visitorKeys // @ts-expect-error see TODO - ast.ast = ast; + ast.ast = ast } - return ast; + return ast } /** @type {(text: string) => string} */ function stripUnicodeBOM(text) { - return text.charCodeAt(0) === 0xFEFF ? text.slice(1) : text; + return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text } /** @type {(text: string) => string} */ function transformHashbang(text) { - return text.replace(/^#!([^\r\n]+)/u, (_, captured) => `//${captured}`); + return text.replace(/^#!([^\r\n]+)/u, (_, captured) => `//${captured}`) } /** @type {import('./parse').default} */ exports.default = function parse(path, content, context) { - if (context == null) { throw new Error('need context to parse properly'); } + if (context == null) { + throw new Error('need context to parse properly') + } // ESLint in "flat" mode only sets context.languageOptions.parserOptions - let parserOptions = context.languageOptions && context.languageOptions.parserOptions || context.parserOptions; - const parserOrPath = getParser(path, context); + let parserOptions = + (context.languageOptions && context.languageOptions.parserOptions) || + context.parserOptions + const parserOrPath = getParser(path, context) - if (!parserOrPath) { throw new Error('parserPath or languageOptions.parser is required!'); } + if (!parserOrPath) { + throw new Error('parserPath or languageOptions.parser is required!') + } // hack: espree blows up with frozen options - parserOptions = Object.assign({}, parserOptions); - parserOptions.ecmaFeatures = Object.assign({}, parserOptions.ecmaFeatures); + parserOptions = { ...parserOptions } + parserOptions.ecmaFeatures = { ...parserOptions.ecmaFeatures } // always include comments and tokens (for doc parsing) - parserOptions.comment = true; - parserOptions.attachComment = true; // keeping this for backward-compat with older parsers - parserOptions.tokens = true; + parserOptions.comment = true + parserOptions.attachComment = true // keeping this for backward-compat with older parsers + parserOptions.tokens = true // attach node locations - parserOptions.loc = true; - parserOptions.range = true; + parserOptions.loc = true + parserOptions.range = true // provide the `filePath` like eslint itself does, in `parserOptions` // https://github.com/eslint/eslint/blob/3ec436ee/lib/linter.js#L637 - parserOptions.filePath = path; + parserOptions.filePath = path // @typescript-eslint/parser will parse the entire project with typechecking if you provide // "project" or "projects" in parserOptions. Removing these options means the parser will // only parse one file in isolate mode, which is much, much faster. // https://github.com/import-js/eslint-plugin-import/issues/1408#issuecomment-509298962 - delete parserOptions.EXPERIMENTAL_useProjectService; - delete parserOptions.project; - delete parserOptions.projects; + delete parserOptions.EXPERIMENTAL_useProjectService + delete parserOptions.project + delete parserOptions.projects // require the parser relative to the main module (i.e., ESLint) - const parser = typeof parserOrPath === 'string' ? moduleRequire(parserOrPath) : parserOrPath; + const parser = + typeof parserOrPath === 'string' + ? moduleRequire(parserOrPath) + : parserOrPath // replicate bom strip and hashbang transform of ESLint // https://github.com/eslint/eslint/blob/b93af98b3c417225a027cabc964c38e779adb945/lib/linter/linter.js#L779 - content = transformHashbang(stripUnicodeBOM(String(content))); + content = transformHashbang(stripUnicodeBOM(String(content))) if (typeof parser.parseForESLint === 'function') { - let ast; + let ast try { - const parserRaw = parser.parseForESLint(content, parserOptions); - ast = parserRaw.ast; + const parserRaw = parser.parseForESLint(content, parserOptions) + ast = parserRaw.ast // @ts-expect-error TODO: FIXME - return makeParseReturn(ast, keysFromParser(parserOrPath, parser, parserRaw)); + return makeParseReturn( + ast, + keysFromParser(parserOrPath, parser, parserRaw), + ) } catch (e) { - console.warn(); - console.warn('Error while parsing ' + parserOptions.filePath); + console.warn() + console.warn(`Error while parsing ${parserOptions.filePath}`) // @ts-expect-error e is almost certainly an Error here - console.warn('Line ' + e.lineNumber + ', column ' + e.column + ': ' + e.message); + console.warn(`Line ${e.lineNumber}, column ${e.column}: ${e.message}`) } if (!ast || typeof ast !== 'object') { console.warn( // Can only be invalid for custom parser per imports/parser - '`parseForESLint` from parser `' + (typeof parserOrPath === 'string' ? parserOrPath : '`context.languageOptions.parser`') + '` is invalid and will just be ignored' - ); + `\`parseForESLint\` from parser \`${typeof parserOrPath === 'string' ? parserOrPath : '`context.languageOptions.parser`'}\` is invalid and will just be ignored`, + ) } else { // @ts-expect-error TODO: FIXME - return makeParseReturn(ast, keysFromParser(parserOrPath, parser, undefined)); + return makeParseReturn( + ast, + keysFromParser(parserOrPath, parser, undefined), + ) } } - const ast = parser.parse(content, parserOptions); + const ast = parser.parse(content, parserOptions) // @ts-expect-error TODO: FIXME - return makeParseReturn(ast, keysFromParser(parserOrPath, parser, undefined)); -}; + return makeParseReturn(ast, keysFromParser(parserOrPath, parser, undefined)) +} /** @type {(path: string, context: import('eslint').Rule.RuleContext) => string | null | (import('eslint').Linter.ParserModule)} */ function getParser(path, context) { - const parserPath = getParserPath(path, context); + const parserPath = getParserPath(path, context) if (parserPath) { - return parserPath; + return parserPath } if ( - !!context.languageOptions - && !!context.languageOptions.parser - && typeof context.languageOptions.parser !== 'string' - && ( + !!context.languageOptions && + !!context.languageOptions.parser && + typeof context.languageOptions.parser !== 'string' && + // @ts-expect-error TODO: figure out a better type + (typeof context.languageOptions.parser.parse === 'function' || // @ts-expect-error TODO: figure out a better type - typeof context.languageOptions.parser.parse === 'function' - // @ts-expect-error TODO: figure out a better type - || typeof context.languageOptions.parser.parseForESLint === 'function' - ) + typeof context.languageOptions.parser.parseForESLint === 'function') ) { - return context.languageOptions.parser; + return context.languageOptions.parser } - return null; + return null } /** @type {(path: string, context: import('eslint').Rule.RuleContext & { settings?: ESLintSettings }) => import('eslint').Rule.RuleContext['parserPath']} */ function getParserPath(path, context) { - const parsers = context.settings['import/parsers']; + const parsers = context.settings['import-x/parsers'] if (parsers != null) { // eslint-disable-next-line no-extra-parens - const extension = /** @type {Extension} */ (extname(path)); + const extension = /** @type {Extension} */ (extname(path)) for (const parserPath in parsers) { if (parsers[parserPath].indexOf(extension) > -1) { // use this alternate parser - log('using alt parser:', parserPath); - return parserPath; + log('using alt parser:', parserPath) + return parserPath } } } // default to use ESLint parser - return context.parserPath; + return context.parserPath } diff --git a/src/utils/pkgDir.d.ts b/src/utils/pkgDir.d.ts new file mode 100644 index 000000000..9b05edf5f --- /dev/null +++ b/src/utils/pkgDir.d.ts @@ -0,0 +1,3 @@ +declare function pkgDir(cwd: string): string | null + +export default pkgDir diff --git a/src/utils/pkgDir.js b/src/utils/pkgDir.js new file mode 100644 index 000000000..bcc5aa28b --- /dev/null +++ b/src/utils/pkgDir.js @@ -0,0 +1,12 @@ +'use strict' + +const path = require('path') +const pkgUp = require('./pkgUp').default + +exports.__esModule = true + +/** @type {import('./pkgDir').default} */ +exports.default = function (cwd) { + const fp = pkgUp({ cwd }) + return fp ? path.dirname(fp) : null +} diff --git a/src/utils/pkgUp.d.ts b/src/utils/pkgUp.d.ts new file mode 100644 index 000000000..2e9e96a23 --- /dev/null +++ b/src/utils/pkgUp.d.ts @@ -0,0 +1,3 @@ +declare function pkgUp(opts?: { cwd?: string }): string | null + +export default pkgUp diff --git a/utils/pkgUp.js b/src/utils/pkgUp.js similarity index 79% rename from utils/pkgUp.js rename to src/utils/pkgUp.js index 076e59fd7..031681d52 100644 --- a/utils/pkgUp.js +++ b/src/utils/pkgUp.js @@ -1,9 +1,9 @@ -'use strict'; +'use strict' -exports.__esModule = true; +exports.__esModule = true -const fs = require('fs'); -const path = require('path'); +const fs = require('fs') +const path = require('path') /** * Derived significantly from package find-up@2.0.0. See license below. @@ -34,28 +34,28 @@ const path = require('path'); /** @type {(filename: string | string[], cwd?: string) => string | null} */ function findUp(filename, cwd) { - let dir = path.resolve(cwd || ''); - const root = path.parse(dir).root; + let dir = path.resolve(cwd || '') + const root = path.parse(dir).root /** @type {string[]} */ // @ts-expect-error TS sucks with concat - const filenames = [].concat(filename); + const filenames = [].concat(filename) // eslint-disable-next-line no-constant-condition while (true) { - const file = filenames.find((el) => fs.existsSync(path.resolve(dir, el))); + const file = filenames.find(el => fs.existsSync(path.resolve(dir, el))) if (file) { - return path.join(dir, file); + return path.join(dir, file) } if (dir === root) { - return null; + return null } - dir = path.dirname(dir); + dir = path.dirname(dir) } } /** @type {import('./pkgUp').default} */ exports.default = function pkgUp(opts) { - return findUp('package.json', opts && opts.cwd); -}; + return findUp('package.json', opts && opts.cwd) +} diff --git a/src/utils/readPkgUp.d.ts b/src/utils/readPkgUp.d.ts new file mode 100644 index 000000000..0323252b9 --- /dev/null +++ b/src/utils/readPkgUp.d.ts @@ -0,0 +1,8 @@ +import pkgUp from './pkgUp' + +declare function readPkgUp( + opts?: Parameters[0], + // eslint-disable-next-line @typescript-eslint/ban-types +): {} | { pkg: string; path: string } + +export default readPkgUp diff --git a/utils/readPkgUp.js b/src/utils/readPkgUp.js similarity index 88% rename from utils/readPkgUp.js rename to src/utils/readPkgUp.js index 08371931f..0333fcad4 100644 --- a/utils/readPkgUp.js +++ b/src/utils/readPkgUp.js @@ -1,13 +1,13 @@ -'use strict'; +'use strict' -exports.__esModule = true; +exports.__esModule = true -const fs = require('fs'); -const pkgUp = require('./pkgUp').default; +const fs = require('fs') +const pkgUp = require('./pkgUp').default /** @type {(str: string) => string} */ function stripBOM(str) { - return str.replace(/^\uFEFF/, ''); + return str.replace(/^\uFEFF/, '') } /** @@ -38,18 +38,18 @@ function stripBOM(str) { */ /** @type {import('./readPkgUp').default} */ exports.default = function readPkgUp(opts) { - const fp = pkgUp(opts); + const fp = pkgUp(opts) if (!fp) { - return {}; + return {} } try { return { pkg: JSON.parse(stripBOM(fs.readFileSync(fp, { encoding: 'utf-8' }))), path: fp, - }; + } } catch (e) { - return {}; + return {} } -}; +} diff --git a/src/utils/resolve.d.ts b/src/utils/resolve.d.ts new file mode 100644 index 000000000..aec2a4ddf --- /dev/null +++ b/src/utils/resolve.d.ts @@ -0,0 +1,45 @@ +import type { Rule } from 'eslint' + +import type ModuleCache from './ModuleCache' +import type { ESLintSettings } from './types' + +export type ResultNotFound = { found: false; path?: undefined } +export type ResultFound = { found: true; path: string | null } +export type ResolvedResult = ResultNotFound | ResultFound + +export type ResolverResolve = ( + modulePath: string, + sourceFile: string, + config: unknown, +) => ResolvedResult +export type ResolverResolveImport = ( + modulePath: string, + sourceFile: string, + config: unknown, +) => string | undefined +export type Resolver = { + interfaceVersion?: 1 | 2 + resolve: ResolverResolve + resolveImport: ResolverResolveImport +} + +declare function resolve( + p: string, + context: Rule.RuleContext, +): ResolvedResult['path'] + +export default resolve + +declare function fileExistsWithCaseSync( + filepath: string | null, + cacheSettings: ESLintSettings, + strict: boolean, +): boolean | ReturnType + +declare function relative( + modulePath: string, + sourceFile: string, + settings: ESLintSettings, +): ResolvedResult['path'] + +export { fileExistsWithCaseSync, relative } diff --git a/src/utils/resolve.js b/src/utils/resolve.js new file mode 100644 index 000000000..2b18bacdb --- /dev/null +++ b/src/utils/resolve.js @@ -0,0 +1,274 @@ +'use strict' + +exports.__esModule = true + +const fs = require('fs') +const { createRequire } = require('module') +const path = require('path') + +const hashObject = require('./hash').hashObject +const ModuleCache = require('./ModuleCache').default +const pkgDir = require('./pkgDir').default + +const CASE_SENSITIVE_FS = !fs.existsSync( + path.join(__dirname.toUpperCase(), 'reSOLVE.js'), +) +exports.CASE_SENSITIVE_FS = CASE_SENSITIVE_FS + +const ERROR_NAME = 'EslintPluginImportResolveError' + +const fileExistsCache = new ModuleCache() + +/** @type {(target: T, sourceFile?: string | null | undefined) => undefined | ReturnType} */ +function tryRequire(target, sourceFile) { + let resolved + try { + // Check if the target exists + if (sourceFile != null) { + try { + resolved = createRequire(path.resolve(sourceFile)).resolve(target) + } catch (e) { + resolved = require.resolve(target) + } + } else { + resolved = require.resolve(target) + } + } catch (e) { + // If the target does not exist then just return undefined + return undefined + } + + // If the target exists then return the loaded module + return require(resolved) +} + +// https://stackoverflow.com/a/27382838 +/** @type {import('./resolve').fileExistsWithCaseSync} */ +exports.fileExistsWithCaseSync = function fileExistsWithCaseSync( + filepath, + cacheSettings, + strict, +) { + // don't care if the FS is case-sensitive + if (CASE_SENSITIVE_FS) { + return true + } + + // null means it resolved to a builtin + if (filepath === null) { + return true + } + if (filepath.toLowerCase() === process.cwd().toLowerCase() && !strict) { + return true + } + const parsedPath = path.parse(filepath) + const dir = parsedPath.dir + + let result = fileExistsCache.get(filepath, cacheSettings) + if (result != null) { + return result + } + + // base case + if (dir === '' || parsedPath.root === filepath) { + result = true + } else { + const filenames = fs.readdirSync(dir) + if (filenames.indexOf(parsedPath.base) === -1) { + result = false + } else { + result = fileExistsWithCaseSync(dir, cacheSettings, strict) + } + } + fileExistsCache.set(filepath, result) + return result +} + +/** @type {import('./types').ESLintSettings | null} */ +let prevSettings = null +let memoizedHash = '' +/** @type {(modulePath: string, sourceFile: string, settings: import('./types').ESLintSettings) => import('./resolve').ResolvedResult} */ +function fullResolve(modulePath, sourceFile, settings) { + // check if this is a bonus core module + const coreSet = new Set(settings['import-x/core-modules']) + if (coreSet.has(modulePath)) { + return { found: true, path: null } + } + + const sourceDir = path.dirname(sourceFile) + + if (prevSettings !== settings) { + memoizedHash = hashObject(settings).digest('hex') + prevSettings = settings + } + + const cacheKey = sourceDir + memoizedHash + modulePath + + const cacheSettings = ModuleCache.getSettings(settings) + + const cachedPath = fileExistsCache.get(cacheKey, cacheSettings) + if (cachedPath !== undefined) { + return { found: true, path: cachedPath } + } + + /** @type {(resolvedPath: string | null) => void} */ + function cache(resolvedPath) { + fileExistsCache.set(cacheKey, resolvedPath) + } + + /** @type {(resolver: import('./resolve').Resolver, config: unknown) => import('./resolve').ResolvedResult} */ + function withResolver(resolver, config) { + if (resolver.interfaceVersion === 2) { + return resolver.resolve(modulePath, sourceFile, config) + } + + try { + const resolved = resolver.resolveImport(modulePath, sourceFile, config) + if (resolved === undefined) { + return { found: false } + } + return { found: true, path: resolved } + } catch (err) { + return { found: false } + } + } + + const configResolvers = settings['import-x/resolver'] || { + node: settings['import-x/resolve'], + } // backward compatibility + + const resolvers = resolverReducer(configResolvers, new Map()) + + for (const pair of resolvers) { + const name = pair[0] + const config = pair[1] + const resolver = requireResolver(name, sourceFile) + const resolved = withResolver(resolver, config) + + if (!resolved.found) { + continue + } + + // else, counts + cache(resolved.path) + return resolved + } + + // failed + // cache(undefined) + return { found: false } +} + +/** @type {import('./resolve').relative} */ +function relative(modulePath, sourceFile, settings) { + return fullResolve(modulePath, sourceFile, settings).path +} +exports.relative = relative + +/** @type {>(resolvers: string[] | string | { [k: string]: string }, map: T) => T} */ +function resolverReducer(resolvers, map) { + if (Array.isArray(resolvers)) { + resolvers.forEach(r => resolverReducer(r, map)) + return map + } + + if (typeof resolvers === 'string') { + map.set(resolvers, null) + return map + } + + if (typeof resolvers === 'object') { + for (const key in resolvers) { + map.set(key, resolvers[key]) + } + return map + } + + const err = new Error('invalid resolver config') + err.name = ERROR_NAME + throw err +} + +/** @type {(sourceFile: string) => string} */ +function getBaseDir(sourceFile) { + return pkgDir(sourceFile) || process.cwd() +} + +/** @type {(name: string, sourceFile: string) => import('./resolve').Resolver} */ +function requireResolver(name, sourceFile) { + // Try to resolve package with conventional name + const resolver = + tryRequire(`eslint-import-resolver-${name}`, sourceFile) || + tryRequire(name, sourceFile) || + tryRequire(path.resolve(getBaseDir(sourceFile), name)) + + if (!resolver) { + const err = new Error(`unable to load resolver "${name}".`) + err.name = ERROR_NAME + throw err + } + if (!isResolverValid(resolver)) { + const err = new Error(`${name} with invalid interface loaded as resolver`) + err.name = ERROR_NAME + throw err + } + + return resolver +} + +/** @type {(resolver: object) => resolver is import('./resolve').Resolver} */ +function isResolverValid(resolver) { + if ('interfaceVersion' in resolver && resolver.interfaceVersion === 2) { + return ( + 'resolve' in resolver && + !!resolver.resolve && + typeof resolver.resolve === 'function' + ) + } + return ( + 'resolveImport' in resolver && + !!resolver.resolveImport && + typeof resolver.resolveImport === 'function' + ) +} + +/** @type {Set} */ +const erroredContexts = new Set() + +/** + * Given + * @param p - module path + * @param context - ESLint context + * @return - the full module filesystem path; null if package is core; undefined if not found + * @type {import('./resolve').default} + */ +function resolve(p, context) { + try { + return relative( + p, + context.getPhysicalFilename + ? context.getPhysicalFilename() + : context.getFilename(), + context.settings, + ) + } catch (err) { + if (!erroredContexts.has(context)) { + // The `err.stack` string starts with `err.name` followed by colon and `err.message`. + // We're filtering out the default `err.name` because it adds little value to the message. + // @ts-expect-error this might be an Error + let errMessage = err.message + // @ts-expect-error this might be an Error + if (err.name !== ERROR_NAME && err.stack) { + // @ts-expect-error this might be an Error + errMessage = err.stack.replace(/^Error: /, '') + } + context.report({ + message: `Resolve error: ${errMessage}`, + loc: { line: 1, column: 0 }, + }) + erroredContexts.add(context) + } + } +} +resolve.relative = relative +exports.default = resolve diff --git a/src/utils/types.d.ts b/src/utils/types.d.ts new file mode 100644 index 000000000..e1e917286 --- /dev/null +++ b/src/utils/types.d.ts @@ -0,0 +1,9 @@ +import type { Rule } from 'eslint' + +export type Extension = `.${string}` + +export type ESLintSettings = NonNullable & { + 'import-x/extensions'?: Extension[] + 'import-x/parsers'?: { [k: string]: Extension[] } + 'import-x/cache'?: { lifetime: number | '∞' | 'Infinity' } +} diff --git a/src/utils/unambiguous.d.ts b/src/utils/unambiguous.d.ts new file mode 100644 index 000000000..78dd4ddee --- /dev/null +++ b/src/utils/unambiguous.d.ts @@ -0,0 +1,7 @@ +import type { AST } from 'eslint' + +declare function isModule(ast: AST.Program): boolean + +declare function test(content: string): boolean + +export { isModule, test } diff --git a/utils/unambiguous.js b/src/utils/unambiguous.js similarity index 72% rename from utils/unambiguous.js rename to src/utils/unambiguous.js index 20aabd1bd..e74d20e6f 100644 --- a/utils/unambiguous.js +++ b/src/utils/unambiguous.js @@ -1,8 +1,8 @@ -'use strict'; +'use strict' -exports.__esModule = true; +exports.__esModule = true -const pattern = /(^|;)\s*(export|import)((\s+\w)|(\s*[{*=]))|import\(/m; +const pattern = /(^|;)\s*(export|import)((\s+\w)|(\s*[{*=]))|import\(/m /** * detect possible imports/exports without a full parse. * @@ -14,16 +14,17 @@ const pattern = /(^|;)\s*(export|import)((\s+\w)|(\s*[{*=]))|import\(/m; * @type {import('./unambiguous').test} */ exports.test = function isMaybeUnambiguousModule(content) { - return pattern.test(content); -}; + return pattern.test(content) +} // future-/Babel-proof at the expense of being a little loose -const unambiguousNodeType = /^(?:(?:Exp|Imp)ort.*Declaration|TSExportAssignment)$/; +const unambiguousNodeType = + /^(?:(?:Exp|Imp)ort.*Declaration|TSExportAssignment)$/ /** * Given an AST, return true if the AST unambiguously represents a module. * @type {import('./unambiguous').isModule} */ exports.isModule = function isUnambiguousModule(ast) { - return ast.body && ast.body.some((node) => unambiguousNodeType.test(node.type)); -}; + return ast.body && ast.body.some(node => unambiguousNodeType.test(node.type)) +} diff --git a/src/utils/visit.d.ts b/src/utils/visit.d.ts new file mode 100644 index 000000000..012686a58 --- /dev/null +++ b/src/utils/visit.d.ts @@ -0,0 +1,10 @@ +import type { Node } from 'estree' + +declare function visit( + node: Node, + keys: { [k in Node['type']]?: (keyof Node)[] }, + // eslint-disable-next-line @typescript-eslint/ban-types + visitorSpec: { [k in Node['type'] | `${Node['type']}:Exit`]?: Function }, +): void + +export default visit diff --git a/src/utils/visit.js b/src/utils/visit.js new file mode 100644 index 000000000..8358b1492 --- /dev/null +++ b/src/utils/visit.js @@ -0,0 +1,30 @@ +'use strict' + +exports.__esModule = true + +/** @type {import('./visit').default} */ +exports.default = function visit(node, keys, visitorSpec) { + if (!node || !keys) { + return + } + const type = node.type + const visitor = visitorSpec[type] + if (typeof visitor === 'function') { + visitor(node) + } + const childFields = keys[type] + if (!childFields) { + return + } + childFields.forEach(fieldName => { + // @ts-expect-error TS sucks with concat + ;[].concat(node[fieldName]).forEach(item => { + visit(item, keys, visitorSpec) + }) + }) + + const exit = visitorSpec[`${type}:Exit`] + if (typeof exit === 'function') { + exit(node) + } +} diff --git a/test/cli.spec.js b/test/cli.spec.js new file mode 100644 index 000000000..b0be5f306 --- /dev/null +++ b/test/cli.spec.js @@ -0,0 +1,151 @@ +/** + * tests that require fully booting up ESLint + */ +import path from 'path' + +import { CLIEngine, ESLint } from 'eslint' +import eslintPkg from 'eslint/package.json' +import semver from 'semver' +import * as importPlugin from '../src/index' + +describe('CLI regression tests', () => { + describe('issue #210', () => { + let eslint + let cli + beforeAll(() => { + if (ESLint) { + eslint = new ESLint({ + useEslintrc: false, + overrideConfigFile: './test/fixtures/issue210.config.js', + rulePaths: ['./src/rules'], + overrideConfig: { + rules: { + named: 2, + }, + }, + plugins: { 'eslint-plugin-import': importPlugin }, + }) + } else { + cli = new CLIEngine({ + useEslintrc: false, + configFile: './test/fixtures/issue210.config.js', + rulePaths: ['./src/rules'], + rules: { + named: 2, + }, + }) + cli.addPlugin('eslint-plugin-import', importPlugin) + } + }) + it("doesn't throw an error on gratuitous, erroneous self-reference", () => { + if (eslint) { + return eslint + .lintFiles(['./test/fixtures/issue210.js']) + .catch(() => expect.fail()) + } else { + expect(() => + cli.executeOnFiles(['./test/fixtures/issue210.js']), + ).not.toThrow() + } + }) + }) + + describe('issue #1645', () => { + let eslint + let cli + beforeEach(() => { + if (semver.satisfies(eslintPkg.version, '< 6')) { + this.skip() + } else { + if (ESLint) { + eslint = new ESLint({ + useEslintrc: false, + overrideConfigFile: + './test/fixtures/just-json-files/.eslintrc.json', + rulePaths: ['./src/rules'], + ignore: false, + plugins: { 'eslint-plugin-import': importPlugin }, + }) + } else { + cli = new CLIEngine({ + useEslintrc: false, + configFile: './test/fixtures/just-json-files/.eslintrc.json', + rulePaths: ['./src/rules'], + ignore: false, + }) + cli.addPlugin('eslint-plugin-import', importPlugin) + } + } + }) + + it('throws an error on invalid JSON', () => { + const invalidJSON = './test/fixtures/just-json-files/invalid.json' + if (eslint) { + return eslint.lintFiles([invalidJSON]).then(results => { + expect(results).toEqual([ + { + filePath: path.resolve(invalidJSON), + messages: [ + { + column: 2, + endColumn: 3, + endLine: 1, + line: 1, + message: 'Expected a JSON object, array or literal.', + nodeType: results[0].messages[0].nodeType, // we don't care about this one + ruleId: 'json/*', + severity: 2, + source: results[0].messages[0].source, // NewLine-characters might differ depending on git-settings + }, + ], + errorCount: 1, + ...(semver.satisfies(eslintPkg.version, '>= 7.32 || ^8.0.0') && { + fatalErrorCount: 0, + }), + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + source: results[0].source, // NewLine-characters might differ depending on git-settings + ...(semver.satisfies(eslintPkg.version, '>= 8.8') && { + suppressedMessages: [], + }), + usedDeprecatedRules: results[0].usedDeprecatedRules, // we don't care about this one + }, + ]) + }) + } else { + const results = cli.executeOnFiles([invalidJSON]) + expect(results).toEqual({ + results: [ + { + filePath: path.resolve(invalidJSON), + messages: [ + { + column: 2, + endColumn: 3, + endLine: 1, + line: 1, + message: 'Expected a JSON object, array or literal.', + nodeType: results.results[0].messages[0].nodeType, // we don't care about this one + ruleId: 'json/*', + severity: 2, + source: results.results[0].messages[0].source, // NewLine-characters might differ depending on git-settings + }, + ], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + source: results.results[0].source, // NewLine-characters might differ depending on git-settings + }, + ], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + usedDeprecatedRules: results.usedDeprecatedRules, // we don't care about this one + }) + } + }) + }) +}) diff --git a/test/config/typescript.spec.js b/test/config/typescript.spec.js new file mode 100644 index 000000000..b59547c9b --- /dev/null +++ b/test/config/typescript.spec.js @@ -0,0 +1,12 @@ +const config = require('eslint-plugin-import-x/config/typescript') + +describe('config typescript', () => { + // https://github.com/import-js/eslint-plugin-import/issues/1525 + it('should mark @types paths as external', () => { + const externalModuleFolders = + config.settings['import-x/external-module-folders'] + expect(externalModuleFolders).toBeDefined() + expect(externalModuleFolders).toContain('node_modules') + expect(externalModuleFolders).toContain('node_modules/@types') + }) +}) diff --git a/test/core/docsUrl.spec.js b/test/core/docsUrl.spec.js new file mode 100644 index 000000000..41b1e6148 --- /dev/null +++ b/test/core/docsUrl.spec.js @@ -0,0 +1,16 @@ +import pkg from '../../package.json' +import docsUrl from '../../src/docsUrl' + +describe('docsUrl', () => { + it('returns the rule documentation URL when given a rule name', () => { + expect(docsUrl('foo')).toBe( + `https://github.com/import-js/eslint-plugin-import/blob/v${pkg.version}/docs/rules/foo.md`, + ) + }) + + it('supports an optional commit-ish parameter', () => { + expect(docsUrl('foo', 'bar')).toBe( + 'https://github.com/import-js/eslint-plugin-import/blob/bar/docs/rules/foo.md', + ) + }) +}) diff --git a/tests/src/core/eslintParser.js b/test/core/eslintParser.js similarity index 87% rename from tests/src/core/eslintParser.js rename to test/core/eslintParser.js index f53a394de..6ff96f39d 100644 --- a/tests/src/core/eslintParser.js +++ b/test/core/eslintParser.js @@ -2,6 +2,6 @@ module.exports = { parseForESLint() { return { ast: {}, - }; + } }, -}; +} diff --git a/test/core/getExports.spec.js b/test/core/getExports.spec.js new file mode 100644 index 000000000..d5b9861e1 --- /dev/null +++ b/test/core/getExports.spec.js @@ -0,0 +1,520 @@ +import semver from 'semver' +import eslintPkg from 'eslint/package.json' +import typescriptPkg from 'typescript/package.json' +import * as tsConfigLoader from 'tsconfig-paths/lib/tsconfig-loader' +import ExportMap from '../../src/ExportMap' + +import * as fs from 'fs' + +import { getFilename } from '../utils' +import { test as testUnambiguous } from '../../src/utils/unambiguous' + +describe('ExportMap', () => { + const fakeContext = Object.assign( + semver.satisfies(eslintPkg.version, '>= 7.28') + ? { + getFilename() { + throw new Error( + 'Should call getPhysicalFilename() instead of getFilename()', + ) + }, + getPhysicalFilename: getFilename, + } + : { + getFilename, + }, + { + settings: {}, + parserPath: '@babel/eslint-parser', + }, + ) + + it('handles ExportAllDeclaration', () => { + let imports + expect(function () { + imports = ExportMap.get('./export-all', fakeContext) + }).not.toThrow(Error) + + expect(imports).toBeDefined() + expect(imports.has('foo')).toBe(true) + }) + + it('returns a cached copy on subsequent requests', () => { + expect(ExportMap.get('./named-exports', fakeContext)).toBe( + ExportMap.get('./named-exports', fakeContext), + ) + }) + + it('does not return a cached copy after modification', done => { + const firstAccess = ExportMap.get('./mutator', fakeContext) + expect(firstAccess).toBeDefined() + + // mutate (update modified time) + const newDate = new Date() + fs.utimes(getFilename('mutator.js'), newDate, newDate, error => { + expect(error).toBeFalsy() + expect(ExportMap.get('./mutator', fakeContext)).not.toBe(firstAccess) + done() + }) + }) + + it('does not return a cached copy with different settings', () => { + const firstAccess = ExportMap.get('./named-exports', fakeContext) + expect(firstAccess).toBeDefined() + + const differentSettings = { + ...fakeContext, + parserPath: 'espree', + } + + const result = ExportMap.get('./named-exports', differentSettings) + expect(result).toBeDefined() + expect(result).not.toBe(firstAccess) + }) + + it('does not throw for a missing file', () => { + let imports + expect(function () { + imports = ExportMap.get('./does-not-exist', fakeContext) + }).not.toThrow(Error) + + expect(imports).toBeFalsy() + }) + + it('exports explicit names for a missing file in exports', () => { + let imports + expect(function () { + imports = ExportMap.get('./exports-missing', fakeContext) + }).not.toThrow(Error) + + expect(imports).toBeDefined() + expect(imports.has('bar')).toBe(true) + }) + + it('finds exports for an ES7 module with @babel/eslint-parser', () => { + const path = getFilename('jsx/FooES7.js') + const contents = fs.readFileSync(path, { encoding: 'utf8' }) + const imports = ExportMap.parse(path, contents, { + parserPath: '@babel/eslint-parser', + settings: {}, + }) + + // imports + expect(imports).toBeDefined() + expect(imports.errors).toHaveLength(0) + // default export + expect(imports.get('default')).toBeDefined() + expect(imports.has('Bar')).toBe(true) + }) + + describe('deprecation metadata', () => { + function jsdocTests(parseContext, lineEnding) { + describe('deprecated imports', () => { + let imports + beforeAll(() => { + const path = getFilename('deprecated.js') + const contents = fs + .readFileSync(path, { encoding: 'utf8' }) + .replace(/[\r]\n/g, lineEnding) + imports = ExportMap.parse(path, contents, parseContext) + + // sanity checks + expect(imports.errors).toHaveLength(0) + }) + + it('works with named imports.', () => { + expect(imports.has('fn')).toBe(true) + + expect(imports.get('fn')).toHaveProperty( + 'doc.tags[0].title', + 'deprecated', + ) + expect(imports.get('fn')).toHaveProperty( + 'doc.tags[0].description', + "please use 'x' instead.", + ) + }) + + it('works with default imports.', () => { + expect(imports.has('default')).toBe(true) + const importMeta = imports.get('default') + + expect(importMeta).toHaveProperty('doc.tags[0].title', 'deprecated') + expect(importMeta).toHaveProperty( + 'doc.tags[0].description', + 'this is awful, use NotAsBadClass.', + ) + }) + + it('works with variables.', () => { + expect(imports.has('MY_TERRIBLE_ACTION')).toBe(true) + const importMeta = imports.get('MY_TERRIBLE_ACTION') + + expect(importMeta).toHaveProperty('doc.tags[0].title', 'deprecated') + expect(importMeta).toHaveProperty( + 'doc.tags[0].description', + 'please stop sending/handling this action type.', + ) + }) + + describe('multi-line variables', () => { + it('works for the first one', () => { + expect(imports.has('CHAIN_A')).toBe(true) + const importMeta = imports.get('CHAIN_A') + + expect(importMeta).toHaveProperty('doc.tags[0].title', 'deprecated') + expect(importMeta).toHaveProperty( + 'doc.tags[0].description', + 'this chain is awful', + ) + }) + it('works for the second one', () => { + expect(imports.has('CHAIN_B')).toBe(true) + const importMeta = imports.get('CHAIN_B') + + expect(importMeta).toHaveProperty('doc.tags[0].title', 'deprecated') + expect(importMeta).toHaveProperty( + 'doc.tags[0].description', + 'so awful', + ) + }) + it('works for the third one, etc.', () => { + expect(imports.has('CHAIN_C')).toBe(true) + const importMeta = imports.get('CHAIN_C') + + expect(importMeta).toHaveProperty('doc.tags[0].title', 'deprecated') + expect(importMeta).toHaveProperty( + 'doc.tags[0].description', + 'still terrible', + ) + }) + }) + }) + + describe('full module', () => { + let imports + beforeAll(() => { + const path = getFilename('deprecated-file.js') + const contents = fs.readFileSync(path, { encoding: 'utf8' }) + imports = ExportMap.parse(path, contents, parseContext) + + // sanity checks + expect(imports.errors).toHaveLength(0) + }) + + it('has JSDoc metadata', () => { + expect(imports.doc).toBeDefined() + }) + }) + } + + describe('default parser', () => { + jsdocTests( + { + parserPath: 'espree', + parserOptions: { + ecmaVersion: 2015, + sourceType: 'module', + attachComment: true, + }, + settings: {}, + }, + '\n', + ) + jsdocTests( + { + parserPath: 'espree', + parserOptions: { + ecmaVersion: 2015, + sourceType: 'module', + attachComment: true, + }, + settings: {}, + }, + '\r\n', + ) + }) + + describe('@babel/eslint-parser', () => { + jsdocTests( + { + parserPath: '@babel/eslint-parser', + parserOptions: { + ecmaVersion: 2015, + sourceType: 'module', + attachComment: true, + }, + settings: {}, + }, + '\n', + ) + jsdocTests( + { + parserPath: '@babel/eslint-parser', + parserOptions: { + ecmaVersion: 2015, + sourceType: 'module', + attachComment: true, + }, + settings: {}, + }, + '\r\n', + ) + }) + }) + + describe('exported static namespaces', () => { + const espreeContext = { + parserPath: 'espree', + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + settings: {}, + } + // const babelContext = { parserPath: '@babel/eslint-parser', parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, settings: {} }; + + it('works with espree & traditional namespace exports', () => { + const path = getFilename('deep/a.js') + const contents = fs.readFileSync(path, { encoding: 'utf8' }) + const a = ExportMap.parse(path, contents, espreeContext) + expect(a.errors).toHaveLength(0) + expect(a.get('b').namespace).toBeDefined() + expect(a.get('b').namespace.has('c')).toBe(true) + }) + + it('captures namespace exported as default', () => { + const path = getFilename('deep/default.js') + const contents = fs.readFileSync(path, { encoding: 'utf8' }) + const def = ExportMap.parse(path, contents, espreeContext) + expect(def.errors).toHaveLength(0) + expect(def.get('default').namespace).toBeDefined() + expect(def.get('default').namespace.has('c')).toBe(true) + }) + + // FIXME: check and enable + // it('works with @babel/eslint-parser & ES7 namespace exports', function () { + // const path = getFilename('deep-es7/a.js'); + // const contents = fs.readFileSync(path, { encoding: 'utf8' }); + // const a = ExportMap.parse(path, contents, babelContext); + // expect(a.errors).to.be.empty; + // expect(a.get('b').namespace).to.exist; + // expect(a.get('b').namespace.has('c')).to.be.true; + // }); + }) + + describe('deep namespace caching', () => { + const espreeContext = { + parserPath: 'espree', + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + settings: {}, + } + let a + beforeAll(done => { + // first version + fs.writeFileSync( + getFilename('deep/cache-2.js'), + fs.readFileSync(getFilename('deep/cache-2a.js')), + ) + + const path = getFilename('deep/cache-1.js') + const contents = fs.readFileSync(path, { encoding: 'utf8' }) + a = ExportMap.parse(path, contents, espreeContext) + expect(a.errors).toHaveLength(0) + + expect(a.get('b').namespace).toBeDefined() + expect(a.get('b').namespace.has('c')).toBe(true) + + // wait ~1s, cache check is 1s resolution + setTimeout(function reup() { + fs.unlinkSync(getFilename('deep/cache-2.js')) + // swap in a new file and touch it + fs.writeFileSync( + getFilename('deep/cache-2.js'), + fs.readFileSync(getFilename('deep/cache-2b.js')), + ) + done() + }, 1100) + }) + + it('works', () => { + expect(a.get('b').namespace.has('c')).toBe(false) + }) + + afterAll(done => fs.unlink(getFilename('deep/cache-2.js'), done)) + }) + + describe('Map API', () => { + describe('#size', () => { + it('counts the names', () => + expect(ExportMap.get('./named-exports', fakeContext)).toHaveProperty( + 'size', + 12, + )) + + it('includes exported namespace size', () => + expect(ExportMap.get('./export-all', fakeContext)).toHaveProperty( + 'size', + 1, + )) + }) + }) + + describe('issue #210: self-reference', () => { + it(`doesn't crash`, () => { + expect(() => ExportMap.get('./narcissist', fakeContext)).not.toThrow( + Error, + ) + }) + it(`'has' circular reference`, () => { + const result = ExportMap.get('./narcissist', fakeContext) + expect(result).toBeDefined() + expect(result.has('soGreat')).toBe(true) + }) + it(`can 'get' circular reference`, () => { + const result = ExportMap.get('./narcissist', fakeContext) + expect(result).toBeDefined() + expect(result.get('soGreat') != null).toBe(true) + }) + }) + + describe('issue #478: never parse non-whitelist extensions', () => { + const context = { + ...fakeContext, + settings: { 'import-x/extensions': ['.js'] }, + } + + let imports + beforeAll(() => { + imports = ExportMap.get('./typescript.ts', context) + }) + + it('returns nothing for a TypeScript file', () => { + expect(imports).toBeFalsy() + }) + }) + + describe('alternate parsers', () => { + const configs = [ + // ['string form', { 'typescript-eslint-parser': '.ts' }], + ] + + if (semver.satisfies(eslintPkg.version, '>5')) { + configs.push([ + 'array form', + { '@typescript-eslint/parser': ['.ts', '.tsx'] }, + ]) + } + + if ( + semver.satisfies(eslintPkg.version, '<6') && + semver.satisfies(typescriptPkg.version, '<4') + ) { + configs.push([ + 'array form', + { 'typescript-eslint-parser': ['.ts', '.tsx'] }, + ]) + } + + configs.forEach(([description, parserConfig]) => { + describe(description, () => { + const context = { + ...fakeContext, + settings: { + 'import-x/extensions': ['.js'], + 'import-x/parsers': parserConfig, + }, + } + + let imports + beforeAll(() => { + jest.setTimeout(20e3) // takes a long time :shrug: + jest.spyOn(tsConfigLoader, 'tsConfigLoader').mockClear() + imports = ExportMap.get('./typescript.ts', context) + }) + afterAll(() => { + tsConfigLoader.tsConfigLoader.mockRestore() + }) + + it('returns something for a TypeScript file', () => { + expect(imports).toBeDefined() + }) + + it('has no parse errors', () => { + expect(imports.errors).toHaveLength(0) + }) + + it('has exported function', () => { + expect(imports.has('getFoo')).toBe(true) + }) + + it('has exported typedef', () => { + expect(imports.has('MyType')).toBe(true) + }) + + it('has exported enum', () => { + expect(imports.has('MyEnum')).toBe(true) + }) + + it('has exported interface', () => { + expect(imports.has('Foo')).toBe(true) + }) + + it('has exported abstract class', () => { + expect(imports.has('Bar')).toBe(true) + }) + + it('should cache tsconfig until tsconfigRootDir parser option changes', () => { + const customContext = { + ...context, + parserOptions: { + tsconfigRootDir: null, + }, + } + expect(tsConfigLoader.tsConfigLoader).toHaveBeenCalledTimes(0) + ExportMap.parse('./baz.ts', 'export const baz = 5', customContext) + expect(tsConfigLoader.tsConfigLoader).toHaveBeenCalledTimes(1) + ExportMap.parse('./baz.ts', 'export const baz = 5', customContext) + expect(tsConfigLoader.tsConfigLoader).toHaveBeenCalledTimes(1) + + const differentContext = { + ...context, + parserOptions: { + tsconfigRootDir: process.cwd(), + }, + } + + ExportMap.parse('./baz.ts', 'export const baz = 5', differentContext) + expect(tsConfigLoader.tsConfigLoader).toHaveBeenCalledTimes(2) + }) + + it('should cache after parsing for an ambiguous module', () => { + const source = './typescript-declare-module.ts' + const parseSpy = jest.spyOn(ExportMap, 'parse').mockClear() + + expect(ExportMap.get(source, context)).toBeNull() + + ExportMap.get(source, context) + + expect(parseSpy).toHaveBeenCalledTimes(1) + + parseSpy.mockRestore() + }) + }) + }) + }) + + // todo: move to utils + describe('unambiguous regex', () => { + const testFiles = [ + ['deep/b.js', true], + ['bar.js', true], + ['deep-es7/b.js', true], + ['common.js', false], + ] + + for (const [testFile, expectedRegexResult] of testFiles) { + it(`works for ${testFile} (${expectedRegexResult})`, () => { + const content = fs.readFileSync(`./test/fixtures/${testFile}`, 'utf8') + expect(testUnambiguous(content)).toBe(expectedRegexResult) + }) + } + }) +}) diff --git a/test/core/hash.spec.js b/test/core/hash.spec.js new file mode 100644 index 000000000..ca82445e7 --- /dev/null +++ b/test/core/hash.spec.js @@ -0,0 +1,108 @@ +import hashify, { hashArray, hashObject } from '../../src/utils/hash' + +const createHash = require('crypto').createHash + +function expectHash(actualHash, expectedString) { + const expectedHash = createHash('sha256') + expectedHash.update(expectedString) + // to be a hex digest of sha256 hash of string <${expectedString}> + expect(actualHash.digest('hex')).toBe(expectedHash.digest('hex')) +} + +describe('hash', () => { + describe('hashify', () => { + it('handles null', () => { + expectHash(hashify(null), 'null') + }) + + it('handles undefined', () => { + expectHash(hashify(undefined), 'undefined') + }) + + it('handles numbers', () => { + expectHash(hashify(123.456), '123.456') + }) + + it('handles strings', () => { + expectHash(hashify('a string'), '"a string"') + }) + + it('handles Array instances', () => { + expectHash(hashify(['a string']), '["a string",]') + }) + + it('handles empty Array instances', () => { + expectHash(hashify([]), '[]') + }) + + it('handles Object instances', () => { + expectHash( + hashify({ foo: 123.456, 'a key': 'a value' }), + '{"a key":"a value","foo":123.456,}', + ) + }) + + it('handles nested Object instances', () => { + expectHash( + hashify({ + foo: 123.456, + 'a key': 'a value', + obj: { abc: { def: 'ghi' } }, + }), + '{"a key":"a value","foo":123.456,"obj":{"abc":{"def":"ghi",},},}', + ) + }) + + it('handles nested Object and Array instances', () => { + expectHash( + hashify({ + foo: 123.456, + 'a key': 'a value', + obj: { arr: [{ def: 'ghi' }] }, + }), + '{"a key":"a value","foo":123.456,"obj":{"arr":[{"def":"ghi",},],},}', + ) + }) + }) + + describe('hashArray', () => { + it('handles Array instances', () => { + expectHash(hashArray(['a string']), '["a string",]') + }) + + it('handles empty Array instances', () => { + expectHash(hashArray([]), '[]') + }) + }) + + describe('hashObject', () => { + it('handles Object instances', () => { + expectHash( + hashObject({ foo: 123.456, 'a key': 'a value' }), + '{"a key":"a value","foo":123.456,}', + ) + }) + + it('handles nested Object instances', () => { + expectHash( + hashObject({ + foo: 123.456, + 'a key': 'a value', + obj: { abc: { def: 'ghi' } }, + }), + '{"a key":"a value","foo":123.456,"obj":{"abc":{"def":"ghi",},},}', + ) + }) + + it('handles nested Object and Array instances', () => { + expectHash( + hashObject({ + foo: 123.456, + 'a key': 'a value', + obj: { arr: [{ def: 'ghi' }] }, + }), + '{"a key":"a value","foo":123.456,"obj":{"arr":[{"def":"ghi",},],},}', + ) + }) + }) +}) diff --git a/test/core/ignore.spec.js b/test/core/ignore.spec.js new file mode 100644 index 000000000..f48fb9d1d --- /dev/null +++ b/test/core/ignore.spec.js @@ -0,0 +1,101 @@ +import isIgnored, { + getFileExtensions, + hasValidExtension, +} from '../../src/utils/ignore' + +import * as utils from '../utils' + +describe('ignore', () => { + describe('isIgnored', () => { + it('ignores paths with extensions other than .js', () => { + const testContext = utils.testContext({}) + + expect(isIgnored('../fixtures/foo.js', testContext)).toBe(false) + + expect(isIgnored('../fixtures/bar.jsx', testContext)).toBe(true) + + expect(isIgnored('../fixtures/typescript.ts', testContext)).toBe(true) + + expect( + isIgnored('../fixtures/ignore.invalid.extension', testContext), + ).toBe(true) + }) + + it('ignores paths with invalid extensions when configured with import-x/extensions', () => { + const testContext = utils.testContext({ + 'import-x/extensions': ['.js', '.jsx', '.ts'], + }) + + expect(isIgnored('../fixtures/foo.js', testContext)).toBe(false) + + expect(isIgnored('../fixtures/bar.jsx', testContext)).toBe(false) + + expect(isIgnored('../fixtures/typescript.ts', testContext)).toBe(false) + + expect( + isIgnored('../fixtures/ignore.invalid.extension', testContext), + ).toBe(true) + }) + }) + + describe('hasValidExtension', () => { + it('assumes only .js as valid by default', () => { + const testContext = utils.testContext({}) + + expect(hasValidExtension('../fixtures/foo.js', testContext)).toBe(true) + + expect(hasValidExtension('../fixtures/foo.jsx', testContext)).toBe(false) + + expect(hasValidExtension('../fixtures/foo.css', testContext)).toBe(false) + + expect( + hasValidExtension('../fixtures/foo.invalid.extension', testContext), + ).toBe(false) + }) + + it('can be configured with import-x/extensions', () => { + const testContext = utils.testContext({ + 'import-x/extensions': ['.foo', '.bar'], + }) + + expect(hasValidExtension('../fixtures/foo.foo', testContext)).toBe(true) + + expect(hasValidExtension('../fixtures/foo.bar', testContext)).toBe(true) + + expect(hasValidExtension('../fixtures/foo.js', testContext)).toBe(false) + }) + }) + + describe('getFileExtensions', () => { + it('returns a set with the file extension ".js" if "import-x/extensions" is not configured', () => { + const fileExtensions = getFileExtensions({}) + + expect(fileExtensions).toContain('.js') + }) + + it('returns a set with the file extensions configured in "import-x/extension"', () => { + const settings = { + 'import-x/extensions': ['.js', '.jsx'], + } + + const fileExtensions = getFileExtensions(settings) + + expect(fileExtensions).toContain('.js') + expect(fileExtensions).toContain('.jsx') + }) + + it('returns a set with the file extensions configured in "import-x/extension" and "import-x/parsers"', () => { + const settings = { + 'import-x/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'], + }, + } + + const fileExtensions = getFileExtensions(settings) + + expect(fileExtensions).toContain('.js') // If "import-x/extensions" is not configured, this is the default + expect(fileExtensions).toContain('.ts') + expect(fileExtensions).toContain('.tsx') + }) + }) +}) diff --git a/test/core/importType.spec.js b/test/core/importType.spec.js new file mode 100644 index 000000000..8dd3624c5 --- /dev/null +++ b/test/core/importType.spec.js @@ -0,0 +1,379 @@ +import * as path from 'path' +import isCoreModule from 'is-core-module' + +import importType, { + isExternalModule, + isScoped, + isAbsolute, +} from 'core/importType' + +import { testContext, testFilePath } from '../utils' + +describe('importType(name)', () => { + const context = testContext() + const pathToTestFixtures = path.join(__dirname, '..', 'fixtures') + + it("should return 'absolute' for paths starting with a /", () => { + expect(importType('/', context)).toBe('absolute') + expect(importType('/path', context)).toBe('absolute') + expect(importType('/some/path', context)).toBe('absolute') + }) + + it("should return 'builtin' for node.js modules", () => { + ;['fs', 'fs/promises', 'path'] + .filter(x => isCoreModule(x)) + .forEach(x => { + expect(importType(x, context)).toBe('builtin') + if (isCoreModule(`node:${x}`)) { + expect(importType(`node:${x}`, context)).toBe('builtin') + } + }) + }) + + it("should return 'external' for non-builtin modules without a relative path", () => { + expect(importType('lodash', context)).toBe('external') + expect(importType('async', context)).toBe('external') + expect(importType('chalk', context)).toBe('external') + expect(importType('foo', context)).toBe('external') + expect(importType('lodash.find', context)).toBe('external') + expect(importType('lodash/fp', context)).toBe('external') + }) + + it("should return 'external' for scopes packages", () => { + expect(importType('@cycle/', context)).toBe('external') + expect(importType('@cycle/core', context)).toBe('external') + expect(importType('@cycle/dom', context)).toBe('external') + expect(importType('@some-thing/something', context)).toBe('external') + expect(importType('@some-thing/something/some-module', context)).toBe( + 'external', + ) + expect( + importType('@some-thing/something/some-directory/someModule.js', context), + ).toBe('external') + }) + + it("should return 'external' for external modules that redirect to its parent module using package.json", () => { + expect( + importType('eslint-import-test-order-redirect/module', context), + ).toBe('external') + expect( + importType('@eslint/import-test-order-redirect-scoped/module', context), + ).toBe('external') + }) + + it("should return 'internal' for non-builtins resolved outside of node_modules", () => { + const pathContext = testContext({ + 'import-x/resolver': { node: { paths: [pathToTestFixtures] } }, + }) + expect(importType('importType', pathContext)).toBe('internal') + }) + + it("should return 'internal' for scoped packages resolved outside of node_modules", () => { + const pathContext = testContext({ + 'import-x/resolver': { node: { paths: [pathToTestFixtures] } }, + }) + expect(importType('@importType/index', pathContext)).toBe('internal') + }) + + it("should return 'internal' for internal modules that are referenced by aliases", () => { + const pathContext = testContext({ + 'import-x/resolver': { node: { paths: [pathToTestFixtures] } }, + }) + expect(importType('@my-alias/fn', pathContext)).toBe('internal') + expect(importType('@importType', pathContext)).toBe('internal') + }) + + it("should return 'internal' for aliased internal modules that look like core modules (node resolver)", () => { + const pathContext = testContext({ + 'import-x/resolver': { node: { paths: [pathToTestFixtures] } }, + }) + expect(importType('constants/index', pathContext)).toBe('internal') + expect(importType('constants/', pathContext)).toBe('internal') + // resolves exact core modules over internal modules + expect(importType('constants', pathContext)).toBe('builtin') + }) + + it("should return 'internal' for aliased internal modules that look like core modules (webpack resolver)", () => { + const webpackConfig = { + resolve: { modules: [pathToTestFixtures, 'node_modules'] }, + } + const pathContext = testContext({ + 'import-x/resolver': { webpack: { config: webpackConfig } }, + }) + expect(importType('constants/index', pathContext)).toBe('internal') + expect(importType('constants/', pathContext)).toBe('internal') + expect(importType('constants', pathContext)).toBe('internal') + }) + + it("should return 'internal' for aliased internal modules that are found, even if they are not discernible as scoped", () => { + // `@` for internal modules is a common alias and is different from scoped names. + // Scoped names are prepended with `@` (e.g. `@scoped/some-file.js`) whereas `@` + // as an alias by itelf is the full root name (e.g. `@/some-file.js`). + const alias = { '@': path.join(pathToTestFixtures, 'internal-modules') } + const webpackConfig = { resolve: { alias } } + const pathContext = testContext({ + 'import-x/resolver': { webpack: { config: webpackConfig } }, + }) + expect(importType('@/api/service', pathContext)).toBe('internal') + expect(importType('@/does-not-exist', pathContext)).toBe('unknown') + }) + + it("should return 'parent' for internal modules that go through the parent", () => { + expect(importType('../foo', context)).toBe('parent') + expect(importType('../../foo', context)).toBe('parent') + expect(importType('../bar/foo', context)).toBe('parent') + }) + + it("should return 'sibling' for internal modules that are connected to one of the siblings", () => { + expect(importType('./foo', context)).toBe('sibling') + expect(importType('./foo/bar', context)).toBe('sibling') + expect(importType('./importType', context)).toBe('sibling') + expect(importType('./importType/', context)).toBe('sibling') + expect(importType('./importType/index', context)).toBe('sibling') + expect(importType('./importType/index.js', context)).toBe('sibling') + }) + + it("should return 'index' for sibling index file", () => { + expect(importType('.', context)).toBe('index') + expect(importType('./', context)).toBe('index') + expect(importType('./index', context)).toBe('index') + expect(importType('./index.js', context)).toBe('index') + }) + + it("should return 'unknown' for any unhandled cases", () => { + expect(importType(' /malformed', context)).toBe('unknown') + expect(importType(' foo', context)).toBe('unknown') + expect(importType('-/no-such-path', context)).toBe('unknown') + }) + + it("should return 'builtin' for additional core modules", () => { + // without extra config, should be marked external + expect(importType('electron', context)).toBe('external') + expect(importType('@org/foobar', context)).toBe('external') + + const electronContext = testContext({ + 'import-x/core-modules': ['electron'], + }) + expect(importType('electron', electronContext)).toBe('builtin') + + const scopedContext = testContext({ + 'import-x/core-modules': ['@org/foobar'], + }) + expect(importType('@org/foobar', scopedContext)).toBe('builtin') + }) + + it("should return 'builtin' for resources inside additional core modules", () => { + const electronContext = testContext({ + 'import-x/core-modules': ['electron'], + }) + expect( + importType('electron/some/path/to/resource.json', electronContext), + ).toBe('builtin') + + const scopedContext = testContext({ + 'import-x/core-modules': ['@org/foobar'], + }) + expect( + importType('@org/foobar/some/path/to/resource.json', scopedContext), + ).toBe('builtin') + }) + + it("should return 'external' for module from 'node_modules' with default config", () => { + expect(importType('resolve', context)).toBe('external') + }) + + it("should return 'internal' for module from 'node_modules' if 'node_modules' missed in 'external-module-folders'", () => { + const foldersContext = testContext({ + 'import-x/external-module-folders': [], + }) + expect(importType('chai', foldersContext)).toBe('internal') + }) + + it("should return 'internal' for module from 'node_modules' if its name matched 'internal-regex'", () => { + const foldersContext = testContext({ 'import-x/internal-regex': '^@org' }) + expect(importType('@org/foobar', foldersContext)).toBe('internal') + }) + + it("should return 'external' for module from 'node_modules' if its name did not match 'internal-regex'", () => { + const foldersContext = testContext({ 'import-x/internal-regex': '^@bar' }) + expect(importType('@org/foobar', foldersContext)).toBe('external') + }) + + it("should return 'external' for module from 'node_modules' if 'node_modules' contained in 'external-module-folders'", () => { + const foldersContext = testContext({ + 'import-x/external-module-folders': ['node_modules'], + }) + expect(importType('resolve', foldersContext)).toBe('external') + }) + + it('returns "external" for a scoped symlinked module', () => { + const foldersContext = testContext({ + 'import-x/resolver': 'node', + 'import-x/external-module-folders': ['node_modules'], + }) + expect(importType('@test-scope/some-module', foldersContext)).toBe( + 'external', + ) + }) + + // We're using Webpack resolver here since it resolves all symlinks, which means that + // directory path will not contain node_modules/ but will point to the + // actual directory inside 'fixtures' instead + it('returns "external" for a scoped module from a symlinked directory which name is contained in "external-module-folders" (webpack resolver)', () => { + const foldersContext = testContext({ + 'import-x/resolver': 'webpack', + 'import-x/external-module-folders': ['symlinked-module'], + }) + expect(importType('@test-scope/some-module', foldersContext)).toBe( + 'external', + ) + }) + + it('returns "internal" for a scoped module from a symlinked directory which incomplete name is contained in "external-module-folders" (webpack resolver)', () => { + const foldersContext_1 = testContext({ + 'import-x/resolver': 'webpack', + 'import-x/external-module-folders': ['symlinked-mod'], + }) + expect(importType('@test-scope/some-module', foldersContext_1)).toBe( + 'internal', + ) + + const foldersContext_2 = testContext({ + 'import-x/resolver': 'webpack', + 'import-x/external-module-folders': ['linked-module'], + }) + expect(importType('@test-scope/some-module', foldersContext_2)).toBe( + 'internal', + ) + }) + + it('returns "external" for a scoped module from a symlinked directory which partial path is contained in "external-module-folders" (webpack resolver)', () => { + const originalFoldersContext = testContext({ + 'import-x/resolver': 'webpack', + 'import-x/external-module-folders': [], + }) + expect(importType('@test-scope/some-module', originalFoldersContext)).toBe( + 'internal', + ) + + const foldersContext = testContext({ + 'import-x/resolver': 'webpack', + 'import-x/external-module-folders': ['symlinked-module'], + }) + expect(importType('@test-scope/some-module', foldersContext)).toBe( + 'external', + ) + }) + + it('returns "internal" for a scoped module from a symlinked directory which partial path w/ incomplete segment is contained in "external-module-folders" (webpack resolver)', () => { + const foldersContext_1 = testContext({ + 'import-x/resolver': 'webpack', + 'import-x/external-module-folders': ['fixtures/symlinked-mod'], + }) + expect(importType('@test-scope/some-module', foldersContext_1)).toBe( + 'internal', + ) + + const foldersContext_2 = testContext({ + 'import-x/resolver': 'webpack', + 'import-x/external-module-folders': ['ymlinked-module'], + }) + expect(importType('@test-scope/some-module', foldersContext_2)).toBe( + 'internal', + ) + }) + + it('returns "external" for a scoped module from a symlinked directory which partial path ending w/ slash is contained in "external-module-folders" (webpack resolver)', () => { + const foldersContext = testContext({ + 'import-x/resolver': 'webpack', + 'import-x/external-module-folders': ['symlinked-module/'], + }) + expect(importType('@test-scope/some-module', foldersContext)).toBe( + 'external', + ) + }) + + it('returns "internal" for a scoped module from a symlinked directory when "external-module-folders" contains an absolute path resembling directory‘s relative path (webpack resolver)', () => { + const foldersContext = testContext({ + 'import-x/resolver': 'webpack', + 'import-x/external-module-folders': ['/symlinked-module'], + }) + expect(importType('@test-scope/some-module', foldersContext)).toBe( + 'internal', + ) + }) + + it('returns "external" for a scoped module from a symlinked directory which absolute path is contained in "external-module-folders" (webpack resolver)', () => { + const foldersContext = testContext({ + 'import-x/resolver': 'webpack', + 'import-x/external-module-folders': [testFilePath('symlinked-module')], + }) + expect(importType('@test-scope/some-module', foldersContext)).toBe( + 'external', + ) + }) + + it('`isExternalModule` works with windows directory separator', () => { + const context = testContext() + expect( + isExternalModule('foo', 'E:\\path\\to\\node_modules\\foo', context), + ).toBe(true) + expect( + isExternalModule( + '@foo/bar', + 'E:\\path\\to\\node_modules\\@foo\\bar', + context, + ), + ).toBe(true) + expect( + isExternalModule( + 'foo', + 'E:\\path\\to\\node_modules\\foo', + testContext({ + settings: { + 'import-x/external-module-folders': ['E:\\path\\to\\node_modules'], + }, + }), + ), + ).toBe(true) + }) + + it('`isExternalModule` works with unix directory separator', () => { + const context = testContext() + expect(isExternalModule('foo', '/path/to/node_modules/foo', context)).toBe( + true, + ) + expect( + isExternalModule('@foo/bar', '/path/to/node_modules/@foo/bar', context), + ).toBe(true) + expect( + isExternalModule( + 'foo', + '/path/to/node_modules/foo', + testContext({ + settings: { + 'import-x/external-module-folders': ['/path/to/node_modules'], + }, + }), + ), + ).toBe(true) + }) + + it('correctly identifies scoped modules with `isScoped`', () => { + expect(isScoped('@/abc')).toBe(false) + expect(isScoped('@/abc/def')).toBe(false) + expect(isScoped('@a/abc')).toBe(true) + expect(isScoped('@a/abc/def')).toBe(true) + }) +}) + +describe('isAbsolute', () => { + it('does not throw on a non-string', () => { + expect(() => isAbsolute()).not.toThrow() + expect(() => isAbsolute(null)).not.toThrow() + expect(() => isAbsolute(true)).not.toThrow() + expect(() => isAbsolute(false)).not.toThrow() + expect(() => isAbsolute(0)).not.toThrow() + expect(() => isAbsolute(NaN)).not.toThrow() + }) +}) diff --git a/test/core/parse.spec.js b/test/core/parse.spec.js new file mode 100644 index 000000000..e5fc2f59b --- /dev/null +++ b/test/core/parse.spec.js @@ -0,0 +1,255 @@ +import * as fs from 'fs' +import parse from '../../src/utils/parse' + +import { getFilename } from '../utils' + +describe('parse(content, { settings, ecmaFeatures })', () => { + const path = getFilename('jsx.js') + const parseStubParser = require('./parseStubParser') + const parseStubParserPath = require.resolve('./parseStubParser') + const eslintParser = require('./eslintParser') + const eslintParserPath = require.resolve('./eslintParser') + let content + + beforeAll(done => { + fs.readFile(path, { encoding: 'utf8' }, (err, f) => { + if (err) { + done(err) + } else { + content = f + done() + } + }) + }) + + it("doesn't support JSX by default", () => { + expect(() => parse(path, content, { parserPath: 'espree' })).toThrow(Error) + }) + + it('infers jsx from ecmaFeatures when using stock parser', () => { + expect(() => + parse(path, content, { + settings: {}, + parserPath: 'espree', + parserOptions: { + ecmaVersion: 2015, + sourceType: 'module', + ecmaFeatures: { jsx: true }, + }, + }), + ).not.toThrow(Error) + }) + + it('passes expected parserOptions to custom parser', () => { + const parseSpy = jest.fn() + const parserOptions = { ecmaFeatures: { jsx: true } } + parseStubParser.parse = parseSpy + parse(path, content, { + settings: {}, + parserPath: parseStubParserPath, + parserOptions, + }) + // custom parser to be called once + expect(parseSpy).toHaveBeenCalledTimes(1) + // custom parser to get content as its first argument + expect(parseSpy.mock.calls[0][0]).toBe(content) + expect(parseSpy.mock.calls[0][1]).toBeInstanceOf(Object) + // custom parser to clone the parserOptions object + expect(parseSpy.mock.calls[0][1]).not.toBe(parserOptions) + // custom parser to get ecmaFeatures in parserOptions which is a clone of ecmaFeatures passed in + const mockParserOptions = parseSpy.mock.calls[0][1] + expect(mockParserOptions).toHaveProperty('ecmaFeatures') + expect(mockParserOptions.ecmaFeatures).toEqual(parserOptions.ecmaFeatures) + expect(mockParserOptions.ecmaFeatures).not.toBe(parserOptions.ecmaFeatures) + // custom parser to get parserOptions.attachComment equal to true + expect(parseSpy.mock.calls[0][1]).toHaveProperty('attachComment', true) + // custom parser to get parserOptions.tokens equal to true + expect(parseSpy.mock.calls[0][1]).toHaveProperty('tokens', true) + // custom parser to get parserOptions.range equal to true + expect(parseSpy.mock.calls[0][1]).toHaveProperty('range', true) + // custom parser to get parserOptions.filePath equal to the full path of the source file + expect(parseSpy.mock.calls[0][1]).toHaveProperty('filePath', path) + }) + + it('passes with custom `parseForESLint` parser', () => { + const parseForESLintSpy = jest + .spyOn(eslintParser, 'parseForESLint') + .mockClear() + const parseSpy = jest.fn() + eslintParser.parse = parseSpy + parse(path, content, { settings: {}, parserPath: eslintParserPath }) + // custom `parseForESLint` parser to be called once + expect(parseForESLintSpy).toHaveBeenCalledTimes(1) + // `parseForESLint` takes higher priority than `parse` + expect(parseSpy).toHaveBeenCalledTimes(0) + }) + + it('throws on context == null', () => { + expect(parse.bind(null, path, content, null)).toThrow(Error) + }) + + it('throws on unable to resolve parserPath', () => { + expect( + parse.bind(null, path, content, { settings: {}, parserPath: null }), + ).toThrow(Error) + }) + + it('takes the alternate parser specified in settings', () => { + const parseSpy = jest.fn() + const parserOptions = { ecmaFeatures: { jsx: true } } + parseStubParser.parse = parseSpy + expect( + parse.bind(null, path, content, { + settings: { 'import-x/parsers': { [parseStubParserPath]: ['.js'] } }, + parserPath: null, + parserOptions, + }), + ).not.toThrow(Error) + // custom parser to be called once + expect(parseSpy).toHaveBeenCalledTimes(1) + }) + + it('throws on invalid languageOptions', () => { + expect( + parse.bind(null, path, content, { + settings: {}, + parserPath: null, + languageOptions: null, + }), + ).toThrow(Error) + }) + + it('throws on non-object languageOptions.parser', () => { + expect( + parse.bind(null, path, content, { + settings: {}, + parserPath: null, + languageOptions: { parser: 'espree' }, + }), + ).toThrow(Error) + }) + + it('throws on null languageOptions.parser', () => { + expect( + parse.bind(null, path, content, { + settings: {}, + parserPath: null, + languageOptions: { parser: null }, + }), + ).toThrow(Error) + }) + + it('throws on empty languageOptions.parser', () => { + expect( + parse.bind(null, path, content, { + settings: {}, + parserPath: null, + languageOptions: { parser: {} }, + }), + ).toThrow(Error) + }) + + it('throws on non-function languageOptions.parser.parse', () => { + expect( + parse.bind(null, path, content, { + settings: {}, + parserPath: null, + languageOptions: { parser: { parse: 'espree' } }, + }), + ).toThrow(Error) + }) + + it('throws on non-function languageOptions.parser.parse', () => { + expect( + parse.bind(null, path, content, { + settings: {}, + parserPath: null, + languageOptions: { parser: { parseForESLint: 'espree' } }, + }), + ).toThrow(Error) + }) + + it('requires only one of the parse methods', () => { + expect( + parse.bind(null, path, content, { + settings: {}, + parserPath: null, + languageOptions: { parser: { parseForESLint: () => ({ ast: {} }) } }, + }), + ).not.toThrow(Error) + }) + + it('uses parse from languageOptions.parser', () => { + const parseSpy = jest.fn() + expect( + parse.bind(null, path, content, { + settings: {}, + languageOptions: { parser: { parse: parseSpy } }, + }), + ).not.toThrow(Error) + // passed parser to be called once + expect(parseSpy).toHaveBeenCalledTimes(1) + }) + + it('uses parseForESLint from languageOptions.parser', () => { + const parseSpy = jest.fn(() => ({ ast: {} })) + expect( + parse.bind(null, path, content, { + settings: {}, + languageOptions: { parser: { parseForESLint: parseSpy } }, + }), + ).not.toThrow(Error) + // passed parser to be called once + expect(parseSpy).toHaveBeenCalledTimes(1) + }) + + it('prefers parsers specified in the settings over languageOptions.parser', () => { + const parseSpy = jest.fn() + parseStubParser.parse = parseSpy + expect( + parse.bind(null, path, content, { + settings: { 'import-x/parsers': { [parseStubParserPath]: ['.js'] } }, + parserPath: null, + languageOptions: { parser: { parse() {} } }, + }), + ).not.toThrow(Error) + // custom parser to be called once + expect(parseSpy).toHaveBeenCalledTimes(1) + }) + + it('ignores parser options from language options set to null', () => { + const parseSpy = jest.fn() + parseStubParser.parse = parseSpy + expect( + parse.bind(null, path, content, { + settings: {}, + parserPath: 'espree', + languageOptions: { parserOptions: null }, + parserOptions: { + sourceType: 'module', + ecmaVersion: 2015, + ecmaFeatures: { jsx: true }, + }, + }), + ).not.toThrow(Error) + }) + + it('prefers languageOptions.parserOptions over parserOptions', () => { + const parseSpy = jest.fn() + parseStubParser.parse = parseSpy + expect( + parse.bind(null, path, content, { + settings: {}, + parserPath: 'espree', + languageOptions: { + parserOptions: { + sourceType: 'module', + ecmaVersion: 2015, + ecmaFeatures: { jsx: true }, + }, + }, + parserOptions: { sourceType: 'script' }, + }), + ).not.toThrow(Error) + }) +}) diff --git a/tests/src/core/parseStubParser.js b/test/core/parseStubParser.js similarity index 97% rename from tests/src/core/parseStubParser.js rename to test/core/parseStubParser.js index 4ed17d9dd..a123672ab 100644 --- a/tests/src/core/parseStubParser.js +++ b/test/core/parseStubParser.js @@ -1,4 +1,4 @@ // this stub must be in a separate file to require from parse via moduleRequire module.exports = { parse() {}, -}; +} diff --git a/test/core/resolve.spec.js b/test/core/resolve.spec.js new file mode 100644 index 000000000..c19ed5512 --- /dev/null +++ b/test/core/resolve.spec.js @@ -0,0 +1,684 @@ +import eslintPkg from 'eslint/package.json' +import semver from 'semver' + +import resolve, { + CASE_SENSITIVE_FS, + fileExistsWithCaseSync, +} from '../../src/utils/resolve' + +import * as path from 'path' +import * as fs from 'fs' +import * as utils from '../utils' + +describe('resolve', () => { + it('throws on bad parameters', () => { + expect(resolve.bind(null, null, null)).toThrow(Error) + }) + + it('resolves via a custom resolver with interface version 1', () => { + const testContext = utils.testContext({ + 'import-x/resolver': './foo-bar-resolver-v1', + }) + + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBe(utils.testFilePath('./bar.jsx')) + + expect( + resolve('../fixtures/exception', { + ...testContext, + getFilename() { + return utils.getFilename('exception.js') + }, + }), + ).toBeUndefined() + + expect( + resolve('../fixtures/not-found', { + ...testContext, + getFilename() { + return utils.getFilename('not-found.js') + }, + }), + ).toBeUndefined() + }) + + it('resolves via a custom resolver with interface version 1 assumed if not specified', () => { + const testContext = utils.testContext({ + 'import-x/resolver': './foo-bar-resolver-no-version', + }) + + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBe(utils.testFilePath('./bar.jsx')) + + expect( + resolve('../fixtures/exception', { + ...testContext, + getFilename() { + return utils.getFilename('exception.js') + }, + }), + ).toBeUndefined() + + expect( + resolve('../fixtures/not-found', { + ...testContext, + getFilename() { + return utils.getFilename('not-found.js') + }, + }), + ).toBeUndefined() + }) + + it('resolves via a custom resolver with interface version 2', () => { + const testContext = utils.testContext({ + 'import-x/resolver': './foo-bar-resolver-v2', + }) + const testContextReports = [] + testContext.report = function (reportInfo) { + testContextReports.push(reportInfo) + } + + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBe(utils.testFilePath('./bar.jsx')) + + testContextReports.length = 0 + expect( + resolve('../fixtures/exception', { + ...testContext, + getFilename() { + return utils.getFilename('exception.js') + }, + }), + ).toBeUndefined() + expect(testContextReports[0]).toBeInstanceOf(Object) + expect(testContextReports[0].message).toMatch( + 'Resolve error: foo-bar-resolver-v2 resolve test exception', + ) + expect(testContextReports[0].loc).toEqual({ line: 1, column: 0 }) + + testContextReports.length = 0 + expect( + resolve('../fixtures/not-found', { + ...testContext, + getFilename() { + return utils.getFilename('not-found.js') + }, + }), + ).toBeUndefined() + expect(testContextReports.length).toBe(0) + }) + + it('respects import-x/resolver as array of strings', () => { + const testContext = utils.testContext({ + 'import-x/resolver': ['./foo-bar-resolver-v2', './foo-bar-resolver-v1'], + }) + + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBe(utils.testFilePath('./bar.jsx')) + }) + + it('respects import-x/resolver as object', () => { + const testContext = utils.testContext({ + 'import-x/resolver': { './foo-bar-resolver-v2': {} }, + }) + + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBe(utils.testFilePath('./bar.jsx')) + }) + + it('respects import-x/resolver as array of objects', () => { + const testContext = utils.testContext({ + 'import-x/resolver': [ + { './foo-bar-resolver-v2': {} }, + { './foo-bar-resolver-v1': {} }, + ], + }) + + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBe(utils.testFilePath('./bar.jsx')) + }) + + it('finds resolvers from the source files rather than ../../../src/utils', () => { + const testContext = utils.testContext({ 'import-x/resolver': { foo: {} } }) + + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBe(utils.testFilePath('./bar.jsx')) + }) + + it('reports invalid import-x/resolver config', () => { + const testContext = utils.testContext({ 'import-x/resolver': 123.456 }) + const testContextReports = [] + testContext.report = function (reportInfo) { + testContextReports.push(reportInfo) + } + + testContextReports.length = 0 + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBeUndefined() + expect(testContextReports[0]).toBeInstanceOf(Object) + expect(testContextReports[0].message).toMatch( + 'Resolve error: invalid resolver config', + ) + expect(testContextReports[0].loc).toEqual({ line: 1, column: 0 }) + }) + + it('reports loaded resolver with invalid interface', () => { + const resolverName = './foo-bar-resolver-invalid' + const testContext = utils.testContext({ 'import-x/resolver': resolverName }) + const testContextReports = [] + testContext.report = function (reportInfo) { + testContextReports.push(reportInfo) + } + testContextReports.length = 0 + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBeUndefined() + expect(testContextReports[0]).toBeInstanceOf(Object) + expect(testContextReports[0].message).toMatch( + `Resolve error: ${resolverName} with invalid interface loaded as resolver`, + ) + expect(testContextReports[0].loc).toEqual({ line: 1, column: 0 }) + }) + + it('respects import-x/resolve extensions', () => { + const testContext = utils.testContext({ + 'import-x/resolve': { extensions: ['.jsx'] }, + }) + + expect(resolve('./jsx/MyCoolComponent', testContext)).toBe( + utils.testFilePath('./jsx/MyCoolComponent.jsx'), + ) + }) + + it('reports load exception in a user resolver', () => { + const testContext = utils.testContext({ + 'import-x/resolver': './load-error-resolver', + }) + const testContextReports = [] + testContext.report = function (reportInfo) { + testContextReports.push(reportInfo) + } + + expect( + resolve('../fixtures/exception', { + ...testContext, + getFilename() { + return utils.getFilename('exception.js') + }, + }), + ).toBeUndefined() + expect(testContextReports[0]).toBeInstanceOf(Object) + expect(testContextReports[0].message).toMatch( + 'Resolve error: SyntaxError: TEST SYNTAX ERROR', + ) + expect(testContextReports[0].loc).toEqual({ line: 1, column: 0 }) + }) + + // context.getPhysicalFilename() is available in ESLint 7.28+ + ;(semver.satisfies(eslintPkg.version, '>= 7.28') ? describe : describe.skip)( + 'getPhysicalFilename()', + () => { + function unexpectedCallToGetFilename() { + throw new Error( + 'Expected to call to getPhysicalFilename() instead of getFilename()', + ) + } + + it('resolves via a custom resolver with interface version 1', () => { + const testContext = utils.testContext({ + 'import-x/resolver': './foo-bar-resolver-v1', + }) + + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBe(utils.testFilePath('./bar.jsx')) + + expect( + resolve('../fixtures/exception', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('exception.js') + }, + }), + ).toBeUndefined() + + expect( + resolve('../fixtures/not-found', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('not-found.js') + }, + }), + ).toBeUndefined() + }) + + it('resolves via a custom resolver with interface version 1 assumed if not specified', () => { + const testContext = utils.testContext({ + 'import-x/resolver': './foo-bar-resolver-no-version', + }) + + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBe(utils.testFilePath('./bar.jsx')) + + expect( + resolve('../fixtures/exception', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('exception.js') + }, + }), + ).toBeUndefined() + + expect( + resolve('../fixtures/not-found', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('not-found.js') + }, + }), + ).toBeUndefined() + }) + + it('resolves via a custom resolver with interface version 2', () => { + const testContext = utils.testContext({ + 'import-x/resolver': './foo-bar-resolver-v2', + }) + const testContextReports = [] + testContext.report = function (reportInfo) { + testContextReports.push(reportInfo) + } + + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBe(utils.testFilePath('./bar.jsx')) + + testContextReports.length = 0 + expect( + resolve('../fixtures/exception', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('exception.js') + }, + }), + ).toBeUndefined() + expect(testContextReports[0]).toBeInstanceOf(Object) + expect(testContextReports[0].message).toMatch( + 'Resolve error: foo-bar-resolver-v2 resolve test exception', + ) + expect(testContextReports[0].loc).toEqual({ line: 1, column: 0 }) + + testContextReports.length = 0 + expect( + resolve('../fixtures/not-found', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('not-found.js') + }, + }), + ).toBeUndefined() + expect(testContextReports.length).toBe(0) + }) + + it('respects import-x/resolver as array of strings', () => { + const testContext = utils.testContext({ + 'import-x/resolver': [ + './foo-bar-resolver-v2', + './foo-bar-resolver-v1', + ], + }) + + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBe(utils.testFilePath('./bar.jsx')) + }) + + it('respects import-x/resolver as object', () => { + const testContext = utils.testContext({ + 'import-x/resolver': { './foo-bar-resolver-v2': {} }, + }) + + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBe(utils.testFilePath('./bar.jsx')) + }) + + it('respects import-x/resolver as array of objects', () => { + const testContext = utils.testContext({ + 'import-x/resolver': [ + { './foo-bar-resolver-v2': {} }, + { './foo-bar-resolver-v1': {} }, + ], + }) + + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBe(utils.testFilePath('./bar.jsx')) + }) + + it('finds resolvers from the source files rather than ../../../src/utils', () => { + const testContext = utils.testContext({ + 'import-x/resolver': { foo: {} }, + }) + + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBe(utils.testFilePath('./bar.jsx')) + }) + + it('reports invalid import-x/resolver config', () => { + const testContext = utils.testContext({ 'import-x/resolver': 123.456 }) + const testContextReports = [] + testContext.report = function (reportInfo) { + testContextReports.push(reportInfo) + } + + testContextReports.length = 0 + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBeUndefined() + expect(testContextReports[0]).toBeInstanceOf(Object) + expect(testContextReports[0].message).toMatch( + 'Resolve error: invalid resolver config', + ) + expect(testContextReports[0].loc).toEqual({ line: 1, column: 0 }) + }) + + it('reports loaded resolver with invalid interface', () => { + const resolverName = './foo-bar-resolver-invalid' + const testContext = utils.testContext({ + 'import-x/resolver': resolverName, + }) + const testContextReports = [] + testContext.report = function (reportInfo) { + testContextReports.push(reportInfo) + } + testContextReports.length = 0 + expect( + resolve('../fixtures/foo', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('foo.js') + }, + }), + ).toBeUndefined() + expect(testContextReports[0]).toBeInstanceOf(Object) + expect(testContextReports[0].message).toMatch( + `Resolve error: ${resolverName} with invalid interface loaded as resolver`, + ) + expect(testContextReports[0].loc).toEqual({ line: 1, column: 0 }) + }) + + it('respects import-x/resolve extensions', () => { + const testContext = utils.testContext({ + 'import-x/resolve': { extensions: ['.jsx'] }, + }) + + expect(resolve('./jsx/MyCoolComponent', testContext)).toBe( + utils.testFilePath('./jsx/MyCoolComponent.jsx'), + ) + }) + + it('reports load exception in a user resolver', () => { + const testContext = utils.testContext({ + 'import-x/resolver': './load-error-resolver', + }) + const testContextReports = [] + testContext.report = function (reportInfo) { + testContextReports.push(reportInfo) + } + + expect( + resolve('../fixtures/exception', { + ...testContext, + getFilename: unexpectedCallToGetFilename, + getPhysicalFilename() { + return utils.getFilename('exception.js') + }, + }), + ).toBeUndefined() + expect(testContextReports[0]).toBeInstanceOf(Object) + expect(testContextReports[0].message).toMatch( + 'Resolve error: SyntaxError: TEST SYNTAX ERROR', + ) + expect(testContextReports[0].loc).toEqual({ line: 1, column: 0 }) + }) + }, + ) + + const caseDescribe = !CASE_SENSITIVE_FS ? describe : describe.skip + caseDescribe('case sensitivity', function () { + let file + const testContext = utils.testContext({ + 'import-x/resolve': { extensions: ['.jsx'] }, + 'import-x/cache': { lifetime: 0 }, + }) + const testSettings = testContext.settings + beforeAll(() => { + file = resolve( + // Note the case difference 'MyUncoolComponent' vs 'MyUnCoolComponent' + './jsx/MyUncoolComponent', + testContext, + ) + }) + it('resolves regardless of case', () => { + // path to ./jsx/MyUncoolComponent + expect(file).toBeDefined() + }) + it('detects case does not match FS', () => { + expect(fileExistsWithCaseSync(file, testSettings)).toBe(false) + }) + it('detecting case does not include parent folder path (issue #720)', () => { + const f = path.join( + process.cwd().toUpperCase(), + './test/fixtures/jsx/MyUnCoolComponent.jsx', + ) + expect(fileExistsWithCaseSync(f, testSettings)).toBe(true) + }) + it('detecting case should include parent folder path', () => { + const f = path.join( + process.cwd().toUpperCase(), + './test/fixtures/jsx/MyUnCoolComponent.jsx', + ) + expect(fileExistsWithCaseSync(f, testSettings, true)).toBe(false) + }) + }) + + describe('rename cache correctness', () => { + const context = utils.testContext({ + 'import-x/cache': { lifetime: 1 }, + }) + + const infiniteContexts = ['∞', 'Infinity'].map(inf => [ + inf, + utils.testContext({ + 'import-x/cache': { lifetime: inf }, + }), + ]) + + const pairs = [['./CaseyKasem.js', './CASEYKASEM2.js']] + + pairs.forEach(([original, changed]) => { + describe(`${original} => ${changed}`, () => { + beforeAll(() => { + expect(resolve(original, context)).toBeDefined() + expect(resolve(changed, context)).toBeFalsy() + }) + + // settings are part of cache key + beforeAll(() => { + infiniteContexts.forEach(([, c]) => { + expect(resolve(original, c)).toBeDefined() + }) + }) + + beforeAll(done => { + fs.rename( + utils.testFilePath(original), + utils.testFilePath(changed), + done, + ) + }) + + beforeAll(done => + fs.exists(utils.testFilePath(changed), exists => + done(exists ? null : new Error('new file does not exist')), + ), + ) + + it('gets cached values within cache lifetime', () => { + // get cached values initially + expect(resolve(original, context)).toBeDefined() + }) + + it('gets updated values immediately', () => { + // get cached values initially + expect(resolve(changed, context)).toBeDefined() + }) + + // special behavior for infinity + describe('infinite cache', () => { + jest.setTimeout(1.5e3) + + beforeAll(done => { + setTimeout(done, 1100) + }) + + infiniteContexts.forEach(([inf, infiniteContext]) => { + it(`lifetime: ${inf} still gets cached values after ~1s`, () => { + // original + expect(resolve(original, infiniteContext)).toBeDefined() + }) + }) + }) + + describe('finite cache', () => { + jest.setTimeout(1.2e3) + beforeAll(done => { + setTimeout(done, 1000) + }) + it('gets correct values after cache lifetime', () => { + expect(resolve(original, context)).toBeFalsy() + expect(resolve(changed, context)).toBeDefined() + }) + }) + + afterAll(done => { + fs.rename( + utils.testFilePath(changed), + utils.testFilePath(original), + done, + ) + }) + }) + }) + }) +}) diff --git a/test/fixtures/.eslintrc.js b/test/fixtures/.eslintrc.js new file mode 100644 index 000000000..f8a3a65e1 --- /dev/null +++ b/test/fixtures/.eslintrc.js @@ -0,0 +1,325 @@ +const eslintPkg = require('eslint/package.json') +const semver = require('semver') + +const supportsArbitraryModuleNamespaceIdentifierNames = semver.satisfies( + eslintPkg.version, + '>= 8.7', +) + +const config = { + root: true, + parser: '@babel/eslint-parser', + parserOptions: { + sourceType: 'module', + ecmaVersion: 8, + }, + rules: { + 'accessor-pairs': 0, + 'array-bracket-newline': 0, + 'array-bracket-spacing': 0, + 'array-callback-return': 0, + 'array-element-newline': 0, + 'arrow-body-style': 0, + 'arrow-parens': 0, + 'arrow-spacing': 0, + 'block-scoped-var': 0, + 'block-spacing': 0, + 'brace-style': 0, + 'callback-return': 0, + camelcase: 0, + 'capitalized-comments': 0, + 'class-methods-use-this': 0, + 'comma-dangle': 0, + 'comma-spacing': 0, + 'comma-style': 0, + complexity: 0, + 'computed-property-spacing': 0, + 'consistent-return': 0, + 'consistent-this': 0, + 'constructor-super': 0, + curly: 0, + 'default-case': 0, + 'dot-location': 0, + 'dot-notation': 0, + 'eol-last': 0, + eqeqeq: 0, + 'for-direction': 0, + 'func-call-spacing': 0, + 'func-name-matching': 0, + 'func-names': 0, + 'func-style': 0, + 'function-paren-newline': 0, + 'generator-star-spacing': 0, + 'getter-return': 0, + 'global-require': 0, + 'guard-for-in': 0, + 'handle-callback-err': 0, + 'id-blacklist': 0, + 'id-length': 0, + 'id-match': 0, + 'implicit-arrow-linebreak': 0, + indent: 0, + 'indent-legacy': 0, + 'init-declarations': 0, + 'jsx-quotes': 0, + 'key-spacing': 0, + 'keyword-spacing': 0, + 'line-comment-position': 0, + 'linebreak-style': 0, + 'lines-around-comment': 0, + 'lines-around-directive': 0, + 'lines-between-class-members': 0, + 'max-classes-per-file': 0, + 'max-depth': 0, + 'max-len': 0, + 'max-lines': 0, + 'max-lines-per-function': 0, + 'max-nested-callbacks': 0, + 'max-params': 0, + 'max-statements': 0, + 'max-statements-per-line': 0, + 'multiline-comment-style': 0, + 'multiline-ternary': 0, + 'new-cap': 0, + 'new-parens': 0, + 'newline-after-var': 0, + 'newline-before-return': 0, + 'newline-per-chained-call': 0, + 'no-alert': 0, + 'no-array-constructor': 0, + 'no-async-promise-executor': 0, + 'no-await-in-loop': 0, + 'no-bitwise': 0, + 'no-buffer-constructor': 0, + 'no-caller': 0, + 'no-case-declarations': 0, + 'no-catch-shadow': 0, + 'no-class-assign': 0, + 'no-compare-neg-zero': 0, + 'no-cond-assign': 0, + 'no-confusing-arrow': 0, + 'no-console': 0, + 'no-const-assign': 0, + 'no-constant-condition': 0, + 'no-continue': 0, + 'no-control-regex': 0, + 'no-debugger': 0, + 'no-delete-var': 0, + 'no-div-regex': 0, + 'no-dupe-args': 0, + 'no-dupe-class-members': 0, + 'no-dupe-keys': 0, + 'no-duplicate-case': 0, + 'no-duplicate-imports': 0, + 'no-else-return': 0, + 'no-empty': 0, + 'no-empty-character-class': 0, + 'no-empty-function': 0, + 'no-empty-pattern': 0, + 'no-eq-null': 0, + 'no-eval': 0, + 'no-ex-assign': 0, + 'no-extend-native': 0, + 'no-extra-bind': 0, + 'no-extra-boolean-cast': 0, + 'no-extra-label': 0, + 'no-extra-parens': 0, + 'no-extra-semi': 0, + 'no-fallthrough': 0, + 'no-floating-decimal': 0, + 'no-func-assign': 0, + 'no-global-assign': 0, + 'no-implicit-coercion': 0, + 'no-implicit-globals': 0, + 'no-implied-eval': 0, + 'no-inline-comments': 0, + 'no-inner-declarations': 0, + 'no-invalid-regexp': 0, + 'no-invalid-this': 0, + 'no-irregular-whitespace': 0, + 'no-iterator': 0, + 'no-label-var': 0, + 'no-labels': 0, + 'no-lone-blocks': 0, + 'no-lonely-if': 0, + 'no-loop-func': 0, + 'no-magic-numbers': 0, + 'no-misleading-character-class': 0, + 'no-mixed-operators': 0, + 'no-mixed-requires': 0, + 'no-mixed-spaces-and-tabs': 0, + 'no-multi-assign': 0, + 'no-multi-spaces': 0, + 'no-multi-str': 0, + 'no-multiple-empty-lines': 0, + 'no-native-reassign': 0, + 'no-negated-condition': 0, + 'no-negated-in-lhs': 0, + 'no-nested-ternary': 0, + 'no-new': 0, + 'no-new-func': 0, + 'no-new-object': 0, + 'no-new-require': 0, + 'no-new-symbol': 0, + 'no-new-wrappers': 0, + 'no-obj-calls': 0, + 'no-octal': 0, + 'no-octal-escape': 0, + 'no-param-reassign': 0, + 'no-path-concat': 0, + 'no-plusplus': 0, + 'no-process-env': 0, + 'no-process-exit': 0, + 'no-proto': 0, + 'no-prototype-builtins': 0, + 'no-redeclare': 0, + 'no-regex-spaces': 0, + 'no-restricted-globals': 0, + 'no-restricted-imports': 0, + 'no-restricted-modules': 0, + 'no-restricted-properties': 0, + 'no-restricted-syntax': 0, + 'no-return-assign': 0, + 'no-return-await': 0, + 'no-script-url': 0, + 'no-self-assign': 0, + 'no-self-compare': 0, + 'no-sequences': 0, + 'no-shadow': 0, + 'no-shadow-restricted-names': 0, + 'no-spaced-func': 0, + 'no-sparse-arrays': 0, + 'no-sync': 0, + 'no-tabs': 0, + 'no-template-curly-in-string': 0, + 'no-ternary': 0, + 'no-this-before-super': 0, + 'no-throw-literal': 0, + 'no-trailing-spaces': 0, + 'no-undef': 0, + 'no-undef-init': 0, + 'no-undefined': 0, + 'no-underscore-dangle': 0, + 'no-unexpected-multiline': 0, + 'no-unmodified-loop-condition': 0, + 'no-unneeded-ternary': 0, + 'no-unreachable': 0, + 'no-unsafe-finally': 0, + 'no-unsafe-negation': 0, + 'no-unused-expressions': 0, + 'no-unused-labels': 0, + 'no-unused-vars': 0, + 'no-use-before-define': 0, + 'no-useless-call': 0, + 'no-useless-catch': 0, + 'no-useless-computed-key': 0, + 'no-useless-concat': 0, + 'no-useless-constructor': 0, + 'no-useless-escape': 0, + 'no-useless-rename': 0, + 'no-useless-return': 0, + 'no-var': 0, + 'no-void': 0, + 'no-warning-comments': 0, + 'no-whitespace-before-property': 0, + 'no-with': 0, + 'nonblock-statement-body-position': 0, + 'object-curly-newline': 0, + 'object-curly-spacing': 0, + 'object-property-newline': 0, + 'object-shorthand': 0, + 'one-var': 0, + 'one-var-declaration-per-line': 0, + 'operator-assignment': 0, + 'operator-linebreak': 0, + 'padded-blocks': 0, + 'padding-line-between-statements': 0, + 'prefer-arrow-callback': 0, + 'prefer-const': 0, + 'prefer-destructuring': 0, + 'prefer-named-capture-group': 0, + 'prefer-numeric-literals': 0, + 'prefer-object-spread': 0, + 'prefer-promise-reject-errors': 0, + 'prefer-reflect': 0, + 'prefer-rest-params': 0, + 'prefer-spread': 0, + 'prefer-template': 0, + 'quote-props': 0, + quotes: 0, + radix: 0, + 'require-atomic-updates': 0, + 'require-await': 0, + 'require-jsdoc': 0, + 'require-unicode-regexp': 0, + 'require-yield': 0, + 'rest-spread-spacing': 0, + semi: 0, + 'semi-spacing': 0, + 'semi-style': 0, + 'sort-imports': 0, + 'sort-keys': 0, + 'sort-vars': 0, + 'space-before-blocks': 0, + 'space-before-function-paren': 0, + 'space-in-parens': 0, + 'space-infix-ops': 0, + 'space-unary-ops': 0, + 'spaced-comment': 0, + strict: 0, + 'switch-colon-spacing': 0, + 'symbol-description': 0, + 'template-curly-spacing': 0, + 'template-tag-spacing': 0, + 'unicode-bom': 0, + 'use-isnan': 0, + 'valid-jsdoc': 0, + 'valid-typeof': 0, + 'vars-on-top': 0, + 'wrap-iife': 0, + 'wrap-regex': 0, + 'yield-star-spacing': 0, + yoda: 0, + 'import-x/no-unresolved': 0, + 'import-x/named': 0, + 'import-x/namespace': 0, + 'import-x/default': 0, + 'import-x/export': 0, + 'import-x/no-named-as-default': 0, + 'import-x/no-named-as-default-member': 0, + 'import-x/no-duplicates': 0, + 'import-x/no-extraneous-dependencies': 0, + 'import-x/unambiguous': 0, + }, + ignorePatterns: [ + 'default-export-namespace-string.js', + 'default-export-string.js', + 'export-default-string-and-named.js', + 'no-unused-modules/arbitrary-module-namespace-identifier-name-a.js', + 'no-unused-modules/arbitrary-module-namespace-identifier-name-b.js', + 'no-unused-modules/arbitrary-module-namespace-identifier-name-c.js', + ], +} + +if (supportsArbitraryModuleNamespaceIdentifierNames) { + config.ignorePatterns = [] + config.overrides = [ + // For parsing arbitrary module namespace names + { + files: [ + 'default-export-namespace-string.js', + 'default-export-string.js', + 'export-default-string-and-named.js', + 'no-unused-modules/arbitrary-module-namespace-identifier-name-a.js', + 'no-unused-modules/arbitrary-module-namespace-identifier-name-b.js', + 'no-unused-modules/arbitrary-module-namespace-identifier-name-c.js', + ], + parser: 'espree', + parserOptions: { + ecmaVersion: 2022, + }, + }, + ] +} + +module.exports = config diff --git a/tests/files/@importType/index.js b/test/fixtures/@importType/index.js similarity index 100% rename from tests/files/@importType/index.js rename to test/fixtures/@importType/index.js diff --git a/tests/files/@my-alias/fn.js b/test/fixtures/@my-alias/fn.js similarity index 100% rename from tests/files/@my-alias/fn.js rename to test/fixtures/@my-alias/fn.js diff --git a/tests/files/CaseyKasem.js b/test/fixtures/CaseyKasem.js similarity index 100% rename from tests/files/CaseyKasem.js rename to test/fixtures/CaseyKasem.js diff --git a/tests/files/alternate-root/in-alternate-root.js b/test/fixtures/alternate-root/in-alternate-root.js similarity index 100% rename from tests/files/alternate-root/in-alternate-root.js rename to test/fixtures/alternate-root/in-alternate-root.js diff --git a/tests/files/bar.coffee b/test/fixtures/bar.coffee similarity index 100% rename from tests/files/bar.coffee rename to test/fixtures/bar.coffee diff --git a/test/fixtures/bar.js b/test/fixtures/bar.js new file mode 100644 index 000000000..bc5249def --- /dev/null +++ b/test/fixtures/bar.js @@ -0,0 +1,4 @@ +export default 'bar' +export function foo() { + return 'foo' +} diff --git a/tests/files/bar.json b/test/fixtures/bar.json similarity index 100% rename from tests/files/bar.json rename to test/fixtures/bar.json diff --git a/tests/files/bar.jsx b/test/fixtures/bar.jsx similarity index 100% rename from tests/files/bar.jsx rename to test/fixtures/bar.jsx diff --git a/tests/files/bar/index.js b/test/fixtures/bar/index.js similarity index 100% rename from tests/files/bar/index.js rename to test/fixtures/bar/index.js diff --git a/tests/files/broken-trampoline.js b/test/fixtures/broken-trampoline.js similarity index 100% rename from tests/files/broken-trampoline.js rename to test/fixtures/broken-trampoline.js diff --git a/test/fixtures/bundled-dependencies/as-array-bundle-deps/package.json b/test/fixtures/bundled-dependencies/as-array-bundle-deps/package.json new file mode 100644 index 000000000..44af1ddc3 --- /dev/null +++ b/test/fixtures/bundled-dependencies/as-array-bundle-deps/package.json @@ -0,0 +1,6 @@ +{ + "bundleDependencies": [ + "@generated/foo" + ], + "dummy": true +} diff --git a/test/fixtures/bundled-dependencies/as-object/package.json b/test/fixtures/bundled-dependencies/as-object/package.json new file mode 100644 index 000000000..2fb621f2f --- /dev/null +++ b/test/fixtures/bundled-dependencies/as-object/package.json @@ -0,0 +1,6 @@ +{ + "bundledDependencies": { + "@generated/foo": "latest" + }, + "dummy": true +} diff --git a/test/fixtures/bundled-dependencies/race-condition/package.json b/test/fixtures/bundled-dependencies/race-condition/package.json new file mode 100644 index 000000000..2e341adf6 --- /dev/null +++ b/test/fixtures/bundled-dependencies/race-condition/package.json @@ -0,0 +1,9 @@ +{ + "bundledDependencies": { + "@generated/bar": "latest" + }, + "bundleDependencies": [ + "@generated/foo" + ], + "dummy": true +} diff --git a/test/fixtures/color.js b/test/fixtures/color.js new file mode 100644 index 000000000..de74b014a --- /dev/null +++ b/test/fixtures/color.js @@ -0,0 +1 @@ +export const example = 'example' diff --git a/tests/files/common-module.js b/test/fixtures/common-module.js similarity index 50% rename from tests/files/common-module.js rename to test/fixtures/common-module.js index 54915edd3..98d026101 100644 --- a/tests/files/common-module.js +++ b/test/fixtures/common-module.js @@ -1,5 +1,7 @@ module.exports = { a: 1, b: 2, - c: function() { return 3 } + c: function () { + return 3 + }, } diff --git a/tests/files/common.js b/test/fixtures/common.js similarity index 87% rename from tests/files/common.js rename to test/fixtures/common.js index 59e8b7e80..bd9d5ccfa 100644 --- a/tests/files/common.js +++ b/test/fixtures/common.js @@ -1,6 +1,6 @@ module.exports = { a: 1, - b: 2 + b: 2, } var c = 3 diff --git a/tests/files/commonjs-namespace/a.js b/test/fixtures/commonjs-namespace/a.js similarity index 100% rename from tests/files/commonjs-namespace/a.js rename to test/fixtures/commonjs-namespace/a.js diff --git a/tests/files/commonjs-namespace/b.js b/test/fixtures/commonjs-namespace/b.js similarity index 100% rename from tests/files/commonjs-namespace/b.js rename to test/fixtures/commonjs-namespace/b.js diff --git a/tests/files/component.html b/test/fixtures/component.html similarity index 93% rename from tests/files/component.html rename to test/fixtures/component.html index b63f55e0b..000bfbc05 100644 --- a/tests/files/component.html +++ b/test/fixtures/component.html @@ -1,5 +1,9 @@
- + Resources & Tools height="24" viewBox="0 0 24 24" > - + @@ -63,7 +70,10 @@

Resources & Tools

Here are some links to help you get started.