Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: import-js/eslint-plugin-import
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 20c373c509ad33e339b96fc38b0daaef8c5f6e9a
Choose a base ref
...
head repository: import-js/eslint-plugin-import
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 7c239fed485ea0785a96c1fa2045d96c181bb79c
Choose a head ref

Commits on Oct 25, 2018

  1. [New] named: add commonjs option

    vikr01 authored and ljharb committed Oct 25, 2018

    Verified

    This commit was signed with the committer’s verified signature.
    lifeofguenter Günter Grodotzki
    Copy the full SHA
    54d86c8 View commit details

Commits on Oct 26, 2018

  1. [New] no-dynamic-require: add option esmodule

    See #700.
    vikr01 authored and ljharb committed Oct 26, 2018

    Verified

    This commit was signed with the committer’s verified signature.
    lifeofguenter Günter Grodotzki
    Copy the full SHA
    7163824 View commit details

Commits on Oct 24, 2019

  1. [Tests] no-cycle: Restructure test files

    soryy708 authored and ljharb committed Oct 24, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    429f3f6 View commit details

Commits on Jul 6, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    b743a65 View commit details

Commits on Apr 13, 2021

  1. Verified

    This commit was signed with the committer’s verified signature.
    lifeofguenter Günter Grodotzki
    Copy the full SHA
    7aea664 View commit details

Commits on May 25, 2021

  1. [Fix] no-extraneous-dependencies: fix package name algorithm

    - resolve nested package.json problems (a/b/c import will check a, a/b and a/b/c)
    - resolve renamed dependencies: checks the import name and the resolve package name
    
    Fixes #2066. Fixes #2065. Fixes #2058. Fixes #2078.
    jeromeh authored and ljharb committed May 25, 2021
    Copy the full SHA
    da8d584 View commit details

Commits on May 26, 2021

  1. Copy the full SHA
    81b9d24 View commit details

Commits on May 27, 2021

  1. [meta] fix changelog entries

    ljharb committed May 27, 2021
    Copy the full SHA
    ec10721 View commit details

Commits on May 29, 2021

  1. Bump to v2.23.4

    ljharb committed May 29, 2021
    Copy the full SHA
    998c300 View commit details

Commits on Jun 2, 2021

  1. [New] no-namespace: Add ignore option

    Closes #1916.
    Closes #1903.
    Arkadii Berezkin authored and ljharb committed Jun 2, 2021
    1
    Copy the full SHA
    bba59c4 View commit details

Commits on Jun 4, 2021

  1. Copy the full SHA
    4079482 View commit details

Commits on Jun 14, 2021

  1. [Fix] no-extraneous-dependencies: add ESM intermediate package.json…

    … support
    
    Fixes #2120.
    jeromeh authored and ljharb committed Jun 14, 2021
    Copy the full SHA
    b3d8c0c View commit details

Commits on Jun 25, 2021

  1. Copy the full SHA
    1012eb9 View commit details

Commits on Jul 6, 2021

  1. [Fix] extensions/importType: fix isScoped treating @/abc as scope…

    …d module
    
    Fixes #2145
    Rafael Perello authored and ljharb committed Jul 6, 2021
    Copy the full SHA
    00d7bc8 View commit details

Commits on Jul 19, 2021

  1. Copy the full SHA
    95e6011 View commit details

Commits on Jul 21, 2021

  1. Copy the full SHA
    bfab4cc View commit details

Commits on Jul 22, 2021

  1. [Docs] order: improve the documentation for the `pathGroupsExcluded…

    …ImportTypes` option
    liby authored and ljharb committed Jul 22, 2021
    Copy the full SHA
    b236748 View commit details

Commits on Jul 26, 2021

  1. [Docs] named: update docs with module info and deprecation notice…

    … for `jsnext:main`
    MustafaHaddara authored and ljharb committed Jul 26, 2021
    Copy the full SHA
    9521284 View commit details
  2. Copy the full SHA
    96e4332 View commit details

Commits on Jul 27, 2021

  1. Copy the full SHA
    5478a40 View commit details
  2. Copy the full SHA
    8dd13e8 View commit details

Commits on Aug 1, 2021

  1. [Tests] eslint v7.32 added a new fatalErrorCount property to `cli.e…

    …xecuteOnFiles` result
    ljharb committed Aug 1, 2021
    Copy the full SHA
    e20db4e View commit details

Commits on Aug 3, 2021

  1. Copy the full SHA
    c8876b1 View commit details
  2. [eslint] fix eslint failures

    ljharb committed Aug 3, 2021
    Copy the full SHA
    3761435 View commit details

Commits on Aug 5, 2021

  1. Copy the full SHA
    7626a14 View commit details

Commits on Aug 7, 2021

  1. Copy the full SHA
    32bf645 View commit details

Commits on Aug 8, 2021

  1. [meta] update repo URLs

    ljharb committed Aug 8, 2021
    Copy the full SHA
    794e869 View commit details
  2. [resolvers/node] v0.3.5

    ljharb committed Aug 8, 2021
    Copy the full SHA
    74fa6c9 View commit details
  3. utils: v2.6.2

    ljharb committed Aug 8, 2021
    Copy the full SHA
    546f87f View commit details
  4. Bump to v2.24.0

    ljharb committed Aug 8, 2021
    Copy the full SHA
    202e5e0 View commit details

Commits on Aug 9, 2021

  1. [Fix] ExportMap: Add default export when esModuleInterop is true an…

    …d anything is exported
    
    Fixes #2183. See #1689.
    Maxim-Mazurok authored and ljharb committed Aug 9, 2021
    Copy the full SHA
    ce540b6 View commit details
  2. Copy the full SHA
    750ba25 View commit details

Commits on Aug 13, 2021

  1. [meta] add Open Collective

    ljharb committed Aug 13, 2021
    Copy the full SHA
    ec825d0 View commit details
  2. [meta] fix some formatting

    ljharb committed Aug 13, 2021
    Copy the full SHA
    513bb0b View commit details

Commits on Aug 14, 2021

  1. [resolvers/node] [fix] when "module" does not exist, fall back to "main"

    Fixes #2186. This is actually exposing a bug with packages that are broken - that ship an invalid "module" field - but it‘s a more friendly and node-accurate behavior to still work when "main" works.
    ljharb committed Aug 14, 2021
    Copy the full SHA
    fa3192a View commit details
  2. [meta] fix some formatting

    ljharb committed Aug 14, 2021
    Copy the full SHA
    f0c1756 View commit details

Commits on Aug 15, 2021

  1. [resolvers/node] v0.3.6

    ljharb committed Aug 15, 2021
    Copy the full SHA
    8be2ec2 View commit details

Commits on Aug 16, 2021

  1. Copy the full SHA
    7610790 View commit details

Commits on Aug 17, 2021

  1. Copy the full SHA
    ce8b203 View commit details
  2. Copy the full SHA
    b2bf591 View commit details
  3. [fix] no-duplicates: correctly handle case of mixed default/named t…

    …ype imports
    
    Co-authored-by: Nathan Walters <nwalters@nerdwallet.com>
    Co-authored-by: Gord Pearson <gord.pearson@shopify.com>
    2 people authored and ljharb committed Aug 17, 2021
    Copy the full SHA
    712ee49 View commit details

Commits on Aug 18, 2021

  1. Copy the full SHA
    3ff4d77 View commit details
  2. Copy the full SHA
    3977c42 View commit details

Commits on Aug 19, 2021

  1. [Deps] update eslint-import-resolver-node, is-core-module, `objec…

    …t.values`, `tsconfig-paths`
    ljharb committed Aug 19, 2021
    Copy the full SHA
    c5d208d View commit details
  2. [Dev Deps] update coveralls

    ljharb committed Aug 19, 2021
    Copy the full SHA
    62cb4b5 View commit details
  3. Copy the full SHA
    4dc4651 View commit details
  4. Bump to v2.24.1

    ljharb committed Aug 19, 2021
    Copy the full SHA
    6171a7a View commit details
  5. 2
    Copy the full SHA
    6c957e7 View commit details

Commits on Aug 20, 2021

  1. [meta] rename default branch

    ljharb committed Aug 20, 2021
    Copy the full SHA
    9485c83 View commit details

Commits on Aug 21, 2021

  1. [Tests] no-extraneous-dependencies: add passing test case of webpac…

    …k loader syntax
    
    Closes #1969
    ljharb committed Aug 21, 2021
    Copy the full SHA
    94d6739 View commit details
