diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 8fd7679907..0ef28872fd 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -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 diff --git a/.github/workflows/node-4+.yml b/.github/workflows/node-4+.yml index 6456762fce..b13e3b88cf 100644 --- a/.github/workflows/node-4+.yml +++ b/.github/workflows/node-4+.yml @@ -33,6 +33,11 @@ jobs: - 3 - 2 include: + - node-version: 'lts/*' + eslint: 7 + ts-parser: 3 + env: + TS_PARSER: 3 - node-version: 'lts/*' eslint: 7 ts-parser: 2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 998055e619..355985a816 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,22 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ## [Unreleased] +## [2.24.2] - 2021-08-24 + +### Fixed +- [`named`], [`namespace`]: properly handle ExportAllDeclarations ([#2199], thanks [@ljharb]) + +## [2.24.1] - 2021-08-19 + +### Fixed +- `ExportMap`: Add default export when esModuleInterop is true and anything is exported ([#2184], thanks [@Maxim-Mazurok]) +- [`named`], [`namespace`]: properly set reexports on `export * as … from` ([#1998], [#2161], thanks [@ljharb]) +- [`no-duplicates`]: correctly handle case of mixed default/named type imports ([#2149], thanks [@GoodForOneFare], [@nwalters512]) +- [`no-duplicates`]: avoid crash with empty `import type {}` ([#2201], thanks [@ljharb]) + +### Changed +- [Docs] `max-dependencies`: 📖 Document `ignoreTypeImports` option ([#2196], thanks [@himynameisdave]) + ## [2.24.0] - 2021-08-08 ### Added @@ -17,11 +33,12 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ### Fixed - [`no-duplicates`]: ensure autofix avoids excessive newlines ([#2028], thanks [@ertrzyiks]) - [`extensions`]: avoid crashing on partially typed import/export statements ([#2118], thanks [@ljharb]) -- [`no-extraneous-dependencies`]: add ESM intermediate package.json support] ([#2121], thanks [@paztis]) +- [`no-extraneous-dependencies`]: add ESM intermediate package.json support ([#2121], thanks [@paztis]) - Use `context.getPhysicalFilename()` when available (ESLint 7.28+) ([#2160], thanks [@pmcelhaney]) - [`extensions`]/`importType`: fix isScoped treating @/abc as scoped module ([#2146], thanks [@rperello]) ### Changed +- [Docs] [`extensions`]: improved cases for using `@/...` ([#2140], thanks [@wenfangdu]) - [Docs] [`extensions`]: removed incorrect cases ([#2138], thanks [@wenfangdu]) - [Tests] [`order`]: add tests for `pathGroupsExcludedImportTypes: ['type']` ([#2158], thanks [@atav32]) - [Docs] [`order`]: improve the documentation for the `pathGroupsExcludedImportTypes` option ([#2156], thanks [@liby]) @@ -94,6 +111,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [Docs] Add import/recommended ruleset to README ([#2034], thanks [@edemaine]) ## [2.22.1] - 2020-09-27 + ### Fixed - [`default`]/TypeScript: avoid crash on `export =` with a MemberExpression ([#1841], thanks [@ljharb]) - [`extensions`]/importType: Fix @/abc being treated as scoped module ([#1854], thanks [@3nuc]) @@ -104,6 +122,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [`export`]: add tests for a name collision with `export * from` ([#1704], thanks @tomprats) ## [2.22.0] - 2020-06-26 + ### Added - [`no-unused-modules`]: consider exported TypeScript interfaces, types and enums ([#1819], thanks [@nicolashenry]) - [`no-cycle`]: allow `maxDepth` option to be `"∞"` (thanks [@ljharb]) @@ -124,16 +143,19 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [docs] [`imports-first`]: deprecation info and link to `first` docs ([#1835], thanks [@beatrizrezener]) ## [2.21.2] - 2020-06-09 + ### Fixed - [`order`]: avoid a crash on TypeScript’s `export import` syntax ([#1808], thanks [@ljharb]) - [`newline-after-import`]: consider TypeScript `import =` syntax' ([#1811], thanks [@ljharb]) - [`no-internal-modules`]: avoid a crash on a named export declaration ([#1814], thanks [@ljharb]) ## [2.21.1] - 2020-06-07 + ### Fixed - TypeScript: [`import/named`]: avoid requiring `typescript` when not using TS ([#1805], thanks [@ljharb]) ## [2.21.0] - 2020-06-07 + ### Added - [`import/default`]: support default export in TSExportAssignment ([#1528], thanks [@joaovieira]) - [`no-cycle`]: add `ignoreExternal` option ([#1681], thanks [@sveyret]) @@ -164,6 +186,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [eslint] bump minimum v7 version to v7.2.0 ## [2.20.2] - 2020-03-28 + ### Fixed - [`order`]: fix `isExternalModule` detect on windows ([#1651], thanks [@fisker]) - [`order`]: recognize ".." as a "parent" path ([#1658], thanks [@golopot]) @@ -173,6 +196,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [Docs] `no-webpack-loader-syntax`: Updates webpack URLs ([#1751], thanks [@MikeyBeLike]) ## [2.20.1] - 2020-02-01 + ### Fixed - [`export`]: Handle function overloading in `*.d.ts` ([#1619], thanks [@IvanGoncharov]) - [`no-absolute-path`]: fix a crash with invalid import syntax ([#1616], thanks [@ljharb]) @@ -190,6 +214,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [Tests] `order`: Add TS import type tests ([#1736], thanks [@kmui2]) ## [2.20.0] - 2020-01-10 + ### Added - [`order`]: added `caseInsensitive` as an additional option to `alphabetize` ([#1586], thanks [@dbrewer5]) - [`no-restricted-paths`]: New `except` option per `zone`, allowing exceptions to be defined for a restricted zone ([#1238], thanks [@rsolomon]) @@ -206,10 +231,12 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [`extensions`]: improve `ignorePackages` docs ([#1248], thanks [@ivo-stefchev]) ## [2.19.1] - 2019-12-08 + ### Fixed - [`no-extraneous-dependencies`]: ensure `node.source` exists ## [2.19.0] - 2019-12-08 + ### Added - [`internal-regex` setting]: regex pattern for marking packages "internal" ([#1491], thanks [@Librazy]) - [`group-exports`]: make aggregate module exports valid ([#1472], thanks [@atikenny]) @@ -241,10 +268,12 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [`no-unused-modules`]/`eslint-module-utils`: Avoid superfluous calls and code ([#1551], thanks [@brettz9]) ## [2.18.2] - 2019-07-19 + ### Fixed - Skip warning on type interfaces ([#1425], thanks [@lencioni]) ## [2.18.1] - 2019-07-18 + ### Fixed - Improve parse perf when using `@typescript-eslint/parser` ([#1409], thanks [@bradzacher]) - [`prefer-default-export`]: don't warn on TypeAlias & TSTypeAliasDeclaration ([#1377], thanks [@sharmilajesupaul]) @@ -255,6 +284,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [`no-extraneous-dependencies`], `importType`: remove lodash ([#1419], thanks [@ljharb]) ## [2.18.0] - 2019-06-24 + ### Added - Support eslint v6 ([#1393], thanks [@sheepsteak]) - [`order`]: Adds support for correctly sorting unknown types into a single group ([#1375], thanks [@swernerx]) @@ -269,6 +299,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [`no-named-as-default-member`]: update broken link ([#1389], thanks [@fooloomanzoo]) ## [2.17.3] - 2019-05-23 + ### Fixed - [`no-common-js`]: Also throw an error when assigning ([#1354], thanks [@charlessuh]) - [`no-unused-modules`]: don't crash when lint file outside src-folder ([#1347], thanks [@rfermann]) @@ -284,15 +315,18 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - Document `env` option for `eslint-import-resolver-webpack` ([#1363], thanks [@kgregory]) ## [2.17.2] - 2019-04-16 + ### Fixed - [`no-unused-modules`]: avoid crash when using `ignoreExports`-option ([#1331], [#1323], thanks [@rfermann]) - [`no-unused-modules`]: make sure that rule with no options will not fail ([#1330], [#1334], thanks [@kiwka]) ## [2.17.1] - 2019-04-13 + ### Fixed - require v2.4 of `eslint-module-utils` ([#1322]) ## [2.17.0] - 2019-04-13 + ### Added - [`no-useless-path-segments`]: Add `noUselessIndex` option ([#1290], thanks [@timkraut]) - [`no-duplicates`]: Add autofix ([#1312], thanks [@lydell]) @@ -318,6 +352,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [`no-commonjs`]: add tests that show corner cases ([#1308], thanks [@TakeScoop]) ## [2.16.0] - 2019-01-29 + ### Added - `typescript` config ([#1257], thanks [@kirill-konshin]) @@ -325,6 +360,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - Memory leak of `SourceCode` objects for all parsed dependencies, resolved. (issue [#1266], thanks [@asapach] and [@sergei-startsev] for digging in) ## [2.15.0] - 2019-01-22 + ### Added - new rule: [`no-named-export`] ([#1157], thanks [@fsmaia]) @@ -334,6 +370,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [`named`]: fix destructuring assignment ([#1232], thanks [@ljqx]) ## [2.14.0] - 2018-08-13 + ### Added - [`no-useless-path-segments`]: add commonJS (CJS) support ([#1128], thanks [@1pete]) - [`namespace`]: add JSX check ([#1151], thanks [@jf248]) @@ -349,6 +386,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - add explicit support for RestElement alongside ExperimentalRestProperty (thanks [@ljharb]) ## [2.13.0] - 2018-06-24 + ### Added - Add ESLint 5 support ([#1122], thanks [@ai] and [@ljharb]) - Add [`no-relative-parent-imports`] rule: disallow relative imports from parent directories ([#1093], thanks [@chrislloyd]) @@ -357,12 +395,14 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - `namespace` rule: ensure it works in eslint 5/ecmaVersion 2018 (thanks [@ljharb]) ## [2.12.0] - 2018-05-17 + ### Added - Ignore type imports for [`named`] rule ([#931], thanks [@mattijsbliek]) - Add documentation for [`no-useless-path-segments`] rule ([#1068], thanks [@manovotny]) - `packageDir` option for [`no-extraneous-dependencies`] can be array-valued ([#1085], thanks [@hulkish]) ## [2.11.0] - 2018-04-09 + ### Added - Fixer for [`first`] ([#1046], thanks [@fengkfengk]) - `allow-require` option for [`no-commonjs`] rule ([#880], thanks [@futpib]) @@ -371,11 +411,13 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - memory/CPU regression where ASTs were held in memory ([#1058], thanks [@klimashkin]/[@lukeapage]) ## [2.10.0] - 2018-03-29 + ### Added - Autofixer for [`order`] rule ([#908], thanks [@tihonove]) - Add [`no-cycle`] rule: reports import cycles. ## [2.9.0] - 2018-02-21 + ### Added - Add [`group-exports`] rule: style-guide rule to report use of multiple named exports ([#721], thanks [@robertrossmann]) - Add [`no-self-import`] rule: forbids a module from importing itself. ([#727], [#449], [#447], thanks [@giodamelio]). @@ -396,14 +438,17 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - TypeError for missing AST fields from TypeScript ([#842] / [#944], thanks [@alexgorbatchev]) ## [2.7.0] - 2017-07-06 + ### Changed - [`no-absolute-path`] picks up speed boost, optional AMD support ([#843], thanks [@jseminck]) ## [2.6.1] - 2017-06-29 + ### Fixed - update bundled node resolver dependency to latest version ## [2.6.0] - 2017-06-23 + ### Changed - update tests / peerDeps for ESLint 4.0 compatibility ([#871], thanks [@mastilver]) - [`memo-parser`] updated to require `filePath` on parser options as it melts @@ -426,6 +471,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - Add `allow` option to [`no-unassigned-import`] to allow for files that match the globs ([#671], [#737], thanks [@kevin940726]). ## [2.3.0] - 2017-05-18 + ### Added - [`no-anonymous-default-export`] rule: report anonymous default exports ([#712], thanks [@duncanbeevers]). - Add new value to [`order`]'s `newlines-between` option to allow newlines inside import groups ([#627], [#628], thanks [@giodamelio]) @@ -440,8 +486,8 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - "default is a reserved keyword" in no-maned-default tests by locking down babylon to 6.15.0 (#756, thanks @gmathieu) - support scoped modules containing non word characters - ## [2.2.0] - 2016-11-07 + ### Fixed - Corrected a few gaffs in the auto-ignore logic to fix major performance issues with projects that did not explicitly ignore `node_modules`. ([#654]) @@ -450,6 +496,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - [`prefer-default-export`]: fixed crash on export extensions ([#653]) ## [2.1.0] - 2016-11-02 + ### Added - Add [`no-named-default`] rule: style-guide rule to report use of unnecessarily named default imports ([#596], thanks [@ntdb]) - [`no-extraneous-dependencies`]: check globs against CWD + absolute path ([#602] + [#630], thanks [@ljharb]) @@ -462,10 +509,12 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - Fixed documentation for the default values for the [`order`] rule ([#601]) ## [2.0.1] - 2016-10-06 + ### Fixed - Fixed code that relied on removed dependencies. ([#604]) ## [2.0.0]! - 2016-09-30 + ### Added - [`unambiguous`] rule: report modules that are not unambiguously ES modules. - `recommended` shared config. Roughly `errors` and `warnings` mixed together, @@ -491,6 +540,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - [`no-internal-modules`]: support `@`-scoped packages ([#577]+[#578], thanks [@spalger]) ## [1.16.0] - 2016-09-22 + ### Added - Added [`no-dynamic-require`] rule: forbid `require()` calls with expressions. ([#567], [#568]) - Added [`no-internal-modules`] rule: restrict deep package imports to specific folders. ([#485], thanks [@spalger]!) @@ -501,6 +551,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - [`default`]: allow re-export of values from ignored files as default ([#545], thanks [@skyrpex]) ## [1.15.0] - 2016-09-12 + ### Added - Added an `allow` option to [`no-nodejs-modules`] to allow exceptions ([#452], [#509]). - Added [`no-absolute-path`] rule ([#530], [#538]) @@ -511,6 +562,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - [`no-named-as-default-member`] Allow default import to have a property named "default" ([#507], [#508], thanks [@jquense] for both!) ## [1.14.0] - 2016-08-22 + ### Added - [`import/parsers` setting]: parse some dependencies (i.e. TypeScript!) with a different parser than the ESLint-configured parser. ([#503]) @@ -518,6 +570,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - [`namespace`] exception for get property from `namespace` import, which are re-export from commonjs module ([#499] fixes [#416], thanks [@wKich]) ## [1.13.0] - 2016-08-11 + ### Added - `allowComputed` option for [`namespace`] rule. If set to `true`, won't report computed member references to namespaces. (see [#456]) @@ -530,15 +583,18 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro in an imported file. (fixes [#478], thanks [@rhys-vdw]) ## [1.12.0] - 2016-07-26 + ### Added - [`import/external-module-folders` setting]: a possibility to configure folders for "external" modules ([#444], thanks [@zloirock]) ## [1.11.1] - 2016-07-20 + ### Fixed - [`newline-after-import`] exception for `switch` branches with `require`s iff parsed as `sourceType:'module'`. (still [#441], thanks again [@ljharb]) ## [1.11.0] - 2016-07-17 + ### Added - Added an `peerDependencies` option to [`no-extraneous-dependencies`] to allow/forbid peer dependencies ([#423], [#428], thanks [@jfmengels]!). @@ -547,20 +603,24 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro function expression (e.g. `() => require('a') || require('b')`). ([#441], thanks [@ljharb]) ## [1.10.3] - 2016-07-08 + ### Fixed - removing `Symbol` dependencies (i.e. `for-of` loops) due to Node 0.10 polyfill issue (see [#415]). Should not make any discernible semantic difference. ## [1.10.2] - 2016-07-04 + ### Fixed - Something horrible happened during `npm prepublish` of 1.10.1. Several `rm -rf node_modules && npm i` and `gulp clean && npm prepublish`s later, it is rebuilt and republished as 1.10.2. Thanks [@rhettlivingston] for noticing and reporting! ## [1.10.1] - 2016-07-02 [YANKED] + ### Added - Officially support ESLint 3.x. (peerDependencies updated to `2.x - 3.x`) ## [1.10.0] - 2016-06-30 + ### Added - Added new rule [`no-restricted-paths`]. ([#155]/[#371], thanks [@lo1tuma]) - [`import/core-modules` setting]: allow configuration of additional module names, @@ -571,14 +631,17 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - Fixed crash with `newline-after-import` related to the use of switch cases. (fixes [#386], thanks [@ljharb] for reporting) ([#395]) ## [1.9.2] - 2016-06-21 + ### Fixed - Issues with ignored/CJS files in [`export`] and [`no-deprecated`] rules. ([#348], [#370]) ## [1.9.1] - 2016-06-16 + ### Fixed - Reordered precedence for loading resolvers. ([#373]) ## [1.9.0] - 2016-06-10 + ### Added - Added support TomDoc comments to [`no-deprecated`]. ([#321], thanks [@josh]) - Added support for loading custom resolvers ([#314], thanks [@le0nik]) @@ -587,6 +650,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - [`prefer-default-export`] handles `export function` and `export const` in same file ([#359], thanks [@scottnonnenberg]) ## [1.8.1] - 2016-05-23 + ### Fixed - `export * from 'foo'` now properly ignores a `default` export from `foo`, if any. ([#328]/[#332], thanks [@jkimbo]) This impacts all static analysis of imported names. ([`default`], [`named`], [`namespace`], [`export`]) @@ -596,6 +660,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - [`prefer-default-export`] properly handles deep destructuring, `export * from ...`, and files with no exports. ([#342]+[#343], thanks [@scottnonnenberg]) ## [1.8.0] - 2016-05-11 + ### Added - [`prefer-default-export`], new rule. ([#308], thanks [@gavriguy]) @@ -604,6 +669,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - Make [`no-extraneous-dependencies`] handle scoped packages ([#316], thanks [@jfmengels]) ## [1.7.0] - 2016-05-06 + ### Added - [`newline-after-import`], new rule. ([#245], thanks [@singles]) - Added an `optionalDependencies` option to [`no-extraneous-dependencies`] to allow/forbid optional dependencies ([#266], thanks [@jfmengels]). @@ -618,6 +684,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro module is not resolved. Also, never report for builtins (i.e. `path`). ([#296]) ## [1.6.1] - 2016-04-28 + ### Fixed - [`no-named-as-default-member`]: don't crash on rest props. ([#281], thanks [@SimenB]) - support for Node 6: don't pass `null` to `path` functions. @@ -625,6 +692,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro config ([#288]). ## [1.6.0] - 2016-04-25 + ### Added - add [`no-named-as-default-member`] to `warnings` canned config - add [`no-extraneous-dependencies`] rule ([#241], thanks [@jfmengels]) @@ -643,6 +711,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - [`no-named-as-default-member`] had a crash on destructuring in loops (thanks for heads up from [@lemonmade]) ## [1.5.0] - 2016-04-18 + ### Added - report resolver errors at the top of the linted file - add [`no-namespace`] rule ([#239], thanks [@singles]) @@ -655,6 +724,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - support for Node 0.10, via `es6-*` ponyfills. Using native Map/Set/Symbol. ## [1.4.0] - 2016-03-25 + ### Added - Resolver plugin interface v2: more explicit response format that more clearly covers the found-but-core-module case, where there is no path. Still backwards-compatible with the original version of the resolver spec. @@ -665,6 +735,7 @@ Yanked due to critical issue in eslint-module-utils with cache key resulting fro - using `es6-*` ponyfills instead of `babel-runtime` ## [1.3.0] - 2016-03-20 + Major perf improvements. Between parsing only once and ignoring gigantic, non-module `node_modules`, there is very little added time. @@ -680,6 +751,7 @@ memoizing parser, and takes only 27s with naked `babel-eslint` (thus, reparsing something that looks like an `export` is detected in the module content. ## [1.2.0] - 2016-03-19 + Thanks [@lencioni] for identifying a huge amount of rework in resolve and kicking off a bunch of memoization. @@ -690,6 +762,7 @@ I'm seeing 62% improvement over my normal test codebase when executing only - added caching to core/resolve via [#214], configured via [`import/cache` setting] ## [1.1.0] - 2016-03-15 + ### Added - Added an [`ignore`](./docs/rules/no-unresolved.md#ignore) option to [`no-unresolved`] for those pesky files that no resolver can find. (still prefer enhancing the Webpack and Node resolvers to using it, though). See [#89] for details. @@ -741,6 +814,7 @@ I'm seeing 62% improvement over my normal test codebase when executing only - `babylon` as default import parser (see Breaking) ## [0.13.0] - 2016-02-08 + ### Added - [`no-commonjs`] rule - [`no-amd`] rule @@ -749,17 +823,21 @@ I'm seeing 62% improvement over my normal test codebase when executing only - Removed vestigial `no-require` rule. [`no-commonjs`] is more complete. ## [0.12.2] - 2016-02-06 [YANKED] + Unpublished from npm and re-released as 0.13.0. See [#170]. ## [0.12.1] - 2015-12-17 + ### Changed - Broke docs for rules out into individual files. ## [0.12.0] - 2015-12-14 + ### Changed - Ignore [`import/ignore` setting] if exports are actually found in the parsed module. Does this to support use of `jsnext:main` in `node_modules` without the pain of managing an allow list or a nuanced deny list. ## [0.11.0] - 2015-11-27 + ### Added - Resolver plugins. Now the linter can read Webpack config, properly follow aliases and ignore externals, dismisses inline loaders, etc. etc.! @@ -822,11 +900,15 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2196]: https://github.com/import-js/eslint-plugin-import/pull/2196 +[#2184]: https://github.com/import-js/eslint-plugin-import/pull/2184 [#2179]: https://github.com/import-js/eslint-plugin-import/pull/2179 [#2160]: https://github.com/import-js/eslint-plugin-import/pull/2160 [#2158]: https://github.com/import-js/eslint-plugin-import/pull/2158 [#2156]: https://github.com/import-js/eslint-plugin-import/pull/2156 +[#2149]: https://github.com/benmosher/eslint-plugin-import/pull/2149 [#2146]: https://github.com/import-js/eslint-plugin-import/pull/2146 +[#2140]: https://github.com/import-js/eslint-plugin-import/pull/2140 [#2138]: https://github.com/import-js/eslint-plugin-import/pull/2138 [#2121]: https://github.com/import-js/eslint-plugin-import/pull/2121 [#2112]: https://github.com/import-js/eslint-plugin-import/pull/2112 @@ -1074,10 +1156,14 @@ for info on changes for earlier releases. [#211]: https://github.com/import-js/eslint-plugin-import/pull/211 [#164]: https://github.com/import-js/eslint-plugin-import/pull/164 [#157]: https://github.com/import-js/eslint-plugin-import/pull/157 +[#2201]: https://github.com/import-js/eslint-plugin-import/issues/2201 +[#2199]: https://github.com/import-js/eslint-plugin-import/issues/2199 +[#2161]: https://github.com/import-js/eslint-plugin-import/issues/2161 [#2118]: https://github.com/import-js/eslint-plugin-import/issues/2118 [#2067]: https://github.com/import-js/eslint-plugin-import/issues/2067 [#2056]: https://github.com/import-js/eslint-plugin-import/issues/2056 [#2063]: https://github.com/import-js/eslint-plugin-import/issues/2063 +[#1998]: https://github.com/import-js/eslint-plugin-import/issues/1998 [#1965]: https://github.com/import-js/eslint-plugin-import/issues/1965 [#1924]: https://github.com/import-js/eslint-plugin-import/issues/1924 [#1854]: https://github.com/import-js/eslint-plugin-import/issues/1854 @@ -1181,7 +1267,9 @@ for info on changes for earlier releases. [#119]: https://github.com/import-js/eslint-plugin-import/issues/119 [#89]: https://github.com/import-js/eslint-plugin-import/issues/89 -[Unreleased]: https://github.com/import-js/eslint-plugin-import/compare/v2.24.0...HEAD +[Unreleased]: https://github.com/import-js/eslint-plugin-import/compare/v2.24.2...HEAD +[2.24.2]: https://github.com/import-js/eslint-plugin-import/compare/v2.24.1...v2.24.2 +[2.24.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.24.0...v2.24.1 [2.24.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.23.4...v2.24.0 [2.23.4]: https://github.com/import-js/eslint-plugin-import/compare/v2.23.3...v2.23.4 [2.23.3]: https://github.com/import-js/eslint-plugin-import/compare/v2.23.2...v2.23.3 @@ -1330,10 +1418,12 @@ for info on changes for earlier releases. [@gavriguy]: https://github.com/gavriguy [@giodamelio]: https://github.com/giodamelio [@golopot]: https://github.com/golopot +[@GoodForOneFare]: https://github.com/GoodForOneFare [@graingert]: https://github.com/graingert [@grit96]: https://github.com/grit96 [@guillaumewuip]: https://github.com/guillaumewuip [@hayes]: https://github.com/hayes +[@himynameisdave]: https://github.com/himynameisdave [@hulkish]: https://github.com/hulkish [@Hypnosphi]: https://github.com/Hypnosphi [@isiahmeadows]: https://github.com/isiahmeadows @@ -1362,8 +1452,8 @@ for info on changes for earlier releases. [@kiwka]: https://github.com/kiwka [@klimashkin]: https://github.com/klimashkin [@kmui2]: https://github.com/kmui2 -[@KostyaZgara]: https://github.com/KostyaZgara [@knpwrs]: https://github.com/knpwrs +[@KostyaZgara]: https://github.com/KostyaZgara [@laysent]: https://github.com/laysent [@le0nik]: https://github.com/le0nik [@lemonmade]: https://github.com/lemonmade @@ -1396,6 +1486,7 @@ for info on changes for earlier releases. [@nicolashenry]: https://github.com/nicolashenry [@noelebrun]: https://github.com/noelebrun [@ntdb]: https://github.com/ntdb +[@nwalters512]: https://github.com/nwalters512 [@panrafal]: https://github.com/panrafal [@paztis]: https://github.com/paztis [@pcorpet]: https://github.com/pcorpet @@ -1458,4 +1549,4 @@ for info on changes for earlier releases. [@wtgtybhertgeghgtwtg]: https://github.com/wtgtybhertgeghgtwtg [@xpl]: https://github.com/xpl [@yordis]: https://github.com/yordis -[@zloirock]: https://github.com/zloirock \ No newline at end of file +[@zloirock]: https://github.com/zloirock diff --git a/README.md b/README.md index 964c123a7e..d800cbfe57 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # eslint-plugin-import -[![build status](https://travis-ci.org/import-js/eslint-plugin-import.svg?branch=master)](https://travis-ci.org/import-js/eslint-plugin-import) -[![Coverage Status](https://coveralls.io/repos/github/import-js/eslint-plugin-import/badge.svg?branch=master)](https://coveralls.io/github/import-js/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/import-js/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) @@ -171,7 +172,7 @@ 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 # Resolvers @@ -510,3 +511,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 diff --git a/RELEASE.md b/RELEASE.md index 34ef1aadc6..6c048dc093 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -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) @@ -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! diff --git a/docs/rules/extensions.md b/docs/rules/extensions.md index bf29dbe5fb..575895c5a0 100644 --- a/docs/rules/extensions.md +++ b/docs/rules/extensions.md @@ -109,6 +109,8 @@ import foo from './foo'; import bar from './bar'; import Component from './Component'; + +import foo from '@/foo'; ``` The following patterns are not considered problems when configuration set to "always": @@ -121,6 +123,8 @@ import bar from './bar.json'; import Component from './Component.jsx'; import * as path from 'path'; + +import foo from '@/foo.js'; ``` The following patterns are considered problems when configuration set to "ignorePackages": @@ -145,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} ]`: @@ -156,6 +161,7 @@ import baz from 'foo/baz.js'; import express from 'express'; +import foo from '@/foo'; ``` ## When Not To Use It diff --git a/docs/rules/imports-first.md b/docs/rules/imports-first.md index e4cdda1151..7dadffa684 100644 --- a/docs/rules/imports-first.md +++ b/docs/rules/imports-first.md @@ -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/import-js/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). diff --git a/docs/rules/max-dependencies.md b/docs/rules/max-dependencies.md index 20d29cf0e9..3bead8273d 100644 --- a/docs/rules/max-dependencies.md +++ b/docs/rules/max-dependencies.md @@ -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. diff --git a/docs/rules/no-unresolved.md b/docs/rules/no-unresolved.md index 30cd8cb2b8..ae6177dfd9 100644 --- a/docs/rules/no-unresolved.md +++ b/docs/rules/no-unresolved.md @@ -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. diff --git a/docs/rules/unambiguous.md b/docs/rules/unambiguous.md index 7955c3fbc8..92839b0c35 100644 --- a/docs/rules/unambiguous.md +++ b/docs/rules/unambiguous.md @@ -51,4 +51,4 @@ a `module`. - [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 +[Unambiguous JavaScript Grammar]: https://github.com/nodejs/node-eps/blob/HEAD/002-es-modules.md#32-determining-if-source-is-an-es-module diff --git a/package.json b/package.json index e3bc5a8d96..dd7cbcb350 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-import", - "version": "2.24.0", + "version": "2.24.2", "description": "Import with sanity.", "engines": { "node": ">=4" @@ -56,7 +56,7 @@ "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", + "@typescript-eslint/parser": "^2.23.0 || ^3.3.0 || ^4.29.3", "array.prototype.flatmap": "^1.2.4", "babel-cli": "^6.26.0", "babel-core": "^6.26.3", @@ -69,7 +69,7 @@ "babel-register": "^6.26.0", "babylon": "^6.18.0", "chai": "^4.3.4", - "coveralls": "^3.1.0", + "coveralls": "^3.1.1", "cross-env": "^4.0.0", "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0", "eslint-import-resolver-node": "file:./resolvers/node", @@ -104,16 +104,16 @@ "array.prototype.flat": "^1.2.4", "debug": "^2.6.9", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.5", + "eslint-import-resolver-node": "^0.3.6", "eslint-module-utils": "^2.6.2", "find-up": "^2.0.0", "has": "^1.0.3", - "is-core-module": "^2.4.0", + "is-core-module": "^2.6.0", "minimatch": "^3.0.4", - "object.values": "^1.1.3", + "object.values": "^1.1.4", "pkg-up": "^2.0.0", "read-pkg-up": "^3.0.0", "resolve": "^1.20.0", - "tsconfig-paths": "^3.9.0" + "tsconfig-paths": "^3.11.0" } } diff --git a/resolvers/node/CHANGELOG.md b/resolvers/node/CHANGELOG.md index 2fbf07ab21..c237855a2e 100644 --- a/resolvers/node/CHANGELOG.md +++ b/resolvers/node/CHANGELOG.md @@ -5,6 +5,10 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ## 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]) @@ -52,13 +56,13 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ### Fixed - find files with `.json` extensions (#333, thanks for noticing @jfmengels) +[#2186]: https://github.com/import-js/eslint-plugin-import/issues/2186 [#2166]: https://github.com/import-js/eslint-plugin-import/pull/2166 -[#438]: https://github.com/import-js/eslint-plugin-import/pull/438 - [#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 @@ -66,4 +70,4 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel [@SkeLLLa]: https://github.com/SkeLLLa [@ljharb]: https://github.com/ljharb [@opichals]: https://github.com/opichals -[@MustafaHaddara]: https://github.com/MustafaHaddara \ No newline at end of file +[@MustafaHaddara]: https://github.com/MustafaHaddara diff --git a/resolvers/node/index.js b/resolvers/node/index.js index 84fc5076b6..899e552e96 100644 --- a/resolvers/node/index.js +++ b/resolvers/node/index.js @@ -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,16 +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) { +function identity(x) { return x; } + +function packageFilter(pkg, dir, config) { + let found = false; + const file = path.join(dir, 'dummy.js'); if (pkg.module) { - pkg.main = pkg.module; - } else if (pkg['jsnext:main']) { - pkg.main = pkg['jsnext:main']; + 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; } diff --git a/resolvers/node/package.json b/resolvers/node/package.json index 993f1b89b6..5e7e9dea75 100644 --- a/resolvers/node/package.json +++ b/resolvers/node/package.json @@ -1,6 +1,6 @@ { "name": "eslint-import-resolver-node", - "version": "0.3.5", + "version": "0.3.6", "description": "Node default behavior import resolution plugin for eslint-plugin-import.", "main": "index.js", "files": [ diff --git a/resolvers/node/test/package-mains/module-broken/main.js b/resolvers/node/test/package-mains/module-broken/main.js new file mode 100644 index 0000000000..f053ebf797 --- /dev/null +++ b/resolvers/node/test/package-mains/module-broken/main.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/resolvers/node/test/package-mains/module-broken/package.json b/resolvers/node/test/package-mains/module-broken/package.json new file mode 100644 index 0000000000..36a3183866 --- /dev/null +++ b/resolvers/node/test/package-mains/module-broken/package.json @@ -0,0 +1,4 @@ +{ + "main": "./main.js", + "module": "./doesNotExist.js" +} diff --git a/resolvers/node/test/packageMains.js b/resolvers/node/test/packageMains.js index 0a7d9463c7..caac6221ca 100644 --- a/resolvers/node/test/packageMains.js +++ b/resolvers/node/test/packageMains.js @@ -4,24 +4,29 @@ 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(webpack.resolve('./module-and-jsnext', file)).property('path') + expect(resolver.resolve('./module-and-jsnext', file)).property('path') .to.equal(path.join(__dirname, 'package-mains', 'module-and-jsnext', 'src', 'index.js')); }); + + it('falls back from a missing "module" to "main"', function () { + expect(resolver.resolve('./module-broken', file)).property('path') + .to.equal(path.join(__dirname, 'package-mains', 'module-broken', 'main.js')); + }); }); diff --git a/resolvers/webpack/index.js b/resolvers/webpack/index.js index 50832f8f31..c7a3fbcae0 100644 --- a/resolvers/webpack/index.js +++ b/resolvers/webpack/index.js @@ -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; }); @@ -311,16 +310,16 @@ function createWebpack1ResolveSync(webpackRequire, resolveConfig, plugins) { /* 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 */ @@ -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); } } diff --git a/resolvers/webpack/package.json b/resolvers/webpack/package.json index 32650c07c2..fd805af8b7 100644 --- a/resolvers/webpack/package.json +++ b/resolvers/webpack/package.json @@ -30,7 +30,7 @@ "bugs": { "url": "https://github.com/import-js/eslint-plugin-import/issues" }, - "homepage": "https://github.com/import-js/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", @@ -38,8 +38,8 @@ "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.6.0", + "is-regex": "^1.1.4", "lodash": "^4.17.21", "resolve": "^1.20.0", "semver": "^5.7.1" diff --git a/resolvers/webpack/test/package-mains/module-broken/main.js b/resolvers/webpack/test/package-mains/module-broken/main.js new file mode 100644 index 0000000000..f053ebf797 --- /dev/null +++ b/resolvers/webpack/test/package-mains/module-broken/main.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/resolvers/webpack/test/package-mains/module-broken/package.json b/resolvers/webpack/test/package-mains/module-broken/package.json new file mode 100644 index 0000000000..36a3183866 --- /dev/null +++ b/resolvers/webpack/test/package-mains/module-broken/package.json @@ -0,0 +1,4 @@ +{ + "main": "./main.js", + "module": "./doesNotExist.js" +} diff --git a/resolvers/webpack/test/packageMains.js b/resolvers/webpack/test/packageMains.js index 96becbc8ce..fef3dde073 100644 --- a/resolvers/webpack/test/packageMains.js +++ b/resolvers/webpack/test/packageMains.js @@ -4,7 +4,7 @@ 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'); @@ -12,43 +12,47 @@ 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(webpack.resolve('./module-and-jsnext', file)).property('path') + 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')); }); - }); diff --git a/src/ExportMap.js b/src/ExportMap.js index 76b07f9dc9..5bda83dd35 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -351,6 +351,7 @@ ExportMap.for = function (context) { ExportMap.parse = function (path, content, context) { const m = new ExportMap(path); + const isEsModuleInteropTrue = isEsModuleInterop(); let ast; try { @@ -413,6 +414,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; @@ -488,6 +522,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 +582,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 +613,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 +637,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 +655,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; }; diff --git a/src/rules/dynamic-import-chunkname.js b/src/rules/dynamic-import-chunkname.js index 7a21ec62d9..16cd85a4f8 100644 --- a/src/rules/dynamic-import-chunkname.js +++ b/src/rules/dynamic-import-chunkname.js @@ -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({ diff --git a/src/rules/first.js b/src/rules/first.js index a3b7f24e03..93e9fc5536 100644 --- a/src/rules/first.js +++ b/src/rules/first.js @@ -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; } diff --git a/src/rules/namespace.js b/src/rules/namespace.js index a23cfeac88..74ee9dae3c 100644 --- a/src/rules/namespace.js +++ b/src/rules/namespace.js @@ -60,7 +60,7 @@ 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); @@ -69,7 +69,7 @@ module.exports = { case 'ImportSpecifier': { const meta = imports.get( // default to 'default' for default http://i.imgur.com/nj6qAWy.jpg - specifier.imported ? specifier.imported.name : 'default' + 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; } diff --git a/src/rules/no-commonjs.js b/src/rules/no-commonjs.js index 08d29a0cdb..98f759cbc2 100644 --- a/src/rules/no-commonjs.js +++ b/src/rules/no-commonjs.js @@ -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' diff --git a/src/rules/no-duplicates.js b/src/rules/no-duplicates.js index 43a29506be..6e03fcaef0 100644 --- a/src/rules/no-duplicates.js +++ b/src/rules/no-duplicates.js @@ -276,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); @@ -291,10 +300,11 @@ module.exports = { } }, - 'Program:exit': function () { + 'Program:exit'() { checkImports(imported, context); checkImports(nsImported, context); - checkImports(typesImported, context); + checkImports(defaultTypesImported, context); + checkImports(namedTypesImported, context); }, }; }, diff --git a/src/rules/no-unresolved.js b/src/rules/no-unresolved.js index 61dc0b6c70..719bbded9d 100644 --- a/src/rules/no-unresolved.js +++ b/src/rules/no-unresolved.js @@ -15,35 +15,36 @@ module.exports = { url: docsUrl('no-unresolved'), }, - schema: [ makeOptionsSchema({ - caseSensitive: { type: 'boolean', default: true }, - })], + schema: [ + makeOptionsSchema({ + caseSensitive: { type: 'boolean', default: true }, + }), + ], }, create: function (context) { - function checkSourceValue(source) { - const shouldCheckCase = !CASE_SENSITIVE_FS && - (!context.options[0] || context.options[0].caseSensitive !== false); + const shouldCheckCase = !CASE_SENSITIVE_FS + && (!context.options[0] || context.options[0].caseSensitive !== false); const resolvedPath = resolve(source.value, context); if (resolvedPath === undefined) { - context.report(source, - `Unable to resolve path to module '${source.value}'.`); - } - - else if (shouldCheckCase) { + context.report( + source, + `Unable to resolve path to module '${source.value}'.` + ); + } else if (shouldCheckCase) { const cacheSettings = ModuleCache.getSettings(context.settings); if (!fileExistsWithCaseSync(resolvedPath, cacheSettings)) { - context.report(source, - `Casing of ${source.value} does not match the underlying filesystem.`); + context.report( + source, + `Casing of ${source.value} does not match the underlying filesystem.` + ); } - } } return moduleVisitor(checkSourceValue, context.options[0]); - }, }; diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index 205259eef9..d49674e7e5 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -540,7 +540,7 @@ module.exports = { const value = exportsKey === IMPORT_DEFAULT_SPECIFIER ? DEFAULT : exportsKey; - if (typeof exportStatement !== 'undefined'){ + if (typeof exportStatement !== 'undefined') { if (exportStatement.whereUsed.size < 1) { context.report( node, diff --git a/src/rules/order.js b/src/rules/order.js index ce34604c64..857a46e3d0 100644 --- a/src/rules/order.js +++ b/src/rules/order.js @@ -47,8 +47,7 @@ function takeTokensAfterWhile(sourceCode, node, condition) { for (let i = 0; i < tokens.length; i++) { if (condition(tokens[i])) { result.push(tokens[i]); - } - else { + } else { break; } } @@ -61,8 +60,7 @@ function takeTokensBeforeWhile(sourceCode, node, condition) { for (let i = tokens.length - 1; i >= 0; i--) { if (condition(tokens[i])) { result.push(tokens[i]); - } - else { + } else { break; } } diff --git a/src/rules/prefer-default-export.js b/src/rules/prefer-default-export.js index 5e77126dc0..3cd2224cdf 100644 --- a/src/rules/prefer-default-export.js +++ b/src/rules/prefer-default-export.js @@ -69,8 +69,7 @@ module.exports = { node.declaration.declarations.forEach(function(declaration) { captureDeclaration(declaration.id); }); - } - else { + } else { // captures 'export function foo() {}' syntax specifierExportCount++; } diff --git a/tests/dep-time-travel.sh b/tests/dep-time-travel.sh index ad00568e4c..d6f5aedfc2 100755 --- a/tests/dep-time-travel.sh +++ b/tests/dep-time-travel.sh @@ -8,10 +8,16 @@ export NPM_CONFIG_LEGACY_PEER_DEPS=true npm install --no-save "eslint@${ESLINT_VERSION}" --ignore-scripts -# completely remove the new TypeScript parser for ESLint < v5 -if [[ "$ESLINT_VERSION" -lt "5" ]]; then + + +if [[ -n "$TS_PARSER" ]]; then # if TS parser is manually set, always use it + echo "Downgrading @typescript-eslint/parser..." + npm i --no-save "@typescript-eslint/parser@${TS_PARSER}" +elif [[ "$ESLINT_VERSION" -lt "5" ]]; then # completely remove the new TypeScript parser for ESLint < v5 echo "Removing @typescript-eslint/parser..." npm uninstall --no-save @typescript-eslint/parser +elif [[ "$TRAVIS_NODE_VERSION" -lt "10" ]]; then # TS parser 3 requires node 10+ + npm i --no-save "@typescript-eslint/parser@3" fi # use these alternate TypeScript dependencies for ESLint < v4 @@ -28,8 +34,3 @@ if [[ "$TRAVIS_NODE_VERSION" -lt "8" ]]; then echo "Downgrading eslint-import-resolver-typescript..." npm i --no-save eslint-import-resolver-typescript@1.0.2 fi - -if [[ -n "$TS_PARSER" ]]; then - echo "Downgrading @typescript-eslint/parser..." - npm i --no-save @typescript-eslint/parser@2 -fi diff --git a/tests/files/export-star-2/middle.js b/tests/files/export-star-2/middle.js new file mode 100644 index 0000000000..2fc07cd9a3 --- /dev/null +++ b/tests/files/export-star-2/middle.js @@ -0,0 +1 @@ +export * as myName from './upstream'; diff --git a/tests/files/export-star-2/upstream.js b/tests/files/export-star-2/upstream.js new file mode 100644 index 0000000000..cc798ff50d --- /dev/null +++ b/tests/files/export-star-2/upstream.js @@ -0,0 +1 @@ +export const a = 1; diff --git a/tests/files/export-star-3/b.ts b/tests/files/export-star-3/b.ts new file mode 100644 index 0000000000..5a91d016fb --- /dev/null +++ b/tests/files/export-star-3/b.ts @@ -0,0 +1 @@ +export * as b from './c'; diff --git a/tests/files/export-star-3/c.ts b/tests/files/export-star-3/c.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/files/export-star/extfield.js b/tests/files/export-star/extfield.js new file mode 100644 index 0000000000..7a4e8a723a --- /dev/null +++ b/tests/files/export-star/extfield.js @@ -0,0 +1 @@ +export default 42; diff --git a/tests/files/export-star/extfield2.js b/tests/files/export-star/extfield2.js new file mode 100644 index 0000000000..5f2610afc7 --- /dev/null +++ b/tests/files/export-star/extfield2.js @@ -0,0 +1 @@ +export default NaN; diff --git a/tests/files/export-star/models.js b/tests/files/export-star/models.js new file mode 100644 index 0000000000..1091770f7f --- /dev/null +++ b/tests/files/export-star/models.js @@ -0,0 +1,2 @@ +export * as ExtfieldModel from './extfield'; +export * as Extfield2Model from './extfield2'; diff --git a/tests/files/foo-bar-resolver-no-version.js b/tests/files/foo-bar-resolver-no-version.js index 89d4eb13e2..f00198562e 100644 --- a/tests/files/foo-bar-resolver-no-version.js +++ b/tests/files/foo-bar-resolver-no-version.js @@ -5,10 +5,8 @@ exports.resolveImport = function (modulePath, sourceFile, config) { if (sourceFileName === 'foo.js') { return path.join(__dirname, 'bar.jsx') } - else if (sourceFileName === 'exception.js') { + if (sourceFileName === 'exception.js') { throw new Error('foo-bar-resolver-v1 resolveImport test exception') } - else { - return undefined - } + return undefined; } diff --git a/tests/files/foo-bar-resolver-v1.js b/tests/files/foo-bar-resolver-v1.js index 8aafe7aa45..af9da1b7a6 100644 --- a/tests/files/foo-bar-resolver-v1.js +++ b/tests/files/foo-bar-resolver-v1.js @@ -3,14 +3,12 @@ var path = require('path') exports.resolveImport = function (modulePath, sourceFile, config) { var sourceFileName = path.basename(sourceFile) if (sourceFileName === 'foo.js') { - return path.join(__dirname, 'bar.jsx') + return path.join(__dirname, 'bar.jsx'); } - else if (sourceFileName === 'exception.js') { - throw new Error('foo-bar-resolver-v1 resolveImport test exception') + if (sourceFileName === 'exception.js') { + throw new Error('foo-bar-resolver-v1 resolveImport test exception'); } - else { - return undefined - } -} + return undefined; +}; -exports.interfaceVersion = 1 +exports.interfaceVersion = 1; diff --git a/tests/files/foo-bar-resolver-v2.js b/tests/files/foo-bar-resolver-v2.js index 9bb68171b7..7f8bcc0f86 100644 --- a/tests/files/foo-bar-resolver-v2.js +++ b/tests/files/foo-bar-resolver-v2.js @@ -5,12 +5,10 @@ exports.resolve = function (modulePath, sourceFile, config) { if (sourceFileName === 'foo.js') { return { found: true, path: path.join(__dirname, 'bar.jsx') } } - else if (sourceFileName === 'exception.js') { + if (sourceFileName === 'exception.js') { throw new Error('foo-bar-resolver-v2 resolve test exception') } - else { - return { found: false } - } -} + return { found: false }; +}; -exports.interfaceVersion = 2 +exports.interfaceVersion = 2; diff --git a/tests/files/typescript-export-react-test-renderer/index.d.ts b/tests/files/typescript-export-react-test-renderer/index.d.ts new file mode 100644 index 0000000000..ff70c71354 --- /dev/null +++ b/tests/files/typescript-export-react-test-renderer/index.d.ts @@ -0,0 +1,19 @@ +// case from @types/react-test-renderer + +export {}; + +export interface ReactTestRendererJSON { + type: string; + props: { [propName: string]: any }; + children: null | ReactTestRendererNode[]; +} +export type ReactTestRendererNode = ReactTestRendererJSON | string; +export interface ReactTestRendererTree extends ReactTestRendererJSON { + nodeType: 'component' | 'host'; + instance: any; + rendered: null | ReactTestRendererTree | ReactTestRendererTree[]; +} + +export function create(nextElement: any, options?: any): any; + +export function act(callback: () => Promise): Promise; diff --git a/tests/files/typescript-export-react-test-renderer/tsconfig.json b/tests/files/typescript-export-react-test-renderer/tsconfig.json new file mode 100644 index 0000000000..a72ee3e88b --- /dev/null +++ b/tests/files/typescript-export-react-test-renderer/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "esModuleInterop": true + } +} diff --git a/tests/files/typescript.ts b/tests/files/typescript.ts index 7f90314e45..f8fe2e8e07 100644 --- a/tests/files/typescript.ts +++ b/tests/files/typescript.ts @@ -23,14 +23,14 @@ export function getFoo() : MyType { } export module MyModule { - export function ModuleFunction(){} + export function ModuleFunction() {} } export namespace MyNamespace { - export function NamespaceFunction(){} + export function NamespaceFunction() {} export module NSModule { - export function NSModuleFunction(){} + export function NSModuleFunction() {} } } diff --git a/tests/src/cli.js b/tests/src/cli.js index 91d3dfc158..b0285593b1 100644 --- a/tests/src/cli.js +++ b/tests/src/cli.js @@ -7,6 +7,7 @@ import { expect } from 'chai'; import { CLIEngine } from 'eslint'; import eslintPkg from 'eslint/package.json'; import semver from 'semver'; +import * as importPlugin from '../../src/index'; describe('CLI regression tests', function () { describe('issue #210', function () { @@ -20,6 +21,7 @@ describe('CLI regression tests', function () { 'named': 2, }, }); + cli.addPlugin('eslint-plugin-import', importPlugin); }); it("doesn't throw an error on gratuitous, erroneous self-reference", function () { expect(() => cli.executeOnFiles(['./tests/files/issue210.js'])).not.to.throw(); @@ -38,6 +40,7 @@ describe('CLI regression tests', function () { rulePaths: ['./src/rules'], ignore: false, }); + cli.addPlugin('eslint-plugin-import', importPlugin); } }); diff --git a/tests/src/rules/default.js b/tests/src/rules/default.js index c7eb780d00..a861fff76f 100644 --- a/tests/src/rules/default.js +++ b/tests/src/rules/default.js @@ -220,6 +220,17 @@ context('TypeScript', function () { tsconfigRootDir: path.resolve(__dirname, '../../files/typescript-export-as-default-namespace/'), }, }), + test({ + code: `import Foo from "./typescript-export-react-test-renderer"`, + parser: parser, + settings: { + 'import/parsers': { [parser]: ['.ts'] }, + 'import/resolver': { 'eslint-import-resolver-typescript': true }, + }, + parserOptions: { + tsconfigRootDir: path.resolve(__dirname, '../../files/typescript-export-react-test-renderer/'), + }, + }), test({ code: `import foobar from "./typescript-export-assign-property"`, parser: parser, diff --git a/tests/src/rules/first.js b/tests/src/rules/first.js index 3f7301319d..9d27870677 100644 --- a/tests/src/rules/first.js +++ b/tests/src/rules/first.js @@ -7,60 +7,71 @@ const rule = require('rules/first'); ruleTester.run('first', rule, { valid: [ - test({ code: "import { x } from './foo'; import { y } from './bar';\ - export { x, y }" }), + test({ + code: "import { x } from './foo'; import { y } from './bar';\ + export { x, y }", + }), test({ code: "import { x } from 'foo'; import { y } from './bar'" }), test({ code: "import { x } from './foo'; import { y } from 'bar'" }), - test({ code: "import { x } from './foo'; import { y } from 'bar'", + test({ + code: "import { x } from './foo'; import { y } from 'bar'", options: ['disable-absolute-first'], }), - test({ code: "'use directive';\ - import { x } from 'foo';" }), + test({ + code: "'use directive';\ + import { x } from 'foo';", + }), ], invalid: [ - test({ code: "import { x } from './foo';\ - export { x };\ - import { y } from './bar';", - errors: 1, - output: "import { x } from './foo';\ - import { y } from './bar';\ - export { x };", + test({ + code: "import { x } from './foo';\ + export { x };\ + import { y } from './bar';", + errors: 1, + output: "import { x } from './foo';\ + import { y } from './bar';\ + export { x };", }), - test({ code: "import { x } from './foo';\ - export { x };\ - import { y } from './bar';\ - import { z } from './baz';", - errors: 2, - output: "import { x } from './foo';\ - import { y } from './bar';\ - import { z } from './baz';\ - export { x };", + test({ + code: "import { x } from './foo';\ + export { x };\ + import { y } from './bar';\ + import { z } from './baz';", + errors: 2, + output: "import { x } from './foo';\ + import { y } from './bar';\ + import { z } from './baz';\ + export { x };", }), - test({ code: "import { x } from './foo'; import { y } from 'bar'", + test({ + code: "import { x } from './foo'; import { y } from 'bar'", options: ['absolute-first'], errors: 1, }), - test({ code: "import { x } from 'foo';\ - 'use directive';\ - import { y } from 'bar';", - errors: 1, - output: "import { x } from 'foo';\ - import { y } from 'bar';\ - 'use directive';", + test({ + code: "import { x } from 'foo';\ + 'use directive';\ + import { y } from 'bar';", + errors: 1, + output: "import { x } from 'foo';\ + import { y } from 'bar';\ + 'use directive';", }), - test({ code: "var a = 1;\ - import { y } from './bar';\ - if (true) { x() };\ - import { x } from './foo';\ - import { z } from './baz';", - errors: 3, - output: "import { y } from './bar';\ - var a = 1;\ - if (true) { x() };\ - import { x } from './foo';\ - import { z } from './baz';", + test({ + code: "var a = 1;\ + import { y } from './bar';\ + if (true) { x() };\ + import { x } from './foo';\ + import { z } from './baz';", + errors: 3, + output: "import { y } from './bar';\ + var a = 1;\ + if (true) { x() };\ + import { x } from './foo';\ + import { z } from './baz';", }), - test({ code: "if (true) { console.log(1) }import a from 'b'", + test({ + code: "if (true) { console.log(1) }import a from 'b'", errors: 1, output: "import a from 'b'\nif (true) { console.log(1) }", }), @@ -72,7 +83,7 @@ context('TypeScript', function () { .filter((parser) => parser !== require.resolve('typescript-eslint-parser')) .forEach((parser) => { const parserConfig = { - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -81,35 +92,29 @@ context('TypeScript', function () { ruleTester.run('order', rule, { valid: [ - test( - { - code: ` - import y = require('bar'); - import { x } from 'foo'; - import z = require('baz'); - `, - parser, - }, - parserConfig, - ), + test({ + code: ` + import y = require('bar'); + import { x } from 'foo'; + import z = require('baz'); + `, + ...parserConfig, + }), ], invalid: [ - test( - { - code: ` - import { x } from './foo'; - import y = require('bar'); - `, - options: ['absolute-first'], - parser, - errors: [ - { - message: 'Absolute imports should come before relative imports.', - }, - ], - }, - parserConfig, - ), + test({ + code: ` + import { x } from './foo'; + import y = require('bar'); + `, + options: ['absolute-first'], + ...parserConfig, + errors: [ + { + message: 'Absolute imports should come before relative imports.', + }, + ], + }), ], }); }); diff --git a/tests/src/rules/named.js b/tests/src/rules/named.js index 57e40c91ac..56babfa509 100644 --- a/tests/src/rules/named.js +++ b/tests/src/rules/named.js @@ -1,4 +1,4 @@ -import { test, SYNTAX_CASES, getTSParsers } from '../utils'; +import { test, SYNTAX_CASES, getTSParsers, testFilePath, testVersion } from '../utils'; import { RuleTester } from 'eslint'; import { CASE_SENSITIVE_FS } from 'eslint-module-utils/resolve'; @@ -182,10 +182,18 @@ ruleTester.run('named', rule, { }), ...SYNTAX_CASES, + + ...[].concat(testVersion('>= 6', () => ({ + code: `import { ExtfieldModel, Extfield2Model } from './models';`, + filename: testFilePath('./export-star/downstream.js'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2020, + }, + })) || []), ], invalid: [ - test({ code: 'import { somethingElse } from "./test-module"', errors: [ error('somethingElse', './test-module') ] }), @@ -344,105 +352,91 @@ ruleTester.run('named (export *)', rule, { context('TypeScript', function () { getTSParsers().forEach((parser) => { + const settings = { + 'import/parsers': { [parser]: ['.ts'] }, + 'import/resolver': { 'eslint-import-resolver-typescript': true }, + }; + + const valid = []; + const invalid = [ + test({ + code: `import {a} from './export-star-3/b';`, + filename: testFilePath('./export-star-3/a.js'), + parser: parser, + settings: settings, + }), + ]; + [ 'typescript', 'typescript-declare', 'typescript-export-assign-namespace', 'typescript-export-assign-namespace-merged', ].forEach((source) => { - ruleTester.run(`named`, rule, { - valid: [ - test({ - code: `import { MyType } from "./${source}"`, - parser: parser, - settings: { - 'import/parsers': { [parser]: ['.ts'] }, - 'import/resolver': { 'eslint-import-resolver-typescript': true }, - }, - }), - test({ - code: `import { Foo } from "./${source}"`, - parser: parser, - settings: { - 'import/parsers': { [parser]: ['.ts'] }, - 'import/resolver': { 'eslint-import-resolver-typescript': true }, - }, - }), - test({ - code: `import { Bar } from "./${source}"`, - parser: parser, - settings: { - 'import/parsers': { [parser]: ['.ts'] }, - 'import/resolver': { 'eslint-import-resolver-typescript': true }, - }, - }), - test({ - code: `import { getFoo } from "./${source}"`, - parser: parser, - settings: { - 'import/parsers': { [parser]: ['.ts'] }, - 'import/resolver': { 'eslint-import-resolver-typescript': true }, - }, - }), - test({ - code: `import { MyEnum } from "./${source}"`, - parser: parser, - settings: { - 'import/parsers': { [parser]: ['.ts'] }, - 'import/resolver': { 'eslint-import-resolver-typescript': true }, - }, - }), - test({ - code: ` + valid.push( + test({ + code: `import { MyType } from "./${source}"`, + parser: parser, + settings: settings, + }), + test({ + code: `import { Foo } from "./${source}"`, + parser: parser, + settings: settings, + }), + test({ + code: `import { Bar } from "./${source}"`, + parser: parser, + settings: settings, + }), + test({ + code: `import { getFoo } from "./${source}"`, + parser: parser, + settings: settings, + }), + test({ + code: `import { MyEnum } from "./${source}"`, + parser: parser, + settings: settings, + }), + test({ + code: ` import { MyModule } from "./${source}" MyModule.ModuleFunction() `, - parser: parser, - settings: { - 'import/parsers': { [parser]: ['.ts'] }, - 'import/resolver': { 'eslint-import-resolver-typescript': true }, - }, - }), - test({ - code: ` + parser: parser, + settings: settings, + }), + test({ + code: ` import { MyNamespace } from "./${source}" MyNamespace.NSModule.NSModuleFunction() `, - parser: parser, - settings: { - 'import/parsers': { [parser]: ['.ts'] }, - 'import/resolver': { 'eslint-import-resolver-typescript': true }, - }, - }), - ], - - invalid: [ - test({ - code: `import { MissingType } from "./${source}"`, - parser: parser, - settings: { - 'import/parsers': { [parser]: ['.ts'] }, - 'import/resolver': { 'eslint-import-resolver-typescript': true }, - }, - errors: [{ - message: `MissingType not found in './${source}'`, - type: 'Identifier', - }], - }), - test({ - code: `import { NotExported } from "./${source}"`, - parser: parser, - settings: { - 'import/parsers': { [parser]: ['.ts'] }, - 'import/resolver': { 'eslint-import-resolver-typescript': true }, - }, - errors: [{ - message: `NotExported not found in './${source}'`, - type: 'Identifier', - }], - }), - ], - }); + parser: parser, + settings: settings, + }), + ); + + invalid.push( + test({ + code: `import { MissingType } from "./${source}"`, + parser: parser, + settings: settings, + errors: [{ + message: `MissingType not found in './${source}'`, + type: 'Identifier', + }], + }), + test({ + code: `import { NotExported } from "./${source}"`, + parser: parser, + settings: settings, + errors: [{ + message: `NotExported not found in './${source}'`, + type: 'Identifier', + }], + }), + ); }); }); }); diff --git a/tests/src/rules/namespace.js b/tests/src/rules/namespace.js index ce55bccc35..802d5b440b 100644 --- a/tests/src/rules/namespace.js +++ b/tests/src/rules/namespace.js @@ -1,4 +1,4 @@ -import { test, SYNTAX_CASES, getTSParsers } from '../utils'; +import { test, SYNTAX_CASES, getTSParsers, testVersion, testFilePath } from '../utils'; import { RuleTester } from 'eslint'; import flatMap from 'array.prototype.flatmap'; @@ -172,6 +172,18 @@ const valid = [ export const getExampleColor = () => color.example `, }), + + ...[].concat(testVersion('>= 6', () => ({ + code: ` + import * as middle from './middle'; + + console.log(middle.myName); + `, + filename: testFilePath('export-star-2/downstream.js'), + parserOptions: { + ecmaVersion: 2020, + }, + })) || []), ]; const invalid = [ diff --git a/tests/src/rules/newline-after-import.js b/tests/src/rules/newline-after-import.js index 867a648575..39f4c42914 100644 --- a/tests/src/rules/newline-after-import.js +++ b/tests/src/rules/newline-after-import.js @@ -26,7 +26,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { code: `const x = () => require('baz') && require('bar')`, parserOptions: { ecmaVersion: 6 } , }, - `function x(){ require('baz'); }`, + `function x() { require('baz'); }`, `a(require('b'), require('c'), require('d'));`, `function foo() { switch (renderData.modalViewKey) { diff --git a/tests/src/rules/no-amd.js b/tests/src/rules/no-amd.js index 74c89c4116..91e29234c8 100644 --- a/tests/src/rules/no-amd.js +++ b/tests/src/rules/no-amd.js @@ -2,7 +2,7 @@ import { RuleTester } from 'eslint'; import eslintPkg from 'eslint/package.json'; import semver from 'semver'; -const ruleTester = new RuleTester(); +const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015, sourceType: 'module' } }); ruleTester.run('no-amd', require('rules/no-amd'), { valid: [ diff --git a/tests/src/rules/no-commonjs.js b/tests/src/rules/no-commonjs.js index b1d8c03c1d..a2e3464ca7 100644 --- a/tests/src/rules/no-commonjs.js +++ b/tests/src/rules/no-commonjs.js @@ -5,7 +5,7 @@ import semver from 'semver'; const EXPORT_MESSAGE = 'Expected "export" or "export default"'; const IMPORT_MESSAGE = 'Expected "import" instead of "require()"'; -const ruleTester = new RuleTester(); +const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015, sourceType: 'module' } }); ruleTester.run('no-commonjs', require('rules/no-commonjs'), { valid: [ diff --git a/tests/src/rules/no-cycle.js b/tests/src/rules/no-cycle.js index 11fc515f0b..c4e3235e78 100644 --- a/tests/src/rules/no-cycle.js +++ b/tests/src/rules/no-cycle.js @@ -61,7 +61,7 @@ ruleTester.run('no-cycle', rule, { options: [{ maxDepth: 1 }], }), test({ - code: `import("./${testDialect}/depth-two").then(function({ foo }){})`, + code: `import("./${testDialect}/depth-two").then(function({ foo }) {})`, options: [{ maxDepth: 1 }], parser: require.resolve('babel-eslint'), }), diff --git a/tests/src/rules/no-duplicates.js b/tests/src/rules/no-duplicates.js index d16f37b103..faf096acec 100644 --- a/tests/src/rules/no-duplicates.js +++ b/tests/src/rules/no-duplicates.js @@ -420,7 +420,7 @@ context('TypeScript', function() { .filter((parser) => parser !== require.resolve('typescript-eslint-parser')) .forEach((parser) => { const parserConfig = { - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -430,15 +430,82 @@ context('TypeScript', function() { ruleTester.run('no-duplicates', rule, { valid: [ // #1667: ignore duplicate if is a typescript type import - test( - { - code: "import type { x } from './foo'; import y from './foo'", - parser, - }, - parserConfig, - ), + test({ + code: "import type { x } from './foo'; import y from './foo'", + ...parserConfig, + }), + test({ + code: "import type x from './foo'; import type y from './bar'", + ...parserConfig, + }), + test({ + code: "import type {x} from './foo'; import type {y} from './bar'", + ...parserConfig, + }), + test({ + code: "import type x from './foo'; import type {y} from './foo'", + ...parserConfig, + }), + test({ + code: ` + import type {} from './module'; + import {} from './module2'; + `, + ...parserConfig, + }), + ], + invalid: [ + test({ + code: "import type x from './foo'; import type y from './foo'", + ...parserConfig, + errors: [ + { + line: 1, + column: 20, + message: "'./foo' imported multiple times.", + }, + { + line: 1, + column: 48, + message: "'./foo' imported multiple times.", + }, + ], + }), + test({ + code: "import type x from './foo'; import type x from './foo'", + output: "import type x from './foo'; ", + ...parserConfig, + errors: [ + { + line: 1, + column: 20, + message: "'./foo' imported multiple times.", + }, + { + line: 1, + column: 48, + message: "'./foo' imported multiple times.", + }, + ], + }), + test({ + code: "import type {x} from './foo'; import type {y} from './foo'", + ...parserConfig, + output: `import type {x,y} from './foo'; `, + errors: [ + { + line: 1, + column: 22, + message: "'./foo' imported multiple times.", + }, + { + line: 1, + column: 52, + message: "'./foo' imported multiple times.", + }, + ], + }), ], - invalid: [], }); }); }); diff --git a/tests/src/rules/no-extraneous-dependencies.js b/tests/src/rules/no-extraneous-dependencies.js index 7a11074684..370cce7e39 100644 --- a/tests/src/rules/no-extraneous-dependencies.js +++ b/tests/src/rules/no-extraneous-dependencies.js @@ -160,7 +160,10 @@ ruleTester.run('no-extraneous-dependencies', rule, { }), test({ - code: 'import "alias/esm-package/esm-module";', + code: ` + import "alias/esm-package/esm-module"; + import 'expose-loader?exposes[]=$&exposes[]=jQuery!jquery'; + `, settings: { 'import/resolver': 'webpack' }, }), ], diff --git a/tests/src/rules/no-unresolved.js b/tests/src/rules/no-unresolved.js index a8694ddf26..d21ee2fd8c 100644 --- a/tests/src/rules/no-unresolved.js +++ b/tests/src/rules/no-unresolved.js @@ -122,7 +122,7 @@ function runResolverTests(resolver) { type: 'Literal', }] }), rest({ - code: "import('in-alternate-root').then(function({DEEP}){});", + code: "import('in-alternate-root').then(function({DEEP}) {});", errors: [{ message: 'Unable to resolve path to module \'in-alternate-root\'.', type: 'Literal', @@ -138,7 +138,7 @@ function runResolverTests(resolver) { // check with eslint parser testVersion('>= 7', () => rest({ - code: "import('in-alternate-root').then(function({DEEP}){});", + code: "import('in-alternate-root').then(function({DEEP}) {});", errors: [{ message: 'Unable to resolve path to module \'in-alternate-root\'.', type: 'Literal', diff --git a/tests/src/rules/no-unused-modules.js b/tests/src/rules/no-unused-modules.js index 283fa3e984..17e52b411b 100644 --- a/tests/src/rules/no-unused-modules.js +++ b/tests/src/rules/no-unused-modules.js @@ -4,8 +4,8 @@ import typescriptConfig from '../../../config/typescript'; import { RuleTester } from 'eslint'; import fs from 'fs'; -import semver from 'semver'; import eslintPkg from 'eslint/package.json'; +import semver from 'semver'; // TODO: figure out why these tests fail in eslint 4 const isESLint4TODO = semver.satisfies(eslintPkg.version, '^4'); @@ -108,34 +108,50 @@ ruleTester.run('no-unused-modules', rule, { // tests for exports ruleTester.run('no-unused-modules', rule, { valid: [ - - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'import { o2 } from "./file-o";export default () => 12', - filename: testFilePath('./no-unused-modules/file-a.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-a.js'), + }), + test({ + options: unusedExportsOptions, code: 'export const b = 2', - filename: testFilePath('./no-unused-modules/file-b.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-b.js'), + }), + test({ + options: unusedExportsOptions, code: 'const c1 = 3; function c2() { return 3 }; export { c1, c2 }', - filename: testFilePath('./no-unused-modules/file-c.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-c.js'), + }), + test({ + options: unusedExportsOptions, code: 'export function d() { return 4 }', - filename: testFilePath('./no-unused-modules/file-d.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-d.js'), + }), + test({ + options: unusedExportsOptions, code: 'export class q { q0() {} }', - filename: testFilePath('./no-unused-modules/file-q.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-q.js'), + }), + test({ + options: unusedExportsOptions, code: 'const e0 = 5; export { e0 as e }', - filename: testFilePath('./no-unused-modules/file-e.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-e.js'), + }), + test({ + options: unusedExportsOptions, code: 'const l0 = 5; const l = 10; export { l0 as l1, l }; export default () => {}', - filename: testFilePath('./no-unused-modules/file-l.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-l.js'), + }), + test({ + options: unusedExportsOptions, code: 'const o0 = 0; const o1 = 1; export { o0, o1 as o2 }; export default () => {}', - filename: testFilePath('./no-unused-modules/file-o.js') }), + filename: testFilePath('./no-unused-modules/file-o.js'), + }), ], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import eslint from 'eslint' import fileA from './file-a' import { b } from './file-b' @@ -155,11 +171,14 @@ ruleTester.run('no-unused-modules', rule, { error(`exported declaration 'o0' not used within other modules`), error(`exported declaration 'o3' not used within other modules`), error(`exported declaration 'p' not used within other modules`), - ] }), - test({ options: unusedExportsOptions, + ], + }), + test({ + options: unusedExportsOptions, code: `const n0 = 'n0'; const n1 = 42; export { n0, n1 }; export default () => {}`, filename: testFilePath('./no-unused-modules/file-n.js'), - errors: [error(`exported declaration 'default' not used within other modules`)] }), + errors: [error(`exported declaration 'default' not used within other modules`)], + }), ], }); @@ -167,60 +186,80 @@ ruleTester.run('no-unused-modules', rule, { ruleTester.run('no-unused-modules', rule, { valid: [], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'export default () => 13', filename: testFilePath('./no-unused-modules/file-f.js'), - errors: [error(`exported declaration 'default' not used within other modules`)] }), - test({ options: unusedExportsOptions, + errors: [error(`exported declaration 'default' not used within other modules`)], + }), + test({ + options: unusedExportsOptions, code: 'export const g = 2', filename: testFilePath('./no-unused-modules/file-g.js'), - errors: [error(`exported declaration 'g' not used within other modules`)] }), - test({ options: unusedExportsOptions, + errors: [error(`exported declaration 'g' not used within other modules`)], + }), + test({ + options: unusedExportsOptions, code: 'const h1 = 3; function h2() { return 3 }; const h3 = true; export { h1, h2, h3 }', filename: testFilePath('./no-unused-modules/file-h.js'), - errors: [error(`exported declaration 'h1' not used within other modules`)] }), - test({ options: unusedExportsOptions, + errors: [error(`exported declaration 'h1' not used within other modules`)], + }), + test({ + options: unusedExportsOptions, code: 'const i1 = 3; function i2() { return 3 }; export { i1, i2 }', filename: testFilePath('./no-unused-modules/file-i.js'), errors: [ error(`exported declaration 'i1' not used within other modules`), error(`exported declaration 'i2' not used within other modules`), - ] }), - test({ options: unusedExportsOptions, + ], + }), + test({ + options: unusedExportsOptions, code: 'export function j() { return 4 }', filename: testFilePath('./no-unused-modules/file-j.js'), - errors: [error(`exported declaration 'j' not used within other modules`)] }), - test({ options: unusedExportsOptions, + errors: [error(`exported declaration 'j' not used within other modules`)], + }), + test({ + options: unusedExportsOptions, code: 'export class q { q0() {} }', filename: testFilePath('./no-unused-modules/file-q.js'), - errors: [error(`exported declaration 'q' not used within other modules`)] }), - test({ options: unusedExportsOptions, + errors: [error(`exported declaration 'q' not used within other modules`)], + }), + test({ + options: unusedExportsOptions, code: 'const k0 = 5; export { k0 as k }', filename: testFilePath('./no-unused-modules/file-k.js'), - errors: [error(`exported declaration 'k' not used within other modules`)] }), + errors: [error(`exported declaration 'k' not used within other modules`)], + }), ], }); -// // test for export from +// test for export from ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export { default } from './file-o'`, - filename: testFilePath('./no-unused-modules/file-s.js') }), + filename: testFilePath('./no-unused-modules/file-s.js'), + }), ], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export { k } from '${testFilePath('./no-unused-modules/file-k.js')}'`, filename: testFilePath('./no-unused-modules/file-j.js'), - errors: [error(`exported declaration 'k' not used within other modules`)] }), + errors: [error(`exported declaration 'k' not used within other modules`)], + }), ], }); ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'const k0 = 5; export { k0 as k }', - filename: testFilePath('./no-unused-modules/file-k.js') }), + filename: testFilePath('./no-unused-modules/file-k.js'), + }), ], invalid: [], }); @@ -228,24 +267,36 @@ ruleTester.run('no-unused-modules', rule, { // test for ignored files ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'export default () => 14', - filename: testFilePath('./no-unused-modules/file-ignored-a.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-ignored-a.js'), + }), + test({ + options: unusedExportsOptions, code: 'export const b = 2', - filename: testFilePath('./no-unused-modules/file-ignored-b.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-ignored-b.js'), + }), + test({ + options: unusedExportsOptions, code: 'const c1 = 3; function c2() { return 3 }; export { c1, c2 }', - filename: testFilePath('./no-unused-modules/file-ignored-c.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-ignored-c.js'), + }), + test({ + options: unusedExportsOptions, code: 'export function d() { return 4 }', - filename: testFilePath('./no-unused-modules/file-ignored-d.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-ignored-d.js'), + }), + test({ + options: unusedExportsOptions, code: 'const f = 5; export { f as e }', - filename: testFilePath('./no-unused-modules/file-ignored-e.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-ignored-e.js'), + }), + test({ + options: unusedExportsOptions, code: 'const l0 = 5; const l = 10; export { l0 as l1, l }; export default () => {}', - filename: testFilePath('./no-unused-modules/file-ignored-l.js') }), + filename: testFilePath('./no-unused-modules/file-ignored-l.js'), + }), ], invalid: [], }); @@ -253,27 +304,35 @@ ruleTester.run('no-unused-modules', rule, { // add named import for file with default export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import { f } from '${testFilePath('./no-unused-modules/file-f.js')}'`, - filename: testFilePath('./no-unused-modules/file-0.js') }), + filename: testFilePath('./no-unused-modules/file-0.js'), + }), ], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'export default () => 15', filename: testFilePath('./no-unused-modules/file-f.js'), - errors: [error(`exported declaration 'default' not used within other modules`)] }), + errors: [error(`exported declaration 'default' not used within other modules`)], + }), ], }); // add default import for file with default export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import f from '${testFilePath('./no-unused-modules/file-f.js')}'`, - filename: testFilePath('./no-unused-modules/file-0.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-0.js'), + }), + test({ + options: unusedExportsOptions, code: 'export default () => 16', - filename: testFilePath('./no-unused-modules/file-f.js') }), + filename: testFilePath('./no-unused-modules/file-f.js'), + }), ], invalid: [], }); @@ -281,26 +340,34 @@ ruleTester.run('no-unused-modules', rule, { // add default import for file with named export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import g from '${testFilePath('./no-unused-modules/file-g.js')}';import {h} from '${testFilePath('./no-unused-modules/file-gg.js')}'`, - filename: testFilePath('./no-unused-modules/file-0.js') }), + filename: testFilePath('./no-unused-modules/file-0.js'), + }), ], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'export const g = 2', filename: testFilePath('./no-unused-modules/file-g.js'), - errors: [error(`exported declaration 'g' not used within other modules`)] })], + errors: [error(`exported declaration 'g' not used within other modules`)], + })], }); // add named import for file with named export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import { g } from '${testFilePath('./no-unused-modules/file-g.js')}'; import eslint from 'eslint'`, - filename: testFilePath('./no-unused-modules/file-0.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-0.js'), + }), + test({ + options: unusedExportsOptions, code: 'export const g = 2', - filename: testFilePath('./no-unused-modules/file-g.js') }), + filename: testFilePath('./no-unused-modules/file-g.js'), + }), ], invalid: [], }); @@ -308,27 +375,35 @@ ruleTester.run('no-unused-modules', rule, { // add different named import for file with named export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import { c } from '${testFilePath('./no-unused-modules/file-b.js')}'`, - filename: testFilePath('./no-unused-modules/file-0.js') }), + filename: testFilePath('./no-unused-modules/file-0.js'), + }), ], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'export const b = 2', filename: testFilePath('./no-unused-modules/file-b.js'), - errors: [error(`exported declaration 'b' not used within other modules`)] }), + errors: [error(`exported declaration 'b' not used within other modules`)], + }), ], }); // add renamed named import for file with named export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import { g as g1 } from '${testFilePath('./no-unused-modules/file-g.js')}'; import eslint from 'eslint'`, - filename: testFilePath('./no-unused-modules/file-0.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-0.js'), + }), + test({ + options: unusedExportsOptions, code: 'export const g = 2', - filename: testFilePath('./no-unused-modules/file-g.js') }), + filename: testFilePath('./no-unused-modules/file-g.js'), + }), ], invalid: [], }); @@ -336,30 +411,38 @@ ruleTester.run('no-unused-modules', rule, { // add different renamed named import for file with named export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import { g1 as g } from '${testFilePath('./no-unused-modules/file-g.js')}'`, - filename: testFilePath('./no-unused-modules/file-0.js') }), + filename: testFilePath('./no-unused-modules/file-0.js'), + }), ], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'export const g = 2', filename: testFilePath('./no-unused-modules/file-g.js'), - errors: [error(`exported declaration 'g' not used within other modules`)] }), + errors: [error(`exported declaration 'g' not used within other modules`)], + }), ], }); // remove default import for file with default export ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import { a1, a2 } from '${testFilePath('./no-unused-modules/file-a.js')}'`, - filename: testFilePath('./no-unused-modules/file-0.js') }), + filename: testFilePath('./no-unused-modules/file-0.js'), + }), ], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'export default () => 17', filename: testFilePath('./no-unused-modules/file-a.js'), - errors: [error(`exported declaration 'default' not used within other modules`)] }), + errors: [error(`exported declaration 'default' not used within other modules`)], + }), ], }); @@ -367,24 +450,30 @@ ruleTester.run('no-unused-modules', rule, { ruleTester.run('no-unused-modules', rule, { valid: [], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'const m0 = 5; const m = 10; export { m0 as m1, m }; export default () => {}', filename: testFilePath('./no-unused-modules/file-m.js'), errors: [ error(`exported declaration 'm1' not used within other modules`), error(`exported declaration 'm' not used within other modules`), error(`exported declaration 'default' not used within other modules`), - ] }), + ], + }), ], }); ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import * as m from '${testFilePath('./no-unused-modules/file-m.js')}'; import unknown from 'unknown-module'`, - filename: testFilePath('./no-unused-modules/file-0.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-0.js'), + }), + test({ + options: unusedExportsOptions, code: 'const m0 = 5; const m = 10; export { m0 as m1, m }; export default () => {}', - filename: testFilePath('./no-unused-modules/file-m.js') }), + filename: testFilePath('./no-unused-modules/file-m.js'), + }), ], invalid: [], }); @@ -392,82 +481,103 @@ ruleTester.run('no-unused-modules', rule, { // remove all exports ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `/* import * as m from '${testFilePath('./no-unused-modules/file-m.js')}' */`, - filename: testFilePath('./no-unused-modules/file-0.js') }), + filename: testFilePath('./no-unused-modules/file-0.js'), + }), ], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'const m0 = 5; const m = 10; export { m0 as m1, m }; export default () => {}', filename: testFilePath('./no-unused-modules/file-m.js'), errors: [ error(`exported declaration 'm1' not used within other modules`), error(`exported declaration 'm' not used within other modules`), error(`exported declaration 'default' not used within other modules`), - ] }), + ], + }), ], }); ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export * from '${testFilePath('./no-unused-modules/file-m.js')}';`, - filename: testFilePath('./no-unused-modules/file-0.js') }), + filename: testFilePath('./no-unused-modules/file-0.js'), + }), ], invalid: [], }); ruleTester.run('no-unused-modules', rule, { valid: [], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'const m0 = 5; const m = 10; export { m0 as m1, m }; export default () => {}', filename: testFilePath('./no-unused-modules/file-m.js'), - errors: [error(`exported declaration 'default' not used within other modules`)] }), + errors: [error(`exported declaration 'default' not used within other modules`)], + }), ], }); ruleTester.run('no-unused-modules', rule, { valid: [], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export { m1, m} from '${testFilePath('./no-unused-modules/file-m.js')}';`, filename: testFilePath('./no-unused-modules/file-0.js'), errors: [ error(`exported declaration 'm1' not used within other modules`), error(`exported declaration 'm' not used within other modules`), - ] }), - test({ options: unusedExportsOptions, + ], + }), + test({ + options: unusedExportsOptions, code: 'const m0 = 5; const m = 10; export { m0 as m1, m }; export default () => {}', filename: testFilePath('./no-unused-modules/file-m.js'), - errors: [error(`exported declaration 'default' not used within other modules`)] }), + errors: [error(`exported declaration 'default' not used within other modules`)], + }), ], }); ruleTester.run('no-unused-modules', rule, { valid: [ - // test({ options: unusedExportsOptions, - // code: `export { default, m1 } from '${testFilePath('./no-unused-modules/file-m.js')}';`, - // filename: testFilePath('./no-unused-modules/file-0.js')}), + /* TODO: + test({ + options: unusedExportsOptions, + code: `export { default, m1 } from '${testFilePath('./no-unused-modules/file-m.js')}';`, + filename: testFilePath('./no-unused-modules/file-0.js') + }), + */ ], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export { default, m1 } from '${testFilePath('./no-unused-modules/file-m.js')}';`, filename: testFilePath('./no-unused-modules/file-0.js'), errors: [ error(`exported declaration 'default' not used within other modules`), error(`exported declaration 'm1' not used within other modules`), - ] }), - test({ options: unusedExportsOptions, + ], + }), + test({ + options: unusedExportsOptions, code: 'const m0 = 5; const m = 10; export { m0 as m1, m }; export default () => {}', filename: testFilePath('./no-unused-modules/file-m.js'), - errors: [error(`exported declaration 'm' not used within other modules`)] }), + errors: [error(`exported declaration 'm' not used within other modules`)], + }), ], }); // Test that import and export in the same file both counts as usage ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export const a = 5;export const b = 't1'`, filename: testFilePath('./no-unused-modules/import-export-1.js'), }), @@ -478,23 +588,31 @@ ruleTester.run('no-unused-modules', rule, { describe('renameDefault', () => { ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'export { default as Component } from "./Component"', - filename: testFilePath('./no-unused-modules/renameDefault/components.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/renameDefault/components.js'), + }), + test({ + options: unusedExportsOptions, code: 'export default function Component() {}', - filename: testFilePath('./no-unused-modules/renameDefault/Component.js') }), + filename: testFilePath('./no-unused-modules/renameDefault/Component.js'), + }), ], invalid: [], }); ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'export { default as ComponentA } from "./ComponentA";export { default as ComponentB } from "./ComponentB";', - filename: testFilePath('./no-unused-modules/renameDefault-2/components.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/renameDefault-2/components.js'), + }), + test({ + options: unusedExportsOptions, code: 'export default function ComponentA() {};', - filename: testFilePath('./no-unused-modules/renameDefault-2/ComponentA.js') }), + filename: testFilePath('./no-unused-modules/renameDefault-2/ComponentA.js'), + }), ], invalid: [], }); @@ -508,12 +626,16 @@ describe('test behavior for new file', () => { // add import in newly created file ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import * as m from '${testFilePath('./no-unused-modules/file-m.js')}'`, - filename: testFilePath('./no-unused-modules/file-added-0.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-added-0.js'), + }), + test({ + options: unusedExportsOptions, code: 'const m0 = 5; const m = 10; export { m0 as m1, m }; export default () => {}', - filename: testFilePath('./no-unused-modules/file-m.js') }), + filename: testFilePath('./no-unused-modules/file-m.js'), + }), ], invalid: [], }); @@ -522,21 +644,27 @@ describe('test behavior for new file', () => { ruleTester.run('no-unused-modules', rule, { valid: [], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export default () => {2}`, filename: testFilePath('./no-unused-modules/file-added-0.js'), - errors: [error(`exported declaration 'default' not used within other modules`)] }), + errors: [error(`exported declaration 'default' not used within other modules`)], + }), ], }); ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import def from '${testFilePath('./no-unused-modules/file-added-0.js')}'`, - filename: testFilePath('./no-unused-modules/file-0.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-0.js'), + }), + test({ + options: unusedExportsOptions, code: `export default () => {}`, - filename: testFilePath('./no-unused-modules/file-added-0.js') }), + filename: testFilePath('./no-unused-modules/file-added-0.js'), + }), ], invalid: [], }); @@ -544,27 +672,34 @@ describe('test behavior for new file', () => { // export * only considers named imports. default imports still need to be reported ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export * from '${testFilePath('./no-unused-modules/file-added-0.js')}'`, - filename: testFilePath('./no-unused-modules/file-0.js') }), + filename: testFilePath('./no-unused-modules/file-0.js'), + }), // Test export * from 'external-compiled-library' - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export * from 'external-compiled-library'`, filename: testFilePath('./no-unused-modules/file-r.js'), }), ], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export const z = 'z';export default () => {}`, filename: testFilePath('./no-unused-modules/file-added-0.js'), - errors: [error(`exported declaration 'default' not used within other modules`)] }), + errors: [error(`exported declaration 'default' not used within other modules`)], + }), ], }); ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export const a = 2`, - filename: testFilePath('./no-unused-modules/file-added-0.js') }), + filename: testFilePath('./no-unused-modules/file-added-0.js'), + }), ], invalid: [], }); @@ -573,17 +708,21 @@ describe('test behavior for new file', () => { ruleTester.run('no-unused-modules', rule, { valid: [], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export { a } from '${testFilePath('./no-unused-modules/file-added-0.js')}'`, filename: testFilePath('./no-unused-modules/file-0.js'), - errors: [error(`exported declaration 'a' not used within other modules`)] }), - test({ options: unusedExportsOptions, + errors: [error(`exported declaration 'a' not used within other modules`)], + }), + test({ + options: unusedExportsOptions, code: `export const z = 'z';export default () => {}`, filename: testFilePath('./no-unused-modules/file-added-0.js'), errors: [ error(`exported declaration 'z' not used within other modules`), error(`exported declaration 'default' not used within other modules`), - ] }), + ], + }), ], }); @@ -594,15 +733,19 @@ describe('test behavior for new file', () => { }); ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export * from '${testFilePath('./no-unused-modules/file-added-1.js')}'`, - filename: testFilePath('./no-unused-modules/file-0.js') }), + filename: testFilePath('./no-unused-modules/file-0.js'), + }), ], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export const z = 'z';export default () => {}`, filename: testFilePath('./no-unused-modules/file-added-1.js'), - errors: [error(`exported declaration 'default' not used within other modules`)] }), + errors: [error(`exported declaration 'default' not used within other modules`)], + }), ], }); after(() => { @@ -625,12 +768,16 @@ describe('test behavior for new file', () => { }); ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import added from '${testFilePath('./no-unused-modules/file-added-2.js')}'`, - filename: testFilePath('./no-unused-modules/file-added-1.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-added-1.js'), + }), + test({ + options: unusedExportsOptions, code: `export default () => {}`, - filename: testFilePath('./no-unused-modules/file-added-2.js') }), + filename: testFilePath('./no-unused-modules/file-added-2.js'), + }), ], invalid: [], }); @@ -647,12 +794,16 @@ describe('test behavior for new file', () => { }); ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import { added } from '${testFilePath('./no-unused-modules/file-added-3.js')}'`, - filename: testFilePath('./no-unused-modules/file-added-1.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-added-1.js'), + }), + test({ + options: unusedExportsOptions, code: `export const added = () => {}`, - filename: testFilePath('./no-unused-modules/file-added-3.js') }), + filename: testFilePath('./no-unused-modules/file-added-3.js'), + }), ], invalid: [], }); @@ -666,18 +817,24 @@ describe('test behavior for new file', () => { describe('test behavior for destructured exports', () => { ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import { destructured } from '${testFilePath('./no-unused-modules/file-destructured-1.js')}'`, - filename: testFilePath('./no-unused-modules/file-destructured-2.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-destructured-2.js'), + }), + test({ + options: unusedExportsOptions, code: `export const { destructured } = {};`, - filename: testFilePath('./no-unused-modules/file-destructured-1.js') }), + filename: testFilePath('./no-unused-modules/file-destructured-1.js'), + }), ], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export const { destructured2 } = {};`, filename: testFilePath('./no-unused-modules/file-destructured-1.js'), - errors: [`exported declaration 'destructured2' not used within other modules`] }), + errors: [`exported declaration 'destructured2' not used within other modules`], + }), ], }); }); @@ -688,12 +845,16 @@ describe('test behavior for new file', () => { }); ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `import * as added from '${testFilePath('./no-unused-modules/file-added-4.js.js')}'`, - filename: testFilePath('./no-unused-modules/file-added-1.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/file-added-1.js'), + }), + test({ + options: unusedExportsOptions, code: `export const added = () => {}; export default () => {}`, - filename: testFilePath('./no-unused-modules/file-added-4.js.js') }), + filename: testFilePath('./no-unused-modules/file-added-4.js.js'), + }), ], invalid: [], }); @@ -707,13 +868,15 @@ describe('test behavior for new file', () => { describe('do not report missing export for ignored file', () => { ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: [{ - src: [testFilePath('./no-unused-modules/**/*.js')], - ignoreExports: [testFilePath('./no-unused-modules/*ignored*.js')], - missingExports: true, - }], - code: 'export const test = true', - filename: testFilePath('./no-unused-modules/file-ignored-a.js') }), + test({ + options: [{ + src: [testFilePath('./no-unused-modules/**/*.js')], + ignoreExports: [testFilePath('./no-unused-modules/*ignored*.js')], + missingExports: true, + }], + code: 'export const test = true', + filename: testFilePath('./no-unused-modules/file-ignored-a.js'), + }), ], invalid: [], }); @@ -722,9 +885,11 @@ describe('do not report missing export for ignored file', () => { // lint file not available in `src` ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export const jsxFoo = 'foo'; export const jsxBar = 'bar'`, - filename: testFilePath('../jsx/named.jsx') }), + filename: testFilePath('../jsx/named.jsx'), + }), ], invalid: [], }); @@ -732,27 +897,39 @@ ruleTester.run('no-unused-modules', rule, { describe('do not report unused export for files mentioned in package.json', () => { ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'export const bin = "bin"', - filename: testFilePath('./no-unused-modules/bin.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/bin.js'), + }), + test({ + options: unusedExportsOptions, code: 'export const binObject = "binObject"', - filename: testFilePath('./no-unused-modules/binObject/index.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/binObject/index.js'), + }), + test({ + options: unusedExportsOptions, code: 'export const browser = "browser"', - filename: testFilePath('./no-unused-modules/browser.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/browser.js'), + }), + test({ + options: unusedExportsOptions, code: 'export const browserObject = "browserObject"', - filename: testFilePath('./no-unused-modules/browserObject/index.js') }), - test({ options: unusedExportsOptions, + filename: testFilePath('./no-unused-modules/browserObject/index.js'), + }), + test({ + options: unusedExportsOptions, code: 'export const main = "main"', - filename: testFilePath('./no-unused-modules/main/index.js') }), + filename: testFilePath('./no-unused-modules/main/index.js'), + }), ], invalid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: 'export const privatePkg = "privatePkg"', filename: testFilePath('./no-unused-modules/privatePkg/index.js'), - errors: [error(`exported declaration 'privatePkg' not used within other modules`)] }), + errors: [error(`exported declaration 'privatePkg' not used within other modules`)], + }), ], }); }); @@ -760,9 +937,11 @@ describe('do not report unused export for files mentioned in package.json', () = describe('Avoid errors if re-export all from umd compiled library', () => { ruleTester.run('no-unused-modules', rule, { valid: [ - test({ options: unusedExportsOptions, + test({ + options: unusedExportsOptions, code: `export * from '${testFilePath('./no-unused-modules/bin.js')}'`, - filename: testFilePath('./no-unused-modules/main/index.js') }), + filename: testFilePath('./no-unused-modules/main/index.js'), + }), ], invalid: [], }); diff --git a/tests/src/rules/no-useless-path-segments.js b/tests/src/rules/no-useless-path-segments.js index 313424d349..21d9a5f704 100644 --- a/tests/src/rules/no-useless-path-segments.js +++ b/tests/src/rules/no-useless-path-segments.js @@ -32,7 +32,7 @@ function runResolverTests(resolver) { parser: require.resolve('babel-eslint') }), test({ code: 'import("..")', parser: require.resolve('babel-eslint') }), - test({ code: 'import("fs").then(function(fs){})', + test({ code: 'import("fs").then(function(fs) {})', parser: require.resolve('babel-eslint') }), ], diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index e7e6b0566d..61bf1bb70f 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -2286,7 +2286,7 @@ context('TypeScript', function () { .filter((parser) => parser !== require.resolve('typescript-eslint-parser')) .forEach((parser) => { const parserConfig = { - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -2298,352 +2298,316 @@ context('TypeScript', function () { // #1667: typescript type import support // Option alphabetize: {order: 'asc'} - test( - { - code: ` - import c from 'Bar'; - import type { C } from 'Bar'; - import b from 'bar'; - import a from 'foo'; - import type { A } from 'foo'; - - import index from './'; - `, - parser, - options: [ - { - groups: ['external', 'index'], - alphabetize: { order: 'asc' }, - }, - ], - }, - parserConfig, - ), + test({ + code: ` + import c from 'Bar'; + import type { C } from 'Bar'; + import b from 'bar'; + import a from 'foo'; + import type { A } from 'foo'; + + import index from './'; + `, + ...parserConfig, + options: [ + { + groups: ['external', 'index'], + alphabetize: { order: 'asc' }, + }, + ], + }), // Option alphabetize: {order: 'desc'} - test( - { - code: ` - import a from 'foo'; - import type { A } from 'foo'; - import b from 'bar'; - import c from 'Bar'; - import type { C } from 'Bar'; - - import index from './'; - `, - parser, - options: [ - { - groups: ['external', 'index'], - alphabetize: { order: 'desc' }, - }, - ], - }, - parserConfig, - ), + test({ + code: ` + import a from 'foo'; + import type { A } from 'foo'; + import b from 'bar'; + import c from 'Bar'; + import type { C } from 'Bar'; + + import index from './'; + `, + ...parserConfig, + options: [ + { + groups: ['external', 'index'], + alphabetize: { order: 'desc' }, + }, + ], + }), // Option alphabetize: {order: 'asc'} with type group - test( - { - code: ` - import c from 'Bar'; - import b from 'bar'; - import a from 'foo'; - - import index from './'; - - import type { C } from 'Bar'; - import type { A } from 'foo'; - `, - parser, - options: [ - { - groups: ['external', 'index', 'type'], - alphabetize: { order: 'asc' }, - }, - ], - }, - parserConfig, - ), + test({ + code: ` + import c from 'Bar'; + import b from 'bar'; + import a from 'foo'; + + import index from './'; + + import type { C } from 'Bar'; + import type { A } from 'foo'; + `, + ...parserConfig, + options: [ + { + groups: ['external', 'index', 'type'], + alphabetize: { order: 'asc' }, + }, + ], + }), // Option alphabetize: {order: 'asc'} with type group & path group - test( - { - // only: true, - code: ` - import c from 'Bar'; - import a from 'foo'; - - import b from 'dirA/bar'; - - import index from './'; - - import type { C } from 'dirA/Bar'; - import type { A } from 'foo'; - `, - parser, - options: [ - { - alphabetize: { order: 'asc' }, - groups: ['external', 'internal', 'index', 'type'], - pathGroups: [ - { - pattern: 'dirA/**', - group: 'internal', - }, - ], - 'newlines-between': 'always', - pathGroupsExcludedImportTypes: ['type'], - }, - ], - }, - parserConfig, - ), + test({ + // only: true, + code: ` + import c from 'Bar'; + import a from 'foo'; + + import b from 'dirA/bar'; + + import index from './'; + + import type { C } from 'dirA/Bar'; + import type { A } from 'foo'; + `, + ...parserConfig, + options: [ + { + alphabetize: { order: 'asc' }, + groups: ['external', 'internal', 'index', 'type'], + pathGroups: [ + { + pattern: 'dirA/**', + group: 'internal', + }, + ], + 'newlines-between': 'always', + pathGroupsExcludedImportTypes: ['type'], + }, + ], + }), // Option alphabetize: {order: 'asc'} with path group - test( - { - // only: true, - code: ` - import c from 'Bar'; - import type { A } from 'foo'; - import a from 'foo'; - - import type { C } from 'dirA/Bar'; - import b from 'dirA/bar'; - - import index from './'; - `, - parser, - options: [ - { - alphabetize: { order: 'asc' }, - groups: ['external', 'internal', 'index'], - pathGroups: [ - { - pattern: 'dirA/**', - group: 'internal', - }, - ], - 'newlines-between': 'always', - pathGroupsExcludedImportTypes: [], - }, - ], - }, - parserConfig, - ), + test({ + // only: true, + code: ` + import c from 'Bar'; + import type { A } from 'foo'; + import a from 'foo'; + + import type { C } from 'dirA/Bar'; + import b from 'dirA/bar'; + + import index from './'; + `, + ...parserConfig, + options: [ + { + alphabetize: { order: 'asc' }, + groups: ['external', 'internal', 'index'], + pathGroups: [ + { + pattern: 'dirA/**', + group: 'internal', + }, + ], + 'newlines-between': 'always', + pathGroupsExcludedImportTypes: [], + }, + ], + }), // Option alphabetize: {order: 'desc'} with type group - test( - { - code: ` - import a from 'foo'; - import b from 'bar'; - import c from 'Bar'; - - import index from './'; - - import type { A } from 'foo'; - import type { C } from 'Bar'; - `, - parser, - options: [ - { - groups: ['external', 'index', 'type'], - alphabetize: { order: 'desc' }, - }, - ], - }, - parserConfig, - ), - test( - { - code: ` - import { Partner } from '@models/partner/partner'; - import { PartnerId } from '@models/partner/partner-id'; - `, - parser, - options: [ - { - alphabetize: { order: 'asc' }, - }, - ], - }, - parserConfig, - ), - test( - { - code: ` - import { serialize, parse, mapFieldErrors } from '@vtaits/form-schema'; - import type { GetFieldSchema } from '@vtaits/form-schema'; - import { useMemo, useCallback } from 'react'; - import type { ReactElement, ReactNode } from 'react'; - import { Form } from 'react-final-form'; - import type { FormProps as FinalFormProps } from 'react-final-form'; - `, - parser, - options: [ - { - alphabetize: { order: 'asc' }, - }, - ], - }, - parserConfig, - ), + test({ + code: ` + import a from 'foo'; + import b from 'bar'; + import c from 'Bar'; + + import index from './'; + + import type { A } from 'foo'; + import type { C } from 'Bar'; + `, + ...parserConfig, + options: [ + { + groups: ['external', 'index', 'type'], + alphabetize: { order: 'desc' }, + }, + ], + }), + test({ + code: ` + import { Partner } from '@models/partner/partner'; + import { PartnerId } from '@models/partner/partner-id'; + `, + ...parserConfig, + options: [ + { + alphabetize: { order: 'asc' }, + }, + ], + }), + test({ + code: ` + import { serialize, parse, mapFieldErrors } from '@vtaits/form-schema'; + import type { GetFieldSchema } from '@vtaits/form-schema'; + import { useMemo, useCallback } from 'react'; + import type { ReactElement, ReactNode } from 'react'; + import { Form } from 'react-final-form'; + import type { FormProps as FinalFormProps } from 'react-final-form'; + `, + ...parserConfig, + options: [ + { + alphabetize: { order: 'asc' }, + }, + ], + }), ], invalid: [ // Option alphabetize: {order: 'asc'} - test( - { - code: ` - import b from 'bar'; - import c from 'Bar'; - import type { C } from 'Bar'; - import a from 'foo'; - import type { A } from 'foo'; - - import index from './'; - `, - output: ` - import c from 'Bar'; - import type { C } from 'Bar'; - import b from 'bar'; - import a from 'foo'; - import type { A } from 'foo'; - - import index from './'; - `, - parser, - options: [ - { - groups: ['external', 'index'], - alphabetize: { order: 'asc' }, - }, - ], - errors: [ - { - message: semver.satisfies(eslintPkg.version, '< 3') - ? '`bar` import should occur after import of `Bar`' - : /(`bar` import should occur after import of `Bar`)|(`Bar` import should occur before import of `bar`)/, - }, - ], - }, - parserConfig, - ), + test({ + code: ` + import b from 'bar'; + import c from 'Bar'; + import type { C } from 'Bar'; + import a from 'foo'; + import type { A } from 'foo'; + + import index from './'; + `, + output: ` + import c from 'Bar'; + import type { C } from 'Bar'; + import b from 'bar'; + import a from 'foo'; + import type { A } from 'foo'; + + import index from './'; + `, + ...parserConfig, + options: [ + { + groups: ['external', 'index'], + alphabetize: { order: 'asc' }, + }, + ], + errors: [ + { + message: semver.satisfies(eslintPkg.version, '< 3') + ? '`bar` import should occur after import of `Bar`' + : /(`bar` import should occur after import of `Bar`)|(`Bar` import should occur before import of `bar`)/, + }, + ], + }), // Option alphabetize: {order: 'desc'} - test( - { - code: ` - import a from 'foo'; - import type { A } from 'foo'; - import c from 'Bar'; - import type { C } from 'Bar'; - import b from 'bar'; - - import index from './'; - `, - output: ` - import a from 'foo'; - import type { A } from 'foo'; - import b from 'bar'; - import c from 'Bar'; - import type { C } from 'Bar'; - - import index from './'; - `, - parser, - options: [ - { - groups: ['external', 'index'], - alphabetize: { order: 'desc' }, - }, - ], - errors: [ - { - message: semver.satisfies(eslintPkg.version, '< 3') - ? '`bar` import should occur before import of `Bar`' - : /(`bar` import should occur before import of `Bar`)|(`Bar` import should occur after import of `bar`)/, - }, - ], - }, - parserConfig, - ), + test({ + code: ` + import a from 'foo'; + import type { A } from 'foo'; + import c from 'Bar'; + import type { C } from 'Bar'; + import b from 'bar'; + + import index from './'; + `, + output: ` + import a from 'foo'; + import type { A } from 'foo'; + import b from 'bar'; + import c from 'Bar'; + import type { C } from 'Bar'; + + import index from './'; + `, + ...parserConfig, + options: [ + { + groups: ['external', 'index'], + alphabetize: { order: 'desc' }, + }, + ], + errors: [ + { + message: semver.satisfies(eslintPkg.version, '< 3') + ? '`bar` import should occur before import of `Bar`' + : /(`bar` import should occur before import of `Bar`)|(`Bar` import should occur after import of `bar`)/, + }, + ], + }), // Option alphabetize: {order: 'asc'} with type group - test( - { - code: ` - import b from 'bar'; - import c from 'Bar'; - import a from 'foo'; - - import index from './'; - - import type { A } from 'foo'; - import type { C } from 'Bar'; - `, - output: ` - import c from 'Bar'; - import b from 'bar'; - import a from 'foo'; - - import index from './'; - - import type { C } from 'Bar'; - import type { A } from 'foo'; - `, - parser, - options: [ - { - groups: ['external', 'index', 'type'], - alphabetize: { order: 'asc' }, - }, - ], - errors: semver.satisfies(eslintPkg.version, '< 3') ? [ - { message: '`Bar` import should occur before import of `bar`' }, - { message: '`Bar` import should occur before import of `foo`' }, - ] : [ - { message: /(`Bar` import should occur before import of `bar`)|(`bar` import should occur after import of `Bar`)/ }, - { message: /(`Bar` import should occur before import of `foo`)|(`foo` import should occur after import of `Bar`)/ }, - ], - }, - parserConfig, - ), + test({ + code: ` + import b from 'bar'; + import c from 'Bar'; + import a from 'foo'; + + import index from './'; + + import type { A } from 'foo'; + import type { C } from 'Bar'; + `, + output: ` + import c from 'Bar'; + import b from 'bar'; + import a from 'foo'; + + import index from './'; + + import type { C } from 'Bar'; + import type { A } from 'foo'; + `, + ...parserConfig, + options: [ + { + groups: ['external', 'index', 'type'], + alphabetize: { order: 'asc' }, + }, + ], + errors: semver.satisfies(eslintPkg.version, '< 3') ? [ + { message: '`Bar` import should occur before import of `bar`' }, + { message: '`Bar` import should occur before import of `foo`' }, + ] : [ + { message: /(`Bar` import should occur before import of `bar`)|(`bar` import should occur after import of `Bar`)/ }, + { message: /(`Bar` import should occur before import of `foo`)|(`foo` import should occur after import of `Bar`)/ }, + ], + }), // Option alphabetize: {order: 'desc'} with type group - test( - { - code: ` - import a from 'foo'; - import c from 'Bar'; - import b from 'bar'; - - import index from './'; - - import type { C } from 'Bar'; - import type { A } from 'foo'; - `, - output: ` - import a from 'foo'; - import b from 'bar'; - import c from 'Bar'; - - import index from './'; - - import type { A } from 'foo'; - import type { C } from 'Bar'; - `, - parser, - options: [ - { - groups: ['external', 'index', 'type'], - alphabetize: { order: 'desc' }, - }, - ], - errors: semver.satisfies(eslintPkg.version, '< 3') ? [ - { message: '`bar` import should occur before import of `Bar`' }, - { message: '`foo` import should occur before import of `Bar`' }, - ] : [ - { message: /(`bar` import should occur before import of `Bar`)|(`Bar` import should occur after import of `bar`)/ }, - { message: /(`foo` import should occur before import of `Bar`)|(`Bar` import should occur after import of `foo`)/ }, - ], - }, - parserConfig, - ), + test({ + code: ` + import a from 'foo'; + import c from 'Bar'; + import b from 'bar'; + + import index from './'; + + import type { C } from 'Bar'; + import type { A } from 'foo'; + `, + output: ` + import a from 'foo'; + import b from 'bar'; + import c from 'Bar'; + + import index from './'; + + import type { A } from 'foo'; + import type { C } from 'Bar'; + `, + ...parserConfig, + options: [ + { + groups: ['external', 'index', 'type'], + alphabetize: { order: 'desc' }, + }, + ], + errors: semver.satisfies(eslintPkg.version, '< 3') ? [ + { message: '`bar` import should occur before import of `Bar`' }, + { message: '`foo` import should occur before import of `Bar`' }, + ] : [ + { message: /(`bar` import should occur before import of `Bar`)|(`Bar` import should occur after import of `bar`)/ }, + { message: /(`foo` import should occur before import of `Bar`)|(`Bar` import should occur after import of `foo`)/ }, + ], + }), // warns for out of order unassigned imports (warnOnUnassignedImports enabled) test({ code: ` diff --git a/tests/src/rules/prefer-default-export.js b/tests/src/rules/prefer-default-export.js index 4efa47f5fc..34323fe53d 100644 --- a/tests/src/rules/prefer-default-export.js +++ b/tests/src/rules/prefer-default-export.js @@ -151,7 +151,7 @@ ruleTester.run('prefer-default-export', rule, { context('TypeScript', function() { getNonDefaultParsers().forEach((parser) => { const parserConfig = { - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -161,52 +161,34 @@ context('TypeScript', function() { ruleTester.run('prefer-default-export', rule, { valid: [ // Exporting types - test( - { - code: ` - export type foo = string; - export type bar = number;`, - parser, - }, - parserConfig, - ), - test( - { - code: ` - export type foo = string; - export type bar = number;`, - parser, - }, - parserConfig, - ), - test( - { - code: 'export type foo = string', - parser, - }, - parserConfig, - ), - test( - { - code: 'export type foo = string', - parser, - }, - parserConfig, - ), - test( - { - code: 'export interface foo { bar: string; }', - parser, - }, - parserConfig, - ), - test( - { - code: 'export interface foo { bar: string; }; export function goo() {}', - parser, - }, - parserConfig, - ), + test({ + code: ` + export type foo = string; + export type bar = number;`, + ...parserConfig, + }), + test({ + code: ` + export type foo = string; + export type bar = number;`, + ...parserConfig, + }), + test({ + code: 'export type foo = string', + ...parserConfig, + }), + test({ + code: 'export type foo = string', + ...parserConfig, + }), + test({ + code: 'export interface foo { bar: string; }', + ...parserConfig, + }), + test({ + code: 'export interface foo { bar: string; }; export function goo() {}', + ...parserConfig, + }), ], invalid: [], }); diff --git a/tests/src/utils.js b/tests/src/utils.js index a76826de51..e805e9545d 100644 --- a/tests/src/utils.js +++ b/tests/src/utils.js @@ -32,6 +32,9 @@ export function testVersion(specifier, t) { } export function test(t) { + if (arguments.length !== 1) { + throw new SyntaxError('`test` requires exactly one object argument'); + } return Object.assign({ filename: FILENAME, }, t, { diff --git a/utils/moduleVisitor.js b/utils/moduleVisitor.js index 69269985bd..8fb2b2604c 100644 --- a/utils/moduleVisitor.js +++ b/utils/moduleVisitor.js @@ -37,7 +37,7 @@ exports.default = function visitModules(visitor, options) { // for esmodule dynamic `import()` calls function checkImportCall(node) { let modulePath; - // refs https://github.com/estree/estree/blob/master/es2020.md#importexpression + // refs https://github.com/estree/estree/blob/HEAD/es2020.md#importexpression if (node.type === 'ImportExpression') { modulePath = node.source; } else if (node.type === 'CallExpression') { @@ -131,7 +131,7 @@ function makeOptionsSchema(additionalProperties) { 'additionalProperties': false, }; - if (additionalProperties){ + if (additionalProperties) { for (const key in additionalProperties) { base.properties[key] = additionalProperties[key]; }