Showing with 4,458 additions and 2,114 deletions.
  1. +3 −3 .babelrc
  2. +51 −5 .eslintrc
  3. +1 −1 .github/FUNDING.yml
  4. +29 −2 .github/workflows/node-4+.yml
  5. +2 −1 .github/workflows/packages.yml
  6. +2 −2 .gitignore
  7. +6 −2 .nycrc
  8. +2 −1 .travis.yml
  9. +646 −424 CHANGELOG.md
  10. +1 −1 CONTRIBUTING.md
  11. +19 −8 README.md
  12. +8 −8 RELEASE.md
  13. +38 −14 appveyor.yml
  14. +5 −2 config/typescript.js
  15. +5 −3 docs/rules/extensions.md
  16. +2 −2 docs/rules/first.md
  17. +1 −1 docs/rules/imports-first.md
  18. +30 −8 docs/rules/max-dependencies.md
  19. +5 −3 docs/rules/named.md
  20. +1 −1 docs/rules/no-cycle.md
  21. +2 −2 docs/rules/no-duplicates.md
  22. +2 −2 docs/rules/no-mutable-exports.md
  23. +11 −0 docs/rules/no-namespace.md
  24. +48 −2 docs/rules/no-restricted-paths.md
  25. +18 −3 docs/rules/no-unresolved.md
  26. +2 −2 docs/rules/no-unused-modules.md
  27. +1 −1 docs/rules/no-useless-path-segments.md
  28. +22 −1 docs/rules/order.md
  29. +2 −2 docs/rules/unambiguous.md
  30. +3 −3 memo-parser/package.json
  31. +21 −24 package.json
  32. +0 −3 resolvers/.eslintrc.yml
  33. +19 −9 resolvers/node/CHANGELOG.md
  34. +26 −7 resolvers/node/index.js
  35. +5 −7 resolvers/node/package.json
  36. +4 −0 resolvers/node/test/package-mains/jsnext/package.json
  37. 0 resolvers/node/test/package-mains/jsnext/src/index.js
  38. +5 −0 resolvers/node/test/package-mains/module-and-jsnext/package.json
  39. 0 resolvers/node/test/package-mains/module-and-jsnext/src/index.js
  40. +1 −0 resolvers/node/test/package-mains/module-broken/main.js
  41. +4 −0 resolvers/node/test/package-mains/module-broken/package.json
  42. +4 −0 resolvers/node/test/package-mains/module/package.json
  43. 0 resolvers/node/test/package-mains/module/src/index.js
  44. +1 −0 resolvers/node/test/package-mains/package.json
  45. +32 −0 resolvers/node/test/packageMains.js
  46. +44 −38 resolvers/webpack/CHANGELOG.md
  47. +20 −21 resolvers/webpack/index.js
  48. +8 −12 resolvers/webpack/package.json
  49. +2 −2 resolvers/webpack/test/alias.js
  50. +5 −5 resolvers/webpack/test/config.js
  51. +5 −0 resolvers/webpack/test/package-mains/module-and-jsnext/package.json
  52. 0 resolvers/webpack/test/package-mains/module-and-jsnext/src/index.js
  53. +1 −0 resolvers/webpack/test/package-mains/module-broken/main.js
  54. +4 −0 resolvers/webpack/test/package-mains/module-broken/package.json
  55. +18 −9 resolvers/webpack/test/packageMains.js
  56. +0 −3 src/.eslintrc.yml
  57. +112 −47 src/ExportMap.js
  58. +3 −7 src/core/importType.js
  59. +10 −6 src/core/packagePath.js
  60. +1 −1 src/docsUrl.js
  61. +2 −2 src/rules/default.js
  62. +2 −2 src/rules/dynamic-import-chunkname.js
  63. +4 −4 src/rules/export.js
  64. +2 −2 src/rules/exports-last.js
  65. +9 −7 src/rules/extensions.js
  66. +5 −5 src/rules/first.js
  67. +12 −6 src/rules/max-dependencies.js
  68. +91 −25 src/rules/named.js
  69. +7 −7 src/rules/namespace.js
  70. +6 −6 src/rules/newline-after-import.js
  71. +1 −1 src/rules/no-absolute-path.js
  72. +1 −1 src/rules/no-amd.js
  73. +1 −1 src/rules/no-anonymous-default-export.js
  74. +2 −2 src/rules/no-commonjs.js
  75. +4 −4 src/rules/no-cycle.js
  76. +1 −1 src/rules/no-deprecated.js
  77. +34 −11 src/rules/no-duplicates.js
  78. +31 −4 src/rules/no-dynamic-require.js
  79. +74 −29 src/rules/no-extraneous-dependencies.js
  80. +20 −7 src/rules/no-import-module-exports.js
  81. +1 −1 src/rules/no-mutable-exports.js
  82. +1 −1 src/rules/no-named-as-default-member.js
  83. +1 −1 src/rules/no-named-as-default.js
  84. +1 −1 src/rules/no-named-default.js
  85. +26 −9 src/rules/no-namespace.js
  86. +1 −1 src/rules/no-nodejs-modules.js
  87. +4 −4 src/rules/no-relative-packages.js
  88. +1 −1 src/rules/no-relative-parent-imports.js
  89. +71 −19 src/rules/no-restricted-paths.js
  90. +2 −2 src/rules/no-self-import.js
  91. +1 −1 src/rules/no-unassigned-import.js
  92. +27 −18 src/rules/no-unresolved.js
  93. +80 −38 src/rules/no-unused-modules.js
  94. +4 −4 src/rules/no-useless-path-segments.js
  95. +2 −2 src/rules/no-webpack-loader-syntax.js
  96. +54 −41 src/rules/order.js
  97. +10 −11 src/rules/prefer-default-export.js
  98. +2 −2 src/rules/unambiguous.js
  99. +9 −0 tests/.eslintrc
  100. +0 −8 tests/.eslintrc.yml
  101. +15 −6 tests/dep-time-travel.sh
  102. +0 −2 tests/files/cycles/depth-one.js
  103. +2 −0 tests/files/cycles/es6/depth-one.js
  104. 0 tests/files/cycles/{ → es6}/depth-three-indirect.js
  105. 0 tests/files/cycles/{ → es6}/depth-three-star.js
  106. 0 tests/files/cycles/{ → es6}/depth-two.js
  107. +1 −1 tests/files/cycles/flow-types-depth-two.js
  108. +1 −0 tests/files/export-star-2/middle.js
  109. +1 −0 tests/files/export-star-2/upstream.js
  110. +1 −0 tests/files/export-star-3/b.ts
  111. 0 tests/files/export-star-3/c.ts
  112. +3 −0 tests/files/export-star-4/module/feature.jsx
  113. +1 −0 tests/files/export-star-4/module/index.ts
  114. +1 −0 tests/files/export-star/extfield.js
  115. +1 −0 tests/files/export-star/extfield2.js
  116. +2 −0 tests/files/export-star/models.js
  117. +1 −1 tests/files/flowtypes.js
  118. +2 −4 tests/files/foo-bar-resolver-no-version.js
  119. +6 −8 tests/files/foo-bar-resolver-v1.js
  120. +4 −6 tests/files/foo-bar-resolver-v2.js
  121. +3 −0 tests/files/missing-entrypoint/package.json
  122. +13 −0 tests/files/no-unused-modules/dynamic-import-js-2.js
  123. +5 −0 tests/files/no-unused-modules/dynamic-import-js.js
  124. +5 −0 tests/files/no-unused-modules/exports-for-dynamic-js-2.js
  125. +5 −0 tests/files/no-unused-modules/exports-for-dynamic-js.js
  126. +6 −0 tests/files/no-unused-modules/typescript/dynamic-import-ts.ts
  127. +5 −0 tests/files/no-unused-modules/typescript/exports-for-dynamic-ts.ts
  128. 0 tests/files/node_modules/esm-package-not-in-pkg-json/esm-module/index.js
  129. +4 −0 tests/files/node_modules/esm-package-not-in-pkg-json/esm-module/package.json
  130. 0 tests/files/node_modules/esm-package-not-in-pkg-json/index.js
  131. +5 −0 tests/files/node_modules/esm-package-not-in-pkg-json/package.json
  132. 0 tests/files/node_modules/esm-package/esm-module/index.js
  133. +4 −0 tests/files/node_modules/esm-package/esm-module/package.json
  134. 0 tests/files/node_modules/esm-package/index.js
  135. +5 −0 tests/files/node_modules/esm-package/package.json
  136. +1 −0 tests/files/node_modules/rxjs/index.js
  137. +1 −0 tests/files/node_modules/rxjs/operators/index.js
  138. +5 −0 tests/files/node_modules/rxjs/operators/package.json
  139. +5 −0 tests/files/node_modules/rxjs/package.json
  140. +3 −1 tests/files/package.json
  141. +19 −0 tests/files/typescript-export-react-test-renderer/index.d.ts
  142. +5 −0 tests/files/typescript-export-react-test-renderer/tsconfig.json
  143. +3 −0 tests/files/typescript-extended-config/index.d.ts
  144. +5 −0 tests/files/typescript-extended-config/tsconfig.base.json
  145. +4 −0 tests/files/typescript-extended-config/tsconfig.json
  146. +3 −3 tests/files/typescript.ts
  147. +2 −1 tests/files/webpack.config.js
  148. +1 −1 tests/files/with-typescript-dev-dependencies/package.json
  149. +112 −44 tests/src/cli.js
  150. +1 −1 tests/src/config/typescript.js
  151. +2 −2 tests/src/core/docsUrl.js
  152. +1 −1 tests/src/core/eslintParser.js
  153. +14 −10 tests/src/core/getExports.js
  154. +41 −29 tests/src/core/importType.js
  155. +2 −3 tests/src/core/parse.js
  156. +1 −1 tests/src/core/parseStubParser.js
  157. +182 −20 tests/src/core/resolve.js
  158. +34 −12 tests/src/rules/default.js
  159. +14 −2 tests/src/rules/export.js
  160. +42 −1 tests/src/rules/extensions.js
  161. +74 −70 tests/src/rules/first.js
  162. +57 −1 tests/src/rules/max-dependencies.js
  163. +142 −89 tests/src/rules/named.js
  164. +17 −5 tests/src/rules/namespace.js
  165. +8 −8 tests/src/rules/newline-after-import.js
  166. +1 −1 tests/src/rules/no-amd.js
  167. +1 −1 tests/src/rules/no-commonjs.js
  168. +113 −103 tests/src/rules/no-cycle.js
  169. +1 −1 tests/src/rules/no-deprecated.js
  170. +102 −23 tests/src/rules/no-duplicates.js
  171. +84 −0 tests/src/rules/no-dynamic-require.js
  172. +56 −57 tests/src/rules/no-extraneous-dependencies.js
  173. +60 −3 tests/src/rules/no-import-module-exports.js
  174. +1 −1 tests/src/rules/no-internal-modules.js
  175. +1 −0 tests/src/rules/no-namespace.js
  176. +71 −1 tests/src/rules/no-restricted-paths.js
  177. +54 −6 tests/src/rules/no-unresolved.js
  178. +452 −200 tests/src/rules/no-unused-modules.js
  179. +1 −1 tests/src/rules/no-useless-path-segments.js
  180. +1 −1 tests/src/rules/no-webpack-loader-syntax.js
  181. +384 −274 tests/src/rules/order.js
  182. +30 −48 tests/src/rules/prefer-default-export.js
  183. +4 −1 tests/src/utils.js
  184. +0 −3 utils/.eslintrc.yml
  185. +56 −27 utils/CHANGELOG.md
  186. +2 −2 utils/module-require.js
  187. +4 −4 utils/moduleVisitor.js
  188. +5 −4 utils/package.json
  189. +49 −4 utils/parse.js
  190. +8 −0 utils/pkgUp.js
  191. +52 −0 utils/readPkgUp.js
  192. +6 −9 utils/resolve.js
  193. +2 −3 utils/unambiguous.js
  194. +24 −0 utils/visit.js
6 changes: 3 additions & 3 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"presets": [ "es2015-argon" ],
"presets": ["airbnb"],
"sourceMaps": "inline",
"retainLines": true,
"env": {
"test": {
"plugins": [
"istanbul",
[ "module-resolver", { "root": [ "./src/" ] } ]
["module-resolver", { "root": ["./src/"] }],
]
},
"testCompiled": {
"plugins": [
[ "module-resolver", { "root": [ "./lib/" ] } ]
["module-resolver", { "root": ["./lib/"] }],
]
}
}
56 changes: 51 additions & 5 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -15,29 +15,53 @@
},
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 6,
"ecmaVersion": 2020,
},
"rules": {
"comma-dangle": [2, "always-multiline"],
"comma-dangle": ["error", {
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "always-multiline",
}],
"comma-style": [2, "last"],
"curly": [2, "multi-line"],
"eol-last": [2, "always"],
"eqeqeq": [2, "allow-null"],
"func-call-spacing": 2,
"indent": [2, 2],
"max-len": [1, 99, 2],
"keyword-spacing": ["error", {
"before": true,
"after": true,
"overrides": {
"return": { "after": true },
"throw": { "after": true },
"case": { "after": true }
}
}],
"max-len": 0,
"no-cond-assign": [2, "always"],
"no-return-assign": [2, "always"],
"no-shadow": 1,
"no-var": 2,
"object-curly-spacing": [2, "always"],
"object-shorthand": ["error", "always", {
"ignoreConstructors": false,
"avoidQuotes": true,
}],
"one-var": [2, "never"],
"prefer-const": 2,
"quotes": [2, "single", {
"allowTemplateLiterals": true,
"avoidEscape": true,
}],
"semi": [2, "always"],
"space-before-function-paren": ["error", {
"anonymous": "always",
"named": "never",
"asyncArrow": "always",
}],

"eslint-plugin/consistent-output": [
"error",
"always",
@@ -72,6 +96,12 @@
"no-console": "off",
},
},
{
"files": "resolvers/**",
"env": {
"es6": false,
},
},
{
"files": [
"resolvers/*/test/**/*",
@@ -80,6 +110,22 @@
"mocha": true,
"es6": false
},
}
},
{
"files": "utils/**",
"parserOptions": {
"ecmaVersion": 6,
},
"rules": {
"comma-dangle": ["error", {
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "never"
}],
"no-console": 1,
},
},
],
}
2 changes: 1 addition & 1 deletion .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@

github: [ljharb]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective 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
31 changes: 29 additions & 2 deletions .github/workflows/node-4+.yml
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ jobs:
with:
versionsAsRoot: true
type: majors
preset: '>= 6' # preset: '>=4' # see https://github.com/benmosher/eslint-plugin-import/issues/2053
preset: '>= 6' # preset: '>=4' # see https://github.com/import-js/eslint-plugin-import/issues/2053

latest:
needs: [matrix]
@@ -26,37 +26,63 @@ jobs:
matrix:
node-version: ${{ fromJson(needs.matrix.outputs.latest) }}
eslint:
- 8
- 7
- 6
- 5
- 4
- 3
- 2
include:
- 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
@@ -76,8 +102,9 @@ jobs:
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
- run: npm run coveralls
- uses: codecov/codecov-action@v2

node:
name: 'node 4+'
3 changes: 2 additions & 1 deletion .github/workflows/packages.yml
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ jobs:
id: set-matrix
with:
type: 'majors'
preset: '>= 6' # preset: '>=4' # see https://github.com/benmosher/eslint-plugin-import/issues/2053
preset: '>= 6' # preset: '>=4' # see https://github.com/import-js/eslint-plugin-import/issues/2053
versionsAsRoot: true

tests:
@@ -43,6 +43,7 @@ jobs:
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@v2

packages:
name: 'packages: all tests'
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -13,10 +13,10 @@ lib-cov
# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Compiled binary addons (http://nodejs.org/api/addons.html)
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Copied from ./LICENSE for the npm module releases
8 changes: 6 additions & 2 deletions .nycrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"all": true,
"check-coverage": false,
"reporter": ["text-summary", "text", "html", "json"],
"reporter": ["text-summary", "lcov", "text", "html", "json"],
"require": [
"babel-register"
],
@@ -10,6 +10,10 @@
"exclude": [
"coverage",
"test",
"tests"
"tests",
"resolvers/*/test",
"scripts",
"memo-parser",
"lib"
]
}
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -31,9 +31,10 @@ before_install:
install:
- 'npm install'
- 'if [ -n "${ESLINT_VERSION}" ]; then ./tests/dep-time-travel.sh; fi'
- 'npm run pretest'

script:
- npm run tests-only

after_success:
- npm run coveralls
- bash <(curl -Os https://uploader.codecov.io/latest/linux/codecov)
1,070 changes: 646 additions & 424 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -110,7 +110,7 @@ If we can all get together behind the common goal of embracing empathy, everythi

#### Attribution

Thanks for help from http://mozillascience.github.io/working-open-workshop/contributing/
Thanks for help from https://mozillascience.github.io/working-open-workshop/contributing/
for inspiration before I wrote this. --ben

[README.md]: ./README.md
27 changes: 19 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# eslint-plugin-import

[![build status](https://travis-ci.org/benmosher/eslint-plugin-import.svg?branch=master)](https://travis-ci.org/benmosher/eslint-plugin-import)
[![Coverage Status](https://coveralls.io/repos/github/benmosher/eslint-plugin-import/badge.svg?branch=master)](https://coveralls.io/github/benmosher/eslint-plugin-import?branch=master)
[![win32 build status](https://ci.appveyor.com/api/projects/status/3mw2fifalmjlqf56/branch/master?svg=true)](https://ci.appveyor.com/project/benmosher/eslint-plugin-import/branch/master)
[![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)
[![coverage][codecov-image]][codecov-url]
[![win32 build status](https://ci.appveyor.com/api/projects/status/3mw2fifalmjlqf56/branch/main?svg=true)](https://ci.appveyor.com/project/import-js/eslint-plugin-import/branch/main)
[![npm](https://img.shields.io/npm/v/eslint-plugin-import.svg)](https://www.npmjs.com/package/eslint-plugin-import)
[![npm downloads](https://img.shields.io/npm/dt/eslint-plugin-import.svg?maxAge=2592000)](http://www.npmtrends.com/eslint-plugin-import)
[![npm downloads](https://img.shields.io/npm/dt/eslint-plugin-import.svg?maxAge=2592000)](https://www.npmtrends.com/eslint-plugin-import)

This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, and prevent issues with misspelling of file paths and import names. All the goodness that the ES2015+ static module syntax intends to provide, marked up in your editor.

@@ -171,7 +172,10 @@ extends:
- plugin:import/typescript # this line does the trick
```
[`@typescript-eslint/parser`]: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser
[`@typescript-eslint/parser`]: https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser

You will also need to install and configure the TypeScript resolver:
[`eslint-import-resolver-typescript`](https://github.com/alexgorbatchev/eslint-import-resolver-typescript).

# Resolvers

@@ -190,7 +194,7 @@ runtime (allowing some modules to be included more traditionally via script tags
In the interest of supporting both of these, v0.11 introduces resolvers.

Currently [Node] and [webpack] resolution have been implemented, but the
resolvers are just npm packages, so [third party packages are supported](https://github.com/benmosher/eslint-plugin-import/wiki/Resolvers) (and encouraged!).
resolvers are just npm packages, so [third party packages are supported](https://github.com/import-js/eslint-plugin-import/wiki/Resolvers) (and encouraged!).

You can reference resolvers in several ways (in order of precedence):

@@ -252,7 +256,7 @@ the process's current working directory if no `package.json` is found.
If you are interesting in writing a resolver, see the [spec](./resolvers/README.md) for more details.

[`resolve`]: https://www.npmjs.com/package/resolve
[`externals`]: http://webpack.github.io/docs/library-and-externals.html
[`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
@@ -348,6 +352,8 @@ An array of folders. Resolved modules only from those folders will be considered

This option is also useful in a monorepo setup: list here all directories that contain monorepo's packages and they will be treated as external ones no matter which resolver is used.

If you are using `yarn` PnP as your package manager, add the `.yarn` folder and all your installed dependencies will be considered as `external`, instead of `internal`.

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`.
@@ -477,7 +483,7 @@ The purpose of the `chdir` setting, in this case, is to set the working director
from which ESLint is executed to be the same as the directory on which SublimeLinter-eslint
bases the relative path it provides.

See the SublimeLinter docs on [`chdir`](http://www.sublimelinter.com/en/latest/linter_settings.html#chdir)
See the SublimeLinter docs on [`chdir`](https://www.sublimelinter.com/en/latest/linter_settings.html#chdir)
for more information, in case this does not work with your project.

If you are not using `.eslintignore`, or don't have a Sublime project file, you can also
@@ -508,3 +514,8 @@ In Package Settings / SublimeLinter / User Settings:

I believe this defaults to `3`, so you may not need to alter it depending on your
project folder max depth.

[codecov-image]: https://codecov.io/gh/import-js/eslint-plugin-import/branch/main/graphs/badge.svg
[codecov-url]: https://app.codecov.io/gh/import-js/eslint-plugin-import/
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/import-js/eslint-plugin-import
[actions-url]: https://github.com/import-js/eslint-plugin-import
16 changes: 8 additions & 8 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Release steps

1. create a `release-[x.y.z]` branch from tip of `master` (or whatever release commit)
1. create a `release-[x.y.z]` branch from tip of `main` (or whatever release commit)

```bash
git checkout master && git pull && git checkout -b release-2.1.0
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)
@@ -13,16 +13,16 @@
at last version's tag.

```markdown
[Unreleased]: https://github.com/benmosher/eslint-plugin-import/compare/v2.0.1...HEAD
[2.0.1]: https://github.com/benmosher/eslint-plugin-import/compare/v2.0.0...v2.0.1
[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/benmosher/eslint-plugin-import/compare/v2.1.0...HEAD
[2.1.0]: https://github.com/benmosher/eslint-plugin-import/compare/v2.0.1...v2.1.0
[2.0.1]: https://github.com/benmosher/eslint-plugin-import/compare/v2.0.0...v2.0.1
[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
@@ -49,6 +49,6 @@
7. merge `release-[x.y.z]` into `release` (
- ideally fast-forward, probably with Git CLI instead of Github

8. merge `release` into `master`
8. merge `release` into `main`

Done!
52 changes: 38 additions & 14 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -5,28 +5,31 @@ configuration:
# 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"
# - nodejs_version: "6"
# - nodejs_version: "4"

image: Visual Studio 2019
matrix:
fast_finish: false
exclude:
- configuration: WSL
nodejs_version: "10"
- configuration: WSL
nodejs_version: "8"
- configuration: WSL
nodejs_version: "6"
- configuration: WSL
nodejs_version: "4"

# allow_failures:
# - nodejs_version: "4" # for eslint 5
allow_failures:
- nodejs_version: "4" # for eslint 5

# platform:
# - x86
# - x64
platform:
- x86
- x64

# Initialization scripts. (runs before repo cloning)
init:
@@ -35,14 +38,26 @@ init:
if ($env:nodejs_version -eq "4") {
$env:NPM_VERSION="3"
}
if ($env:nodejs_version -in @("8", "10", "12")) {
$env:NPM_VERSION="6.14.5"
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")"
@@ -54,6 +69,8 @@ before_build:

# 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
@@ -99,9 +116,6 @@ test_script:
$env:RESOLVERS = [string]::Join(";", $resolvers);
- FOR %%G in ("%RESOLVERS:;=";"%") do ( pushd %%~G & ci npm test & popd )

on_success:
- ci npm run coveralls

# Configuration-specific steps
for:
- matrix:
@@ -127,6 +141,12 @@ for:
}
$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
@@ -136,5 +156,9 @@ for:
- 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
7 changes: 5 additions & 2 deletions config/typescript.js
Original file line number Diff line number Diff line change
@@ -2,15 +2,18 @@
* Adds `.jsx`, `.ts` and `.tsx` as an extension, and enables JSX/TSX parsing.
*/

const allExtensions = ['.ts', '.tsx', '.d.ts', '.js', '.jsx'];
// 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 allExtensions = ['.ts', '.tsx', '.js', '.jsx'];

module.exports = {

settings: {
'import/extensions': allExtensions,
'import/external-module-folders': ['node_modules', 'node_modules/@types'],
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx', '.d.ts'],
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
'node': {
8 changes: 5 additions & 3 deletions docs/rules/extensions.md
Original file line number Diff line number Diff line change
@@ -110,7 +110,7 @@ import bar from './bar';

import Component from './Component';

import express from 'express';
import foo from '@/foo';
```

The following patterns are not considered problems when configuration set to "always":
@@ -122,9 +122,9 @@ import bar from './bar.json';

import Component from './Component.jsx';

import express from 'express/index.js';

import * as path from 'path';

import foo from '@/foo.js';
```

The following patterns are considered problems when configuration set to "ignorePackages":
@@ -149,6 +149,7 @@ import Component from './Component.jsx';

import express from 'express';

import foo from '@/foo'
```

The following patterns are not considered problems when configuration set to `['error', 'always', {ignorePackages: true} ]`:
@@ -160,6 +161,7 @@ import baz from 'foo/baz.js';

import express from 'express';

import foo from '@/foo';
```

## When Not To Use It
4 changes: 2 additions & 2 deletions docs/rules/first.md
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@ A directive in this case is assumed to be a single statement that contains only
a literal string-valued expression.

`'use strict'` would be a good example, except that [modules are always in strict
mode](http://www.ecma-international.org/ecma-262/6.0/#sec-strict-mode-code) so it would be surprising to see a `'use strict'` sharing a file with `import`s and
mode](https://262.ecma-international.org/6.0/#sec-strict-mode-code) so it would be surprising to see a `'use strict'` sharing a file with `import`s and
`export`s.

Given that, see [#255] for the reasoning.
@@ -67,4 +67,4 @@ enable this rule.
- Issue [#255]

[`import/order`]: ./order.md
[#255]: https://github.com/benmosher/eslint-plugin-import/issues/255
[#255]: https://github.com/import-js/eslint-plugin-import/issues/255
2 changes: 1 addition & 1 deletion docs/rules/imports-first.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# imports-first

This rule was **deprecated** in eslint-plugin-import v2.0.0. Please use the corresponding rule [`first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/first.md).
This rule was **deprecated** in eslint-plugin-import v2.0.0. Please use the corresponding rule [`first`](https://github.com/import-js/eslint-plugin-import/blob/HEAD/docs/rules/first.md).
38 changes: 30 additions & 8 deletions docs/rules/max-dependencies.md
Original file line number Diff line number Diff line change
@@ -6,20 +6,20 @@ This is a useful rule because a module with too many dependencies is a code smel

Importing multiple named exports from a single module will only count once (e.g. `import {x, y, z} from './foo'` will only count as a single dependency).

### Options
## Options

This rule takes the following option:

`max`: The maximum number of dependencies allowed. Anything over will trigger the rule. **Default is 10** if the rule is enabled and no `max` is specified.

You can set the option like this:
This rule has the following options, with these defaults:

```js
"import/max-dependencies": ["error", {"max": 10}]
"import/max-dependencies": ["error", {
"max": 10,
"ignoreTypeImports": false,
}]
```

### `max`

## Example
This option sets the maximum number of dependencies allowed. Anything over will trigger the rule. **Default is 10** if the rule is enabled and no `max` is specified.

Given a max value of `{"max": 2}`:

@@ -39,6 +39,28 @@ const anotherA = require('./a'); // still 1
import {x, y, z} from './foo'; // 2
```

### `ignoreTypeImports`

Ignores `type` imports. Type imports are a feature released in TypeScript 3.8, you can [read more here](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export). Defaults to `false`.

Given `{"max": 2, "ignoreTypeImports": true}`:

### Fail

```ts
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
```

## When Not To Use It

If you don't care how many dependencies a module has.
8 changes: 5 additions & 3 deletions docs/rules/named.md
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ Verifies that all named imports are part of the set of named exports in the refe
For `export`, verifies that all named exports exist in the referenced module.

Note: for packages, the plugin will find exported names
from [`jsnext:main`], if present in `package.json`.
from [`jsnext:main`] (deprecated) or `module`, if present in `package.json`.
Redux's npm module includes this key, and thereby is lintable, for example.

A module path that is [ignored] or not [unambiguously an ES module] will not be reported when imported. Note that type imports and exports, as used by [Flow], are always ignored.
@@ -91,8 +91,10 @@ runtime, you will likely see false positives with this rule.
## Further Reading

- [`import/ignore`] setting
- [`jsnext:main`] (Rollup)
- [`jsnext:main`] deprecation
- [`pkg.module`] (Rollup)


[`jsnext:main`]: https://github.com/rollup/rollup/wiki/jsnext:main
[`jsnext:main`]: https://github.com/jsforum/jsforum/issues/5
[`pkg.module`]: https://github.com/rollup/rollup/wiki/pkg.module
[`import/ignore`]: ../../README.md#importignore
2 changes: 1 addition & 1 deletion docs/rules/no-cycle.md
Original file line number Diff line number Diff line change
@@ -83,7 +83,7 @@ this rule enabled.

## Further Reading

- [Original inspiring issue](https://github.com/benmosher/eslint-plugin-import/issues/941)
- [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

4 changes: 2 additions & 2 deletions docs/rules/no-duplicates.md
Original file line number Diff line number Diff line change
@@ -3,11 +3,11 @@
Reports if a resolved path is imported more than once.
+(fixable) The `--fix` option on the [command line] automatically fixes some problems reported by this rule.

ESLint core has a similar rule ([`no-duplicate-imports`](http://eslint.org/docs/rules/no-duplicate-imports)), but this version
ESLint core has a similar rule ([`no-duplicate-imports`](https://eslint.org/docs/rules/no-duplicate-imports)), but this version
is different in two key ways:

1. the paths in the source code don't have to exactly match, they just have to point to the same module on the filesystem. (i.e. `./foo` and `./foo.js`)
2. this version distinguishes Flow `type` imports from standard imports. ([#334](https://github.com/benmosher/eslint-plugin-import/pull/334))
2. this version distinguishes Flow `type` imports from standard imports. ([#334](https://github.com/import-js/eslint-plugin-import/pull/334))

## Rule Details

4 changes: 2 additions & 2 deletions docs/rules/no-mutable-exports.md
Original file line number Diff line number Diff line change
@@ -44,8 +44,8 @@ want to enable the following core ESLint rules:
- [no-func-assign]
- [no-class-assign]

[no-func-assign]: http://eslint.org/docs/rules/no-func-assign
[no-class-assign]: http://eslint.org/docs/rules/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

## When Not To Use It

11 changes: 11 additions & 0 deletions docs/rules/no-namespace.md
Original file line number Diff line number Diff line change
@@ -5,6 +5,12 @@ Enforce a convention of not using namespace (a.k.a. "wildcard" `*`) imports.
+(fixable) The `--fix` option on the [command line] automatically fixes problems reported by this rule, provided that the namespace object is only used for direct member access, e.g. `namespace.a`.
The `--fix` functionality for this rule requires ESLint 5 or newer.

### Options

This rule supports the following options:

- `ignore`: array of glob strings for modules that should be ignored by the rule.

## Rule Details

Valid:
@@ -15,6 +21,11 @@ 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';
```

Invalid:

```js
50 changes: 48 additions & 2 deletions docs/rules/no-restricted-paths.md
Original file line number Diff line number Diff line change
@@ -9,8 +9,18 @@ In order to prevent such scenarios this rule allows you to define restricted zon

This rule has one option. The option is an object containing the definition of all restricted `zones` and the optional `basePath` which is used to resolve relative paths within.
The default value for `basePath` is the current working directory.
Each zone consists of the `target` path and a `from` path. The `target` is the path where the restricted imports should be applied. The `from` path defines the folder that is not allowed to be used in an import. An optional `except` may be defined for a zone, allowing exception paths that would otherwise violate the related `from`. Note that `except` is relative to `from` and cannot backtrack to a parent directory.
You may also specify an optional `message` for a zone, which will be displayed in case of the rule violation.

Each zone consists of the `target` path, a `from` path, and an optional `except` and `message` attribute.
- `target` is the path 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
- `from` path defines the folder that is 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
- `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` is a glob pattern, `except` must be an array of glob patterns as well
- in case `from` is a 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

@@ -77,4 +87,40 @@ The following pattern is not considered a problem:

```js
import b from './b'

```

---------------

Given the following folder structure:

```
my-project
├── client
└── foo.js
└── sub-module
└── bar.js
└── baz.js
```

and the current configuration is set to:

```
{ "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`:

```js
import a from './sub-module/baz'
```

The following import is not considered a problem in `my-project/client/sub-module/bar.js`:

```js
import b from './baz'
```
21 changes: 18 additions & 3 deletions docs/rules/no-unresolved.md
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ as defined by standard Node `require.resolve` behavior.
See [settings](../../README.md#settings) for customization options for the resolution (i.e.
additional filetypes, `NODE_PATH`, etc.)

This rule can also optionally report on unresolved modules in CommonJS `require('./foo')` calls and AMD `require(['./foo'], function (foo){...})` and `define(['./foo'], function (foo){...})`.
This rule can also optionally report on unresolved modules in CommonJS `require('./foo')` calls and AMD `require(['./foo'], function (foo) {...})` and `define(['./foo'], function (foo) {...})`.

To enable this, send `{ commonjs: true/false, amd: true/false }` as a rule option.
Both are disabled by default.
@@ -76,10 +76,25 @@ By default, this rule will report paths whose case do not match the underlying f
const { default: x } = require('./foo') // reported if './foo' is actually './Foo' and caseSensitive: true
```

#### `caseSensitiveStrict`

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 }]*/

// Absolute paths
import Foo from `/Users/fOo/bar/file.js` // reported, /Users/foo/bar/file.js
import Foo from `d:/fOo/bar/file.js` // reported, d:/foo/bar/file.js

// Relative paths, cwd is Users/foo/
import Foo from `./../fOo/bar/file.js` // reported
```

## When Not To Use It

If you're using a module bundler other than Node or Webpack, you may end up with
a lot of false positive reports of missing dependencies.
If you're using a module bundler other than Node or Webpack, you may end up with a lot of false positive reports of missing dependencies.

## Further Reading

4 changes: 2 additions & 2 deletions docs/rules/no-unused-modules.md
Original file line number Diff line number Diff line change
@@ -3,14 +3,14 @@
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

Note: dynamic imports are currently not supported.

## Rule Details

### Usage

In order for this plugin to work, one of the options `missingExports` or `unusedExports` must be enabled (see "Options" section below). In the future, these options will be enabled by default (see https://github.com/benmosher/eslint-plugin-import/issues/1324)
In order for this plugin to work, one of the options `missingExports` or `unusedExports` must be enabled (see "Options" section below). In the future, these options will be enabled by default (see https://github.com/import-js/eslint-plugin-import/issues/1324)

Example:
```
2 changes: 1 addition & 1 deletion docs/rules/no-useless-path-segments.md
Original file line number Diff line number Diff line change
@@ -72,7 +72,7 @@ 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/benmosher/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/extensions](https://github.com/import-js/eslint-plugin-import#importextensions) for details.

### commonjs

23 changes: 22 additions & 1 deletion docs/rules/order.md
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
Enforce a convention in the order of `require()` / `import` statements.
+(fixable) The `--fix` option on the [command line] automatically fixes problems reported by this rule.

With the [`groups`](#groups-array) option set to `["builtin", "external", "internal", "parent", "sibling", "index", "object"]` the order is as shown in the following example:
With the [`groups`](#groups-array) option set to `["builtin", "external", "internal", "parent", "sibling", "index", "object", "type"]` the order is as shown in the following example:

```js
// 1. node "builtin" modules
@@ -148,6 +148,27 @@ Example:
}]
}
```

You can also use `patterns`(e.g., `react`, `react-router-dom`, etc).

Example:
```json
{
"import/order": [
"error",
{
"pathGroups": [
{
"pattern": "react",
"group": "builtin",
"position": "before"
}
],
"pathGroupsExcludedImportTypes": ["react"]
}
]
}
```
The default value is `["builtin", "external"]`.

### `newlines-between: [ignore|always|always-and-inside-groups|never]`:
4 changes: 2 additions & 2 deletions docs/rules/unambiguous.md
Original file line number Diff line number Diff line change
@@ -50,5 +50,5 @@ a `module`.
- [`parserOptions.sourceType`]
- [node-eps#13](https://github.com/nodejs/node-eps/issues/13)

[`parserOptions.sourceType`]: http://eslint.org/docs/user-guide/configuring#specifying-parser-options
[Unambiguous JavaScript Grammar]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md#32-determining-if-source-is-an-es-module
[`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
6 changes: 3 additions & 3 deletions memo-parser/package.json
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/benmosher/eslint-plugin-import.git"
"url": "git+https://github.com/import-js/eslint-plugin-import.git"
},
"keywords": [
"eslint",
@@ -22,9 +22,9 @@
"author": "Ben Mosher (me@benmosher.com)",
"license": "MIT",
"bugs": {
"url": "https://github.com/benmosher/eslint-plugin-import/issues"
"url": "https://github.com/import-js/eslint-plugin-import/issues"
},
"homepage": "https://github.com/benmosher/eslint-plugin-import#readme",
"homepage": "https://github.com/import-js/eslint-plugin-import#readme",
"peerDependencies": {
"eslint": ">=3.5.0"
},
45 changes: 21 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eslint-plugin-import",
"version": "2.23.3",
"version": "2.25.3",
"description": "Import with sanity.",
"engines": {
"node": ">=4"
@@ -25,18 +25,17 @@
"watch": "npm run tests-only -- -- --watch",
"pretest": "linklocal",
"posttest": "eslint .",
"mocha": "cross-env BABEL_ENV=test nyc -s mocha",
"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",
"coveralls": "nyc report --reporter lcovonly && coveralls < ./coverage/lcov.info"
"prepublish": "not-in-publish || npm run prepublishOnly"
},
"repository": {
"type": "git",
"url": "https://github.com/benmosher/eslint-plugin-import"
"url": "https://github.com/import-js/eslint-plugin-import"
},
"keywords": [
"eslint",
@@ -50,27 +49,27 @@
"author": "Ben Mosher <me@benmosher.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/benmosher/eslint-plugin-import/issues"
"url": "https://github.com/import-js/eslint-plugin-import/issues"
},
"homepage": "https://github.com/benmosher/eslint-plugin-import",
"homepage": "https://github.com/import-js/eslint-plugin-import",
"devDependencies": {
"@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",
"array.prototype.flatmap": "^1.2.4",
"@typescript-eslint/parser": "^2.23.0 || ^3.3.0 || ^4.29.3",
"array.prototype.flatmap": "^1.2.5",
"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-es2015-argon": "latest",
"babel-preset-airbnb": "^2.6.0",
"babel-preset-flow": "^6.23.0",
"babel-register": "^6.26.0",
"babylon": "^6.18.0",
"chai": "^4.3.4",
"coveralls": "^3.1.0",
"cross-env": "^4.0.0",
"eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0",
"escope": "^3.6.0",
"eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8",
"eslint-import-resolver-node": "file:./resolvers/node",
"eslint-import-resolver-typescript": "^1.0.2 || ^1.1.1",
"eslint-import-resolver-webpack": "file:./resolvers/webpack",
@@ -80,7 +79,7 @@
"eslint-plugin-import": "2.x",
"eslint-plugin-json": "^2.1.2",
"fs-copy-file-sync": "^1.1.1",
"glob": "^7.1.7",
"glob": "^7.2.0",
"in-publish": "^2.0.1",
"linklocal": "^2.8.2",
"lodash.isarray": "^4.0.0",
@@ -96,23 +95,21 @@
"typescript-eslint-parser": "^15 || ^22.0.0"
},
"peerDependencies": {
"eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0"
"eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
},
"dependencies": {
"array-includes": "^3.1.3",
"array.prototype.flat": "^1.2.4",
"array-includes": "^3.1.4",
"array.prototype.flat": "^1.2.5",
"debug": "^2.6.9",
"doctrine": "^2.1.0",
"eslint-import-resolver-node": "^0.3.4",
"eslint-module-utils": "^2.6.1",
"find-up": "^2.0.0",
"eslint-import-resolver-node": "^0.3.6",
"eslint-module-utils": "^2.7.1",
"has": "^1.0.3",
"is-core-module": "^2.4.0",
"is-core-module": "^2.8.0",
"is-glob": "^4.0.3",
"minimatch": "^3.0.4",
"object.values": "^1.1.3",
"pkg-up": "^2.0.0",
"read-pkg-up": "^3.0.0",
"object.values": "^1.1.5",
"resolve": "^1.20.0",
"tsconfig-paths": "^3.9.0"
"tsconfig-paths": "^3.11.0"
}
}
3 changes: 0 additions & 3 deletions resolvers/.eslintrc.yml

This file was deleted.

28 changes: 19 additions & 9 deletions resolvers/node/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
# Change Log
All notable changes to this resolver will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com).
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.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])
@@ -48,16 +56,18 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
### Fixed
- find files with `.json` extensions (#333, thanks for noticing @jfmengels)

[#438]: https://github.com/benmosher/eslint-plugin-import/pull/438

[#1663]: https://github.com/benmosher/eslint-plugin-import/issues/1663
[#1595]: https://github.com/benmosher/eslint-plugin-import/pull/1595
[#939]: https://github.com/benmosher/eslint-plugin-import/issues/939
[#531]: https://github.com/benmosher/eslint-plugin-import/issues/531
[#437]: https://github.com/benmosher/eslint-plugin-import/issues/437
[#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
[@lukeapage]: https://github.com/lukeapage
[@SkeLLLa]: https://github.com/SkeLLLa
[@ljharb]: https://github.com/ljharb
[@opichals]: https://github.com/opichals
[@MustafaHaddara]: https://github.com/MustafaHaddara
33 changes: 26 additions & 7 deletions resolvers/node/index.js
Original file line number Diff line number Diff line change
@@ -17,7 +17,8 @@ exports.resolve = function (source, file, config) {
}

try {
resolvedPath = resolve.sync(source, opts(file, config));
const cachedFilter = function (pkg, dir) { return packageFilter(pkg, dir, config); };
resolvedPath = resolve.sync(source, opts(file, config, cachedFilter));
log('Resolved to:', resolvedPath);
return { found: true, path: resolvedPath };
} catch (err) {
@@ -26,7 +27,7 @@ exports.resolve = function (source, file, config) {
}
};

function opts(file, config) {
function opts(file, config, packageFilter) {
return Object.assign({
// more closely matches Node (#333)
// plus 'mjs' for native modules! (#939)
@@ -36,14 +37,32 @@ function opts(file, config) {
{
// path.resolve will handle paths relative to CWD
basedir: path.dirname(path.resolve(file)),
packageFilter: packageFilter,

packageFilter,
});
}

function packageFilter(pkg) {
if (pkg['jsnext:main']) {
pkg['main'] = pkg['jsnext:main'];
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.sync(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.sync(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;
}
12 changes: 5 additions & 7 deletions resolvers/node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eslint-import-resolver-node",
"version": "0.3.4",
"version": "0.3.6",
"description": "Node default behavior import resolution plugin for eslint-plugin-import.",
"main": "index.js",
"files": [
@@ -9,12 +9,11 @@
"scripts": {
"prepublishOnly": "cp ../../{LICENSE,.npmrc} ./",
"tests-only": "nyc mocha",
"test": "npm run tests-only",
"coveralls": "nyc report --reporter lcovonly && cd ../.. && coveralls < ./resolvers/node/coverage/lcov.info"
"test": "npm run tests-only"
},
"repository": {
"type": "git",
"url": "https://github.com/benmosher/eslint-plugin-import"
"url": "https://github.com/import-js/eslint-plugin-import"
},
"keywords": [
"eslint",
@@ -26,16 +25,15 @@
"author": "Ben Mosher (me@benmosher.com)",
"license": "MIT",
"bugs": {
"url": "https://github.com/benmosher/eslint-plugin-import/issues"
"url": "https://github.com/import-js/eslint-plugin-import/issues"
},
"homepage": "https://github.com/benmosher/eslint-plugin-import",
"homepage": "https://github.com/import-js/eslint-plugin-import",
"dependencies": {
"debug": "^3.2.7",
"resolve": "^1.20.0"
},
"devDependencies": {
"chai": "^3.5.0",
"coveralls": "^3.1.0",
"mocha": "^3.5.3",
"nyc": "^11.9.0"
}
4 changes: 4 additions & 0 deletions resolvers/node/test/package-mains/jsnext/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"main": "lib/index.js",
"jsnext:main": "src/index.js"
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"main": "lib/index.js",
"module": "src/index.js",
"jsnext:main": "lib/index.js"
}
Empty file.
1 change: 1 addition & 0 deletions resolvers/node/test/package-mains/module-broken/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {};
4 changes: 4 additions & 0 deletions resolvers/node/test/package-mains/module-broken/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"main": "./main.js",
"module": "./doesNotExist.js"
}
4 changes: 4 additions & 0 deletions resolvers/node/test/package-mains/module/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"main": "lib/index.js",
"module": "src/index.js"
}
Empty file.
1 change: 1 addition & 0 deletions resolvers/node/test/package-mains/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "dummy": true }
32 changes: 32 additions & 0 deletions resolvers/node/test/packageMains.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'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'));
});
});
82 changes: 44 additions & 38 deletions resolvers/webpack/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
# Change Log
All notable changes to this resolver will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com).
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.2 - 2021-10-20

### Changed
- [meta] expand `engines.node` to include node 17 ([#2268], thanks [@ljharb])

## 0.13.1 - 2021-05-13

### Added
@@ -150,41 +155,42 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
- `interpret` configs (such as `.babel.js`).
Thanks to [@gausie] for the initial PR ([#164], ages ago! 😅) and [@jquense] for tests ([#278]).

[#2023]: https://github.com/benmosher/eslint-plugin-import/pull/2023
[#1967]: https://github.com/benmosher/eslint-plugin-import/pull/1967
[#1962]: https://github.com/benmosher/eslint-plugin-import/pull/1962
[#1705]: https://github.com/benmosher/eslint-plugin-import/pull/1705
[#1595]: https://github.com/benmosher/eslint-plugin-import/pull/1595
[#1503]: https://github.com/benmosher/eslint-plugin-import/pull/1503
[#1297]: https://github.com/benmosher/eslint-plugin-import/pull/1297
[#1261]: https://github.com/benmosher/eslint-plugin-import/pull/1261
[#1220]: https://github.com/benmosher/eslint-plugin-import/pull/1220
[#1091]: https://github.com/benmosher/eslint-plugin-import/pull/1091
[#969]: https://github.com/benmosher/eslint-plugin-import/pull/969
[#968]: https://github.com/benmosher/eslint-plugin-import/pull/968
[#768]: https://github.com/benmosher/eslint-plugin-import/pull/768
[#683]: https://github.com/benmosher/eslint-plugin-import/pull/683
[#572]: https://github.com/benmosher/eslint-plugin-import/pull/572
[#569]: https://github.com/benmosher/eslint-plugin-import/pull/569
[#533]: https://github.com/benmosher/eslint-plugin-import/pull/533
[#413]: https://github.com/benmosher/eslint-plugin-import/pull/413
[#377]: https://github.com/benmosher/eslint-plugin-import/pull/377
[#363]: https://github.com/benmosher/eslint-plugin-import/pull/363
[#289]: https://github.com/benmosher/eslint-plugin-import/pull/289
[#287]: https://github.com/benmosher/eslint-plugin-import/pull/287
[#278]: https://github.com/benmosher/eslint-plugin-import/pull/278
[#181]: https://github.com/benmosher/eslint-plugin-import/pull/181
[#164]: https://github.com/benmosher/eslint-plugin-import/pull/164

[#1219]: https://github.com/benmosher/eslint-plugin-import/issues/1219
[#788]: https://github.com/benmosher/eslint-plugin-import/issues/788
[#767]: https://github.com/benmosher/eslint-plugin-import/issues/767
[#681]: https://github.com/benmosher/eslint-plugin-import/issues/681
[#435]: https://github.com/benmosher/eslint-plugin-import/issues/435
[#411]: https://github.com/benmosher/eslint-plugin-import/issues/411
[#357]: https://github.com/benmosher/eslint-plugin-import/issues/357
[#286]: https://github.com/benmosher/eslint-plugin-import/issues/286
[#283]: https://github.com/benmosher/eslint-plugin-import/issues/283
[#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

[#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

[@gausie]: https://github.com/gausie
[@jquense]: https://github.com/jquense
@@ -211,4 +217,4 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
[@opichals]: https://github.com/opichals
[@andersk]: https://github.com/andersk
[@ogonkov]: https://github.com/ogonkov
[@jet2jet]: https://github.com/jet2jet
[@jet2jet]: https://github.com/jet2jet
41 changes: 20 additions & 21 deletions resolvers/webpack/index.js
Original file line number Diff line number Diff line change
@@ -84,7 +84,7 @@ exports.resolve = function (source, file, settings) {
if (configPath) {
try {
webpackConfig = require(configPath);
} catch(e) {
} catch (e) {
console.log('Error resolving webpackConfig', e);
throw e;
}
@@ -118,8 +118,7 @@ exports.resolve = function (source, file, settings) {

if (typeof configIndex !== 'undefined' && webpackConfig.length > configIndex) {
webpackConfig = webpackConfig[configIndex];
}
else {
} else {
webpackConfig = find(webpackConfig, function findFirstWithResolve(config) {
return !!config.resolve;
});
@@ -164,7 +163,7 @@ exports.resolve = function (source, file, settings) {
const MAX_CACHE = 10;
const _cache = [];
function getResolveSync(configPath, webpackConfig, cwd) {
const cacheKey = { configPath: configPath, webpackConfig: webpackConfig };
const cacheKey = { configPath, webpackConfig };
let cached = find(_cache, function (entry) { return isEqual(entry.key, cacheKey); });
if (!cached) {
cached = {
@@ -237,7 +236,7 @@ function createWebpack2ResolveSync(webpackRequire, resolveConfig) {
}

/**
* webpack 1 defaults: http://webpack.github.io/docs/configuration.html#resolve-packagemains
* webpack 1 defaults: https://webpack.github.io/docs/configuration.html#resolve-packagemains
* @type {Array}
*/
const webpack1DefaultMains = [
@@ -266,23 +265,23 @@ function createWebpack1ResolveSync(webpackRequire, resolveConfig, plugins) {
resolver.apply(
resolveConfig.packageAlias
? new DirectoryDescriptionFileFieldAliasPlugin('package.json', resolveConfig.packageAlias)
: function() {},
: function () {},
new ModuleAliasPlugin(resolveConfig.alias || {}),
makeRootPlugin(ModulesInRootPlugin, 'module', resolveConfig.root),
new ModulesInDirectoriesPlugin(
'module',
resolveConfig.modulesDirectories || resolveConfig.modules || ['web_modules', 'node_modules']
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)
['module', 'jsnext:main'].concat(resolveConfig.packageMains || webpack1DefaultMains),
),
new DirectoryDefaultFilePlugin(['index']),
new FileAppendPlugin(resolveConfig.extensions || ['', '.webpack.js', '.web.js', '.js']),
new ResultSymlinkPlugin()
new ResultSymlinkPlugin(),
);


@@ -303,24 +302,24 @@ function createWebpack1ResolveSync(webpackRequire, resolveConfig, plugins) {

resolver.apply.apply(resolver, resolvePlugins);

return function() {
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")
if (typeof root === 'string') {
return new ModulesInRootPlugin(name, root);
else if(Array.isArray(root)) {
} else if (Array.isArray(root)) {
return function() {
root.forEach(function(root) {
root.forEach(function (root) {
this.apply(new ModulesInRootPlugin(name, root));
}, this);
};
}
return function() {};
return function () {};
}
/* eslint-enable */

@@ -392,7 +391,7 @@ function findExternal(source, externals, context, resolveSync) {
}

function findConfigPath(configPath, packageDir) {
const extensions = Object.keys(interpret.extensions).sort(function(a, b) {
const extensions = Object.keys(interpret.extensions).sort(function (a, b) {
return a === '.js' ? -1 : b === '.js' ? 1 : a.length - b.length;
});
let extension;
@@ -422,7 +421,7 @@ function findConfigPath(configPath, packageDir) {
}

const maybePath = path.resolve(
path.join(packageDir, 'webpack.config' + maybeExtension)
path.join(packageDir, 'webpack.config' + maybeExtension),
);
if (fs.existsSync(maybePath)) {
configPath = maybePath;
@@ -436,17 +435,17 @@ function findConfigPath(configPath, packageDir) {
}

function registerCompiler(moduleDescriptor) {
if(moduleDescriptor) {
if(typeof moduleDescriptor === 'string') {
if (moduleDescriptor) {
if (typeof moduleDescriptor === 'string') {
require(moduleDescriptor);
} else if(!Array.isArray(moduleDescriptor)) {
} else if (!Array.isArray(moduleDescriptor)) {
moduleDescriptor.register(require(moduleDescriptor.module));
} else {
for(let i = 0; i < moduleDescriptor.length; i++) {
for (let i = 0; i < moduleDescriptor.length; i++) {
try {
registerCompiler(moduleDescriptor[i]);
break;
} catch(e) {
} catch (e) {
log('Failed to register compiler for moduleDescriptor[]:', i, moduleDescriptor);
}
}
20 changes: 8 additions & 12 deletions resolvers/webpack/package.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
{
"name": "eslint-import-resolver-webpack",
"version": "0.13.1",
"version": "0.13.2",
"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",
"report": "nyc report --reporter=html",
"coveralls": "nyc report --reporter lcovonly && cd ../.. && coveralls < ./resolvers/webpack/coverage/lcov.info"
"test": "npm run tests-only"
},
"files": [
"index.js",
"config.js"
],
"repository": {
"type": "git",
"url": "git+https://github.com/benmosher/eslint-plugin-import.git"
"url": "git+https://github.com/import-js/eslint-plugin-import.git"
},
"keywords": [
"eslint-plugin-import",
@@ -28,18 +26,18 @@
"author": "Ben Mosher (me@benmosher.com)",
"license": "MIT",
"bugs": {
"url": "https://github.com/benmosher/eslint-plugin-import/issues"
"url": "https://github.com/import-js/eslint-plugin-import/issues"
},
"homepage": "https://github.com/benmosher/eslint-plugin-import/tree/master/resolvers/webpack",
"homepage": "https://github.com/import-js/eslint-plugin-import/tree/HEAD/resolvers/webpack",
"dependencies": {
"array-find": "^1.0.0",
"debug": "^3.2.7",
"enhanced-resolve": "^0.9.1",
"find-root": "^1.1.0",
"has": "^1.0.3",
"interpret": "^1.4.0",
"is-core-module": "^2.4.0",
"is-regex": "^1.1.3",
"is-core-module": "^2.7.0",
"is-regex": "^1.1.4",
"lodash": "^4.17.21",
"resolve": "^1.20.0",
"semver": "^5.7.1"
@@ -50,15 +48,13 @@
},
"devDependencies": {
"babel-plugin-istanbul": "^4.1.6",
"babel-preset-es2015-argon": "latest",
"babel-register": "^6.26.0",
"chai": "^3.5.0",
"coveralls": "^3.1.0",
"mocha": "^3.5.3",
"nyc": "^11.9.0",
"webpack": "https://gist.github.com/ljharb/9cdb687f3806f8e6cb8a365d0b7840eb"
},
"engines": {
"node": "^16 || ^15 || ^14 || ^13 || ^12 || ^11 || ^10 || ^9 || ^8 || ^7 || ^6"
"node": ">= 6"
}
}
4 changes: 2 additions & 2 deletions resolvers/webpack/test/alias.js
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ describe('resolve.alias', function () {

// todo: reimplement with resolver function / config
// describe.skip('webpack alias spec', function () {
// // from table: http://webpack.github.io/docs/configuration.html#resolve-alias
// // 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 () {
@@ -82,7 +82,7 @@ describe('resolve.alias', function () {
// });

// describe.skip('nested module names', function () {
// // from table: http://webpack.github.io/docs/configuration.html#resolve-alias
// // 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 () {
10 changes: 5 additions & 5 deletions resolvers/webpack/test/config.js
Original file line number Diff line number Diff line change
@@ -101,7 +101,7 @@ describe('config', function () {
.and.equal(path.join(__dirname, 'files', 'some', 'goofy', 'path', 'foo.js'));
});

it('finds the config at option env when config is a function', function() {
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: {
@@ -113,7 +113,7 @@ describe('config', function () {
.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() {
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: {
@@ -125,7 +125,7 @@ describe('config', function () {
.and.equal(path.join(__dirname, 'files', 'some', 'goofy', 'path', 'bar.js'));
});

it('passes argv to config when it is a function', function() {
it('passes argv to config when it is a function', function () {
const settings = {
config: require(path.join(__dirname, './files/webpack.function.config.js')),
argv: {
@@ -137,7 +137,7 @@ describe('config', function () {
.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() {
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,
@@ -146,7 +146,7 @@ describe('config', function () {
expect(function () { resolve('baz', file, settings); }).to.not.throw(Error);
});

it('prevents async config using', function() {
it('prevents async config using', function () {
const settings = {
config: require(path.join(__dirname, './files/webpack.config.async.js')),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"main": "lib/index.js",
"module": "src/index.js",
"jsnext:main": "lib/index.js"
}
Empty file.
1 change: 1 addition & 0 deletions resolvers/webpack/test/package-mains/module-broken/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"main": "./main.js",
"module": "./doesNotExist.js"
}
27 changes: 18 additions & 9 deletions resolvers/webpack/test/packageMains.js
Original file line number Diff line number Diff line change
@@ -4,46 +4,55 @@ const chai = require('chai');
const expect = chai.expect;
const path = require('path');

const webpack = require('../');
const resolver = require('../');

const file = path.join(__dirname, 'package-mains', 'dummy.js');


describe('packageMains', function () {

it('captures module', function () {
expect(webpack.resolve('./module', file)).property('path')
expect(resolver.resolve('./module', file)).property('path')
.to.equal(path.join(__dirname, 'package-mains', 'module', 'src', 'index.js'));
});

it('captures jsnext', function () {
expect(webpack.resolve('./jsnext', file)).property('path')
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(webpack.resolve('./webpack', file)).property('path')
expect(resolver.resolve('./webpack', file)).property('path')
.to.equal(path.join(__dirname, 'package-mains', 'webpack', 'webpack.js'));
});

it('captures jam (array path)', function () {
expect(webpack.resolve('./jam', file)).property('path')
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(webpack.resolve('./webpack', file, { config: 'webpack.alt.config.js' })).property('path')
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(webpack.resolve('./module', file, { config: 'webpack.alt.config.js' })).property('path')
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(webpack.resolve('./jsnext', file, { config: 'webpack.alt.config.js' })).property('path')
expect(resolver.resolve('./jsnext', file, { config: 'webpack.alt.config.js' })).property('path')
.to.equal(path.join(__dirname, 'package-mains', 'jsnext', 'src', 'index.js'));
});

});
3 changes: 0 additions & 3 deletions src/.eslintrc.yml

This file was deleted.

159 changes: 112 additions & 47 deletions src/ExportMap.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fs from 'fs';
import { dirname } from 'path';

import doctrine from 'doctrine';

@@ -7,6 +8,7 @@ import debug from 'debug';
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';

@@ -17,7 +19,7 @@ import { tsConfigLoader } from 'tsconfig-paths/lib/tsconfig-loader';

import includes from 'array-includes';

let parseConfigFileTextToJson;
let ts;

const log = debug('eslint-plugin-import:ExportMap');

@@ -351,17 +353,60 @@ ExportMap.for = function (context) {

ExportMap.parse = function (path, content, context) {
const m = new ExportMap(path);
const isEsModuleInteropTrue = isEsModuleInterop();

let ast;
let visitorKeys;
try {
ast = parse(path, content, context);
const result = parse(path, content, context);
ast = result.ast;
visitorKeys = result.visitorKeys;
} catch (err) {
log('parse error:', path, err);
m.errors.push(err);
return m; // can't continue
}

if (!unambiguous.isModule(ast)) return null;
m.visitorKeys = visitorKeys;

let hasDynamicImports = false;

function processDynamicImport(source) {
hasDynamicImports = true;
if (source.type !== 'Literal') {
return null;
}
const p = remotePath(source.value);
if (p == null) {
return null;
}
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,
},
importedSpecifiers,
}]),
});
}

visit(ast, visitorKeys, {
ImportExpression(node) {
processDynamicImport(node.source);
},
CallExpression(node) {
if (node.callee.type === 'Import') {
processDynamicImport(node.arguments[0]);
}
},
});

if (!unambiguous.isModule(ast) && !hasDynamicImports) return null;

const docstyle = (context.settings && context.settings['import/docstyle']) || ['jsdoc'];
const docStyleParsers = {};
@@ -413,6 +458,39 @@ ExportMap.parse = function (path, content, context) {
return object;
}

function processSpecifier(s, n, m) {
const nsource = n.source && n.source.value;
const exportMeta = {};
let local;

switch (s.type) {
case 'ExportDefaultSpecifier':
if (!nsource) return;
local = 'default';
break;
case 'ExportNamespaceSpecifier':
m.namespace.set(s.exported.name, Object.defineProperty(exportMeta, 'namespace', {
get() { return resolveImport(nsource); },
}));
return;
case 'ExportAllDeclaration':
m.namespace.set(s.exported.name, addNamespace(exportMeta, s.source.value));
return;
case 'ExportSpecifier':
if (!n.source) {
m.namespace.set(s.exported.name, addNamespace(exportMeta, s.local));
return;
}
// else falls through
default:
local = s.local.name;
break;
}

// todo: JSDoc
m.reexports.set(s.exported.name, { local, getImport: () => resolveImport(nsource) });
}

function captureDependency({ source }, isOnlyImportingTypes, importedSpecifiers = new Set()) {
if (source == null) return null;

@@ -448,12 +526,15 @@ ExportMap.parse = function (path, content, context) {
});
try {
if (tsConfigInfo.tsConfigPath !== undefined) {
const jsonText = fs.readFileSync(tsConfigInfo.tsConfigPath).toString();
if (!parseConfigFileTextToJson) {
// this is because projects not using TypeScript won't have typescript installed
({ parseConfigFileTextToJson } = require('typescript'));
}
return parseConfigFileTextToJson(tsConfigInfo.tsConfigPath, jsonText).config;
// Projects not using TypeScript won't have `typescript` installed.
if (!ts) { ts = require('typescript'); }

const configFile = ts.readConfigFile(tsConfigInfo.tsConfigPath, ts.sys.readFile);
return ts.parseJsonConfigFileContent(
configFile.config,
ts.sys,
dirname(tsConfigInfo.tsConfigPath),
);
}
} catch (e) {
// Catch any errors
@@ -468,11 +549,11 @@ ExportMap.parse = function (path, content, context) {
}).digest('hex');
let tsConfig = tsConfigCache.get(cacheKey);
if (typeof tsConfig === 'undefined') {
tsConfig = readTsConfig();
tsConfig = readTsConfig(context);
tsConfigCache.set(cacheKey, tsConfig);
}

return tsConfig && tsConfig.compilerOptions ? tsConfig.compilerOptions.esModuleInterop : false;
return tsConfig && tsConfig.options ? tsConfig.options.esModuleInterop : false;
}

ast.body.forEach(function (n) {
@@ -488,6 +569,9 @@ ExportMap.parse = function (path, content, context) {
if (n.type === 'ExportAllDeclaration') {
const getter = captureDependency(n, n.exportKind === 'type');
if (getter) m.dependencies.add(getter);
if (n.exported) {
processSpecifier(n, n.exported, m);
}
return;
}

@@ -545,39 +629,9 @@ ExportMap.parse = function (path, content, context) {
}
}

const nsource = n.source && n.source.value;
n.specifiers.forEach((s) => {
const exportMeta = {};
let local;

switch (s.type) {
case 'ExportDefaultSpecifier':
if (!n.source) return;
local = 'default';
break;
case 'ExportNamespaceSpecifier':
m.namespace.set(s.exported.name, Object.defineProperty(exportMeta, 'namespace', {
get() { return resolveImport(nsource); },
}));
return;
case 'ExportSpecifier':
if (!n.source) {
m.namespace.set(s.exported.name, addNamespace(exportMeta, s.local));
return;
}
// else falls through
default:
local = s.local.name;
break;
}

// todo: JSDoc
m.reexports.set(s.exported.name, { local, getImport: () => resolveImport(nsource) });
});
n.specifiers.forEach((s) => processSpecifier(s, n, m));
}

const isEsModuleInteropTrue = isEsModuleInterop();

const exports = ['TSExportAssignment'];
if (isEsModuleInteropTrue) {
exports.push('TSNamespaceExportDeclaration');
@@ -606,8 +660,11 @@ ExportMap.parse = function (path, content, context) {
m.namespace.set('default', captureDoc(source, docStyleParsers, n));
return;
}
if (isEsModuleInteropTrue) {
m.namespace.set('default', {});
if (
isEsModuleInteropTrue // esModuleInterop is on in tsconfig
&& !m.namespace.has('default') // and default isn't added already
) {
m.namespace.set('default', {}); // add default export
}
exportedDecls.forEach((decl) => {
if (decl.type === 'TSModuleDeclaration') {
@@ -627,8 +684,8 @@ ExportMap.parse = function (path, content, context) {
namespaceDecl.declarations.forEach((d) =>
recursivePatternCapture(d.id, (id) => m.namespace.set(
id.name,
captureDoc(source, docStyleParsers, decl, namespaceDecl, moduleBlockNode)
))
captureDoc(source, docStyleParsers, decl, namespaceDecl, moduleBlockNode),
)),
);
} else {
m.namespace.set(
@@ -645,6 +702,14 @@ ExportMap.parse = function (path, content, context) {
}
});

if (
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
}

return m;
};

10 changes: 3 additions & 7 deletions src/core/importType.js
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ export function isExternalModule(name, settings, path, context) {
if (arguments.length < 4) {
throw new TypeError('isExternalModule: name, settings, path, and context are all required');
}
return isModule(name) && isExternalPath(name, settings, path, getContextPackagePath(context));
return (isModule(name) || isScoped(name)) && isExternalPath(name, settings, path, getContextPackagePath(context));
}

export function isExternalModuleMain(name, settings, path, context) {
@@ -64,7 +64,7 @@ function isModuleMain(name) {
return name && moduleMainRegExp.test(name);
}

const scopedRegExp = /^@[^/]*\/?[^/]+/;
const scopedRegExp = /^@[^/]+\/?[^/]+/;
export function isScoped(name) {
return name && scopedRegExp.test(name);
}
@@ -75,7 +75,7 @@ export function isScopedMain(name) {
}

function isRelativeToParent(name) {
return/^\.\.$|^\.\.[\\/]/.test(name);
return /^\.\.$|^\.\.[\\/]/.test(name);
}

const indexFiles = ['.', './', './index', './index.js'];
@@ -101,10 +101,6 @@ function typeTest(name, context, path) {
return 'unknown';
}

export function isScopedModule(name) {
return name.indexOf('@') === 0 && !name.startsWith('@/');
}

export default function resolveImportType(name, context) {
return typeTest(name, context, resolve(name, context));
}
16 changes: 10 additions & 6 deletions src/core/packagePath.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { dirname } from 'path';
import findUp from 'find-up';
import readPkgUp from 'read-pkg-up';
import pkgUp from 'eslint-module-utils/pkgUp';
import readPkgUp from 'eslint-module-utils/readPkgUp';


export function getContextPackagePath(context) {
return getFilePackagePath(context.getFilename());
return getFilePackagePath(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename());
}

export function getFilePackagePath(filePath) {
const fp = findUp.sync('package.json', { cwd: filePath });
const fp = pkgUp({ cwd: filePath });
return dirname(fp);
}

export function getFilePackageName(filePath) {
const { pkg } = readPkgUp.sync({ cwd: filePath, normalize: false });
return pkg && pkg.name;
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 null;
}
2 changes: 1 addition & 1 deletion src/docsUrl.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pkg from '../package.json';

const repoUrl = 'https://github.com/benmosher/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`;
4 changes: 2 additions & 2 deletions src/rules/default.js
Original file line number Diff line number Diff line change
@@ -10,12 +10,12 @@ module.exports = {
schema: [],
},

create: function (context) {
create(context) {

function checkDefault(specifierType, node) {

const defaultSpecifier = node.specifiers.find(
specifier => specifier.type === specifierType
specifier => specifier.type === specifierType,
);

if (!defaultSpecifier) return;
4 changes: 2 additions & 2 deletions src/rules/dynamic-import-chunkname.js
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@ module.exports = {
}],
},

create: function (context) {
create(context) {
const config = context.options[0];
const { importFunctions = [] } = config || {};
const { webpackChunknameFormat = '[0-9a-zA-Z-_/.]+' } = config || {};
@@ -69,7 +69,7 @@ module.exports = {

try {
// just like webpack itself does
vm.runInNewContext(`(function(){return {${comment.value}}})()`);
vm.runInNewContext(`(function() {return {${comment.value}}})()`);
}
catch (error) {
context.report({
8 changes: 4 additions & 4 deletions src/rules/export.js
Original file line number Diff line number Diff line change
@@ -54,7 +54,7 @@ module.exports = {
schema: [],
},

create: function (context) {
create(context) {
const namespace = new Map([[rootProgram, new Map()]]);

function addNamed(name, node, parent, isType) {
@@ -90,7 +90,7 @@ module.exports = {
'ExportSpecifier': (node) => addNamed(
node.exported.name,
node.exported,
getParent(node.parent)
getParent(node.parent),
),

'ExportNamedDeclaration': function (node) {
@@ -146,7 +146,7 @@ module.exports = {
if (!any) {
context.report(
node.source,
`No named exports found in module '${node.source.value}'.`
`No named exports found in module '${node.source.value}'.`,
);
}
},
@@ -164,7 +164,7 @@ module.exports = {
} else {
context.report(
node,
`Multiple exports of name '${name.replace(tsTypePrefix, '')}'.`
`Multiple exports of name '${name.replace(tsTypePrefix, '')}'.`,
);
}
}
4 changes: 2 additions & 2 deletions src/rules/exports-last.js
Original file line number Diff line number Diff line change
@@ -15,9 +15,9 @@ module.exports = {
schema: [],
},

create: function (context) {
create(context) {
return {
Program: function ({ body }) {
Program({ body }) {
const lastNonExportStatementIndex = body.reduce(function findLastIndex(acc, item, index) {
if (isNonExportStatement(item)) {
return index;
16 changes: 9 additions & 7 deletions src/rules/extensions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from 'path';

import resolve from 'eslint-module-utils/resolve';
import { isBuiltIn, isExternalModule, isScoped, isScopedModule } from '../core/importType';
import { isBuiltIn, isExternalModule, isScoped } from '../core/importType';
import moduleVisitor from 'eslint-module-utils/moduleVisitor';
import docsUrl from '../docsUrl';

@@ -103,7 +103,7 @@ module.exports = {
},
},

create: function (context) {
create(context) {

const props = buildProperties(context);

@@ -131,13 +131,13 @@ module.exports = {
const slashCount = file.split('/').length - 1;

if (slashCount === 0) return true;
if (isScopedModule(file) && slashCount <= 1) return true;
if (isScoped(file) && slashCount <= 1) return true;
return false;
}

function checkFileExtension(source) {
// bail if the declaration doesn't have a source, e.g. "export { foo };"
if (!source) return;
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;

const importPathWithQueryString = source.value;

@@ -161,10 +161,12 @@ module.exports = {
importPath,
context.settings,
resolve(importPath, context),
context
context,
) || isScoped(importPath);

if (!extension || !importPath.endsWith(`.${extension}`)) {
// ignore type-only imports
if (node.importKind === 'type') return;
const extensionRequired = isUseOfExtensionRequired(extension, isPackage);
const extensionForbidden = isUseOfExtensionForbidden(extension);
if (extensionRequired && !extensionForbidden) {
10 changes: 5 additions & 5 deletions src/rules/first.js
Original file line number Diff line number Diff line change
@@ -21,8 +21,8 @@ module.exports = {
],
},

create: function (context) {
function isPossibleDirective (node) {
create(context) {
function isPossibleDirective(node) {
return node.type === 'ExpressionStatement' &&
node.expression.type === 'Literal' &&
typeof node.expression.value === 'string';
@@ -42,7 +42,7 @@ module.exports = {
const errorInfos = [];
let shouldSort = true;
let lastSortNodesIndex = 0;
body.forEach(function (node, index){
body.forEach(function (node, index) {
if (!anyExpressions && isPossibleDirective(node)) {
return;
}
@@ -105,7 +105,7 @@ module.exports = {
const range = [0, removeFixers[removeFixers.length - 1].range[1]];
let insertSourceCode = sortNodes.map(function (_errorInfo) {
const nodeSourceCode = String.prototype.slice.apply(
originSourceCode, _errorInfo.range
originSourceCode, _errorInfo.range,
);
if (/\S/.test(nodeSourceCode[0])) {
return '\n' + nodeSourceCode;
@@ -124,7 +124,7 @@ module.exports = {
const fixers = [insertFixer].concat(removeFixers);
fixers.forEach(function (computedFixer, i) {
replaceSourceCode += (originSourceCode.slice(
fixers[i - 1] ? fixers[i - 1].range[1] : 0, computedFixer.range[0]
fixers[i - 1] ? fixers[i - 1].range[1] : 0, computedFixer.range[0],
) + computedFixer.text);
});
return fixer.replaceTextRange(range, replaceSourceCode);
18 changes: 12 additions & 6 deletions src/rules/max-dependencies.js
Original file line number Diff line number Diff line change
@@ -2,15 +2,14 @@ import moduleVisitor from 'eslint-module-utils/moduleVisitor';
import docsUrl from '../docsUrl';

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 };

if (dependencies.size > max) {
context.report(
lastNode,
`Maximum number of dependencies (${max}) exceeded.`
);
context.report(lastNode, `Maximum number of dependencies (${max}) exceeded.`);
}
};

@@ -26,22 +25,29 @@ module.exports = {
'type': 'object',
'properties': {
'max': { 'type': 'number' },
'ignoreTypeImports': { 'type': 'boolean' },
},
'additionalProperties': false,
},
],
},

create: context => {
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

return Object.assign({
'Program:exit': function () {
countDependencies(dependencies, lastNode, context);
},
}, moduleVisitor((source) => {
dependencies.add(source.value);
}, moduleVisitor((source, { importKind }) => {
if (importKind !== TYPE_IMPORT || !ignoreTypeImports) {
dependencies.add(source.value);
}
lastNode = source;
}, { commonjs: true }));
},
116 changes: 91 additions & 25 deletions src/rules/named.js
Original file line number Diff line number Diff line change
@@ -8,65 +8,131 @@ module.exports = {
docs: {
url: docsUrl('named'),
},
schema: [],
schema: [
{
type: 'object',
properties: {
commonjs: {
type: 'boolean',
},
},
additionalProperties: false,
},
],
},

create: function (context) {
create(context) {
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') {
if (
node.source == null
|| node.importKind === 'type'
|| node.importKind === 'typeof'
|| node.exportKind === 'type'
) {
return;
}

if (!node.specifiers
.some(function (im) { return im.type === type; })) {
if (!node.specifiers.some((im) => im.type === type)) {
return; // no named imports/exports
}

const imports = Exports.get(node.source.value, context);
if (imports == null) return;
if (imports == null) {
return;
}

if (imports.errors.length) {
imports.reportErrors(context, node);
return;
}

node.specifiers.forEach(function (im) {
if (im.type !== type) return;

// ignore type imports
if (im.importKind === 'type' || im.importKind === 'typeof') return;
if (
im.type !== type
// ignore type imports
|| im.importKind === 'type' || im.importKind === 'typeof'
) {
return;
}

const deepLookup = imports.hasDeep(im[key].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], `${im[key].name} not found via ${deepPath}`);
} else {
context.report(im[key], im[key].name + ' not found in \'' + node.source.value + '\'');
}
}
});
}

function checkRequire(node) {
if (
!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
// return if there is no call expression on the right side
|| !node.init || node.init.type !== 'CallExpression'
) {
return;
}

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
// return if it's not a string source
|| source.type !== 'Literal'
|| variableExports == null
) {
return;
}

if (variableExports.errors.length) {
variableExports.reportErrors(context, node);
return;
}

variableImports.forEach(function (im) {
if (im.type !== 'Property' || !im.key || im.key.type !== 'Identifier') {
return;
}

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(' -> ');

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 \'' + node.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,
};
},
};
14 changes: 7 additions & 7 deletions src/rules/namespace.js
Original file line number Diff line number Diff line change
@@ -60,16 +60,16 @@ module.exports = {
if (!imports.size) {
context.report(
specifier,
`No exported names found in module '${declaration.source.value}'.`
`No exported names found in module '${declaration.source.value}'.`,
);
}
namespaces.set(specifier.local.name, imports);
break;
case 'ImportDefaultSpecifier':
case 'ImportSpecifier': {
const meta = imports.get(
// default to 'default' for default http://i.imgur.com/nj6qAWy.jpg
specifier.imported ? specifier.imported.name : 'default'
// default to 'default' for default https://i.imgur.com/nj6qAWy.jpg
specifier.imported ? specifier.imported.name : 'default',
);
if (!meta || !meta.namespace) { break; }
namespaces.set(specifier.local.name, meta.namespace);
@@ -96,7 +96,7 @@ module.exports = {
if (!imports.size) {
context.report(
namespace,
`No exported names found in module '${declaration.source.value}'.`
`No exported names found in module '${declaration.source.value}'.`,
);
}
},
@@ -111,7 +111,7 @@ module.exports = {
if (dereference.parent.type === 'AssignmentExpression' && dereference.parent.left === dereference) {
context.report(
dereference.parent,
`Assignment to member of namespace '${dereference.object.name}'.`
`Assignment to member of namespace '${dereference.object.name}'.`,
);
}

@@ -125,7 +125,7 @@ module.exports = {
if (!allowComputed) {
context.report(
dereference.property,
`Unable to validate computed reference to imported namespace '${dereference.object.name}'.`
`Unable to validate computed reference to imported namespace '${dereference.object.name}'.`,
);
}
return;
@@ -134,7 +134,7 @@ module.exports = {
if (!namespace.has(dereference.property.name)) {
context.report(
dereference.property,
makeMessage(dereference.property, namepath)
makeMessage(dereference.property, namepath),
);
break;
}
12 changes: 6 additions & 6 deletions src/rules/newline-after-import.js
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ function isExportDefaultClass(node) {
}

function isExportNameClass(node) {

return node.type === 'ExportNamedDeclaration' && node.declaration && node.declaration.type === 'ClassDeclaration';
}

@@ -72,7 +72,7 @@ module.exports = {
},
],
},
create: function (context) {
create(context) {
let level = 0;
const requireCalls = [];

@@ -107,7 +107,7 @@ module.exports = {
after ${type} statement not followed by another ${type}.`,
fix: fixer => fixer.insertTextAfter(
node,
'\n'.repeat(EXPECTED_LINE_DIFFERENCE - lineDifference)
'\n'.repeat(EXPECTED_LINE_DIFFERENCE - lineDifference),
),
});
}
@@ -124,7 +124,7 @@ after ${type} statement not followed by another ${type}.`,
const { parent } = node;
const nodePosition = parent.body.indexOf(node);
const nextNode = parent.body[nodePosition + 1];

// skip "export import"s
if (node.type === 'TSImportEqualsDeclaration' && node.isExport) {
return;
@@ -138,13 +138,13 @@ after ${type} statement not followed by another ${type}.`,
return {
ImportDeclaration: checkImport,
TSImportEqualsDeclaration: checkImport,
CallExpression: function(node) {
CallExpression(node) {
if (isStaticRequire(node) && level === 0) {
requireCalls.push(node);
}
},
'Program:exit': function () {
log('exit processing for', context.getFilename());
log('exit processing for', context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename());
const scopeBody = getScopeBody(context.getScope());
log('got scope:', scopeBody);

2 changes: 1 addition & 1 deletion src/rules/no-absolute-path.js
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ module.exports = {
schema: [ makeOptionsSchema() ],
},

create: function (context) {
create(context) {
function reportIfAbsolute(source) {
if (typeof source.value === 'string' && isAbsolute(source.value)) {
context.report(source, 'Do not import modules using an absolute path');
2 changes: 1 addition & 1 deletion src/rules/no-amd.js
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ module.exports = {
schema: [],
},

create: function (context) {
create(context) {
return {
'CallExpression': function (node) {
if (context.getScope().type !== 'module') return;
2 changes: 1 addition & 1 deletion src/rules/no-anonymous-default-export.js
Original file line number Diff line number Diff line change
@@ -86,7 +86,7 @@ module.exports = {
],
},

create: function (context) {
create(context) {
const options = Object.assign({}, defaults, context.options[0]);

return {
4 changes: 2 additions & 2 deletions src/rules/no-commonjs.js
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ function validateScope(scope) {
return scope.variableScope.type === 'module';
}

// https://github.com/estree/estree/blob/master/es5.md
// https://github.com/estree/estree/blob/HEAD/es5.md
function isConditional(node) {
if (
node.type === 'IfStatement'
@@ -88,7 +88,7 @@ module.exports = {
},
},

create: function (context) {
create(context) {
const options = normalizeLegacyOptions(context.options);

return {
8 changes: 4 additions & 4 deletions src/rules/no-cycle.js
Original file line number Diff line number Diff line change
@@ -36,8 +36,8 @@ module.exports = {
})],
},

create: function (context) {
const myPath = context.getFilename();
create(context) {
const myPath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename();
if (myPath === '<text>') return {}; // can't cycle-check a non-file

const options = context.options[0] || {};
@@ -46,7 +46,7 @@ module.exports = {
name,
context.settings,
resolve(name, context),
context
context,
);

function checkSourceValue(sourceNode, importer) {
@@ -88,7 +88,7 @@ module.exports = {
const toTraverse = [...declarations].filter(({ source, isOnlyImportingTypes }) =>
!ignoreModule(source.value) &&
// Ignore only type imports
!isOnlyImportingTypes
!isOnlyImportingTypes,
);
/*
Only report as a cycle if there are any import declarations that are considered by
2 changes: 1 addition & 1 deletion src/rules/no-deprecated.js
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ module.exports = {
schema: [],
},

create: function (context) {
create(context) {
const deprecated = new Map();
const namespaces = new Map();

45 changes: 34 additions & 11 deletions src/rules/no-duplicates.js
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@ function getFix(first, rest, sourceCode) {
}

const defaultImportNames = new Set(
[first, ...rest].map(getDefaultImportName).filter(Boolean)
[first, ...rest].map(getDefaultImportName).filter(Boolean),
);

// Bail if there are multiple different default import names – it's up to the
@@ -83,7 +83,7 @@ function getFix(first, rest, sourceCode) {
const unnecessaryImports = restWithoutComments.filter(node =>
!hasSpecifiers(node) &&
!hasNamespace(node) &&
!specifiers.some(specifier => specifier.importNode === node)
!specifiers.some(specifier => specifier.importNode === node),
);

const shouldAddDefault = getDefaultImportName(first) == null && defaultImportNames.size === 1;
@@ -115,15 +115,15 @@ function getFix(first, rest, sourceCode) {
specifier.isEmpty ? needsComma : true,
];
},
['', !firstHasTrailingComma && !firstIsEmpty]
['', !firstHasTrailingComma && !firstIsEmpty],
);

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'`
@@ -150,14 +150,27 @@ function getFix(first, rest, sourceCode) {

// Remove imports whose specifiers have been moved into the first import.
for (const specifier of specifiers) {
fixes.push(fixer.remove(specifier.importNode));
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));
}
}

// Remove imports whose default import has been moved to the first import,
// and side-effect-only imports that are unnecessary due to the first
// import.
for (const node of unnecessaryImports) {
fixes.push(fixer.remove(node));

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));
}
}

return fixes;
@@ -248,7 +261,7 @@ module.exports = {
],
},

create: function (context) {
create(context) {
// Prepare the resolver from options.
const considerQueryStringOption = context.options[0] &&
context.options[0]['considerQueryString'];
@@ -263,13 +276,22 @@ module.exports = {

const imported = new Map();
const nsImported = new Map();
const typesImported = new Map();
const defaultTypesImported = new Map();
const namedTypesImported = new Map();

function getImportMap(n) {
if (n.importKind === 'type') {
return n.specifiers.length > 0 && n.specifiers[0].type === 'ImportDefaultSpecifier' ? defaultTypesImported : namedTypesImported;
}

return hasNamespace(n) ? nsImported : imported;
}

return {
'ImportDeclaration': function (n) {
ImportDeclaration(n) {
// resolved path will cover aliased duplicates
const resolvedPath = resolver(n.source.value);
const importMap = n.importKind === 'type' ? typesImported :
(hasNamespace(n) ? nsImported : imported);
const importMap = getImportMap(n);

if (importMap.has(resolvedPath)) {
importMap.get(resolvedPath).push(n);
@@ -281,7 +303,8 @@ module.exports = {
'Program:exit': function () {
checkImports(imported, context);
checkImports(nsImported, context);
checkImports(typesImported, context);
checkImports(defaultTypesImported, context);
checkImports(namedTypesImported, context);
},
};
},
35 changes: 31 additions & 4 deletions src/rules/no-dynamic-require.js
Original file line number Diff line number Diff line change
@@ -8,6 +8,12 @@ function isRequire(node) {
node.arguments.length >= 1;
}

function isDynamicImport(node) {
return node &&
node.callee &&
node.callee.type === 'Import';
}

function isStaticValue(arg) {
return arg.type === 'Literal' ||
(arg.type === 'TemplateLiteral' && arg.expressions.length === 0);
@@ -19,18 +25,39 @@ module.exports = {
docs: {
url: docsUrl('no-dynamic-require'),
},
schema: [],
schema: [
{
type: 'object',
properties: {
esmodule: {
type: 'boolean',
},
},
additionalProperties: false,
},
],
},

create: function (context) {
create(context) {
const options = context.options[0] || {};

return {
CallExpression(node) {
if (isRequire(node) && !isStaticValue(node.arguments[0])) {
context.report({
if (!node.arguments[0] || isStaticValue(node.arguments[0])) {
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: 'Calls to import() should use string literals',
});
}
},
};
},
103 changes: 74 additions & 29 deletions src/rules/no-extraneous-dependencies.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'path';
import fs from 'fs';
import readPkgUp from 'read-pkg-up';
import readPkgUp from 'eslint-module-utils/readPkgUp';
import minimatch from 'minimatch';
import resolve from 'eslint-module-utils/resolve';
import moduleVisitor from 'eslint-module-utils/moduleVisitor';
@@ -55,22 +55,22 @@ function getDependencies(context, packageDir) {
const packageJsonPath = path.join(dir, 'package.json');
if (!depFieldCache.has(packageJsonPath)) {
const depFields = extractDepFields(
JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')),
);
depFieldCache.set(packageJsonPath, depFields);
}
const _packageContent = depFieldCache.get(packageJsonPath);
Object.keys(packageContent).forEach(depsKey =>
Object.assign(packageContent[depsKey], _packageContent[depsKey])
Object.assign(packageContent[depsKey], _packageContent[depsKey]),
);
});
} else {
// use closest package.json
Object.assign(
packageContent,
extractDepFields(
readPkgUp.sync({ cwd: context.getFilename(), normalize: false }).pkg
)
readPkgUp({ cwd: context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(), normalize: false }).pkg,
),
);
}

@@ -126,9 +126,44 @@ function getModuleRealName(resolved) {
return getFilePackageName(resolved);
}

function checkDependencyDeclaration(deps, packageName, declarationStatus) {
const newDeclarationStatus = declarationStatus || {
isInDeps: false,
isInDevDeps: false,
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('/') : [];
packageNameParts.forEach((namePart, index) => {
if (!namePart.startsWith('@')) {
const ancestor = packageNameParts.slice(0, index + 1).join('/');
packageHierarchy.push(ancestor);
}
});

return packageHierarchy.reduce((result, ancestorName) => {
return {
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
if (node.importKind === 'type' || (node.parent && node.parent.importKind === 'type') || node.importKind === 'typeof') {
if (
node.importKind === 'type' ||
node.importKind === 'typeof'
) {
return;
}

@@ -139,37 +174,47 @@ function reportIfMissing(context, deps, depsOptions, node, name) {
const resolved = resolve(name, context);
if (!resolved) { return; }

// get the real name from the resolved package.json
// if not aliased imports (alias/react for example) will not be correctly interpreted
// fallback on original name in case no package.json found
const packageName = getModuleRealName(resolved) || getModuleOriginalName(name);

const isInDeps = deps.dependencies[packageName] !== undefined;
const isInDevDeps = deps.devDependencies[packageName] !== undefined;
const isInOptDeps = deps.optionalDependencies[packageName] !== undefined;
const isInPeerDeps = deps.peerDependencies[packageName] !== undefined;
const isInBundledDeps = deps.bundledDependencies.indexOf(packageName) !== -1;

if (isInDeps ||
(depsOptions.allowDevDeps && isInDevDeps) ||
(depsOptions.allowPeerDeps && isInPeerDeps) ||
(depsOptions.allowOptDeps && isInOptDeps) ||
(depsOptions.allowBundledDeps && isInBundledDeps)
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)
) {
return;
}

if (isInDevDeps && !depsOptions.allowDevDeps) {
context.report(node, devDepErrorMessage(packageName));
// 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);
if (realPackageName && realPackageName !== importPackageName) {
declarationStatus = checkDependencyDeclaration(deps, realPackageName, declarationStatus);

if (
declarationStatus.isInDeps ||
(depsOptions.allowDevDeps && declarationStatus.isInDevDeps) ||
(depsOptions.allowPeerDeps && declarationStatus.isInPeerDeps) ||
(depsOptions.allowOptDeps && declarationStatus.isInOptDeps) ||
(depsOptions.allowBundledDeps && declarationStatus.isInBundledDeps)
) {
return;
}
}

if (declarationStatus.isInDevDeps && !depsOptions.allowDevDeps) {
context.report(node, devDepErrorMessage(realPackageName || importPackageName));
return;
}

if (isInOptDeps && !depsOptions.allowOptDeps) {
context.report(node, optDepErrorMessage(packageName));
if (declarationStatus.isInOptDeps && !depsOptions.allowOptDeps) {
context.report(node, optDepErrorMessage(realPackageName || importPackageName));
return;
}

context.report(node, missingErrorMessage(packageName));
context.report(node, missingErrorMessage(realPackageName || importPackageName));
}

function testConfig(config, filename) {
@@ -206,9 +251,9 @@ module.exports = {
],
},

create: function (context) {
create(context) {
const options = context.options[0] || {};
const filename = context.getFilename();
const filename = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename();
const deps = getDependencies(context, options.packageDir) || extractDepFields({});

const depsOptions = {
Loading