Skip to content

Commit f12ae59

Browse files
pcorpetljharb
authored andcommittedMay 28, 2018
[New] no-duplicates: add a considerQueryString option to handle false positives when using some webpack loaders.
Fixes #1107.
1 parent 2d3d045 commit f12ae59

File tree

4 files changed

+85
-2
lines changed

4 files changed

+85
-2
lines changed
 

Diff for: ‎CHANGELOG.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ This project adheres to [Semantic Versioning](http://semver.org/).
44
This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com).
55

66
## [Unreleased]
7-
- [`no-extraneous-dependencies`]: Check `export from` ([#1049], thanks [@marcusdarmstrong])
87

98
### Added
109
- [`internal-regex`]: regex pattern for marking packages "internal" ([#1491], thanks [@Librazy])
@@ -14,6 +13,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
1413
- [`no-extraneous-dependencies`]: Implement support for [bundledDependencies](https://npm.github.io/using-pkgs-docs/package-json/types/bundleddependencies.html) ([#1436], thanks [@schmidsi]))
1514
- [`no-unused-modules`]: add flow type support ([#1542], thanks [@rfermann])
1615
- [`order`]: Adds support for pathGroups to allow ordering by defined patterns ([#795], [#1386], thanks [@Mairu])
16+
- [`no-duplicates`]: Add `considerQueryString` option : allow duplicate imports with different query strings ([#1107], thanks [@pcorpet]).
1717

1818
### Fixed
1919
- [`default`]: make error message less confusing ([#1470], thanks [@golopot])
@@ -25,6 +25,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
2525
- [`no-unused-modules`]: fix crash due to `export *` ([#1496], thanks [@Taranys])
2626
- [`no-cycle`]: should not warn for Flow imports ([#1494], thanks [@maxmalov])
2727
- [`order`]: fix `@someModule` considered as `unknown` instead of `internal` ([#1493], thanks [@aamulumi])
28+
- [`no-extraneous-dependencies`]: Check `export from` ([#1049], thanks [@marcusdarmstrong])
2829

2930
### Docs
3031
- [`no-useless-path-segments`]: add docs for option `commonjs` ([#1507], thanks [@golopot])
@@ -692,6 +693,7 @@ for info on changes for earlier releases.
692693
[#1126]: https://github.com/benmosher/eslint-plugin-import/pull/1126
693694
[#1122]: https://github.com/benmosher/eslint-plugin-import/pull/1122
694695
[#1112]: https://github.com/benmosher/eslint-plugin-import/pull/1112
696+
[#1107]: https://github.com/benmosher/eslint-plugin-import/pull/1107
695697
[#1106]: https://github.com/benmosher/eslint-plugin-import/pull/1106
696698
[#1093]: https://github.com/benmosher/eslint-plugin-import/pull/1093
697699
[#1085]: https://github.com/benmosher/eslint-plugin-import/pull/1085
@@ -1032,3 +1034,4 @@ for info on changes for earlier releases.
10321034
[@marcusdarmstrong]: https://github.com/marcusdarmstrong
10331035
[@Mairu]: https://github.com/Mairu
10341036
[@aamulumi]: https://github.com/aamulumi
1037+
[@pcorpet]: https://github.com/pcorpet

Diff for: ‎docs/rules/no-duplicates.md

+25
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,31 @@ The motivation is that this is likely a result of two developers importing diffe
3636
names from the same module at different times (and potentially largely different
3737
locations in the file.) This rule brings both (or n-many) to attention.
3838

39+
### Query Strings
40+
41+
By default, this rule ignores query strings (i.e. paths followed by a question mark), and thus imports from `./mod?a` and `./mod?b` will be considered as duplicates. However you can use the option `considerQueryString` to handle them as different (primarily because browsers will resolve those imports differently).
42+
43+
Config:
44+
45+
```json
46+
"import/no-duplicates": ["error", {"considerQueryString": true}]
47+
```
48+
49+
And then the following code becomes valid:
50+
```js
51+
import minifiedMod from './mod?minify'
52+
import noCommentsMod from './mod?comments=0'
53+
import originalMod from './mod'
54+
```
55+
56+
It will still catch duplicates when using the same module and the exact same query string:
57+
```js
58+
import SomeDefaultClass from './mod?minify'
59+
60+
// This is invalid, assuming `./mod` and `./mod.js` are the same target:
61+
import * from './mod.js?minify'
62+
```
63+
3964
## When Not To Use It
4065

4166
If the core ESLint version is good enough (i.e. you're _not_ using Flow and you _are_ using [`import/extensions`](./extensions.md)), keep it and don't use this.

Diff for: ‎src/rules/no-duplicates.js

+24-1
Original file line numberDiff line numberDiff line change
@@ -230,15 +230,38 @@ module.exports = {
230230
url: docsUrl('no-duplicates'),
231231
},
232232
fixable: 'code',
233+
schema: [
234+
{
235+
type: 'object',
236+
properties: {
237+
considerQueryString: {
238+
type: 'boolean',
239+
},
240+
},
241+
additionalProperties: false,
242+
},
243+
],
233244
},
234245

235246
create: function (context) {
247+
// Prepare the resolver from options.
248+
const considerQueryStringOption = context.options[0] &&
249+
context.options[0]['considerQueryString']
250+
const defaultResolver = sourcePath => resolve(sourcePath, context) || sourcePath
251+
const resolver = considerQueryStringOption ? (sourcePath => {
252+
const parts = sourcePath.match(/^([^?]*)\?(.*)$/)
253+
if (!parts) {
254+
return defaultResolver(sourcePath)
255+
}
256+
return defaultResolver(parts[1]) + '?' + parts[2]
257+
}) : defaultResolver
258+
236259
const imported = new Map()
237260
const typesImported = new Map()
238261
return {
239262
'ImportDeclaration': function (n) {
240263
// resolved path will cover aliased duplicates
241-
const resolvedPath = resolve(n.source.value, context) || n.source.value
264+
const resolvedPath = resolver(n.source.value)
242265
const importMap = n.importKind === 'type' ? typesImported : imported
243266

244267
if (importMap.has(resolvedPath)) {

Diff for: ‎tests/src/rules/no-duplicates.js

+32
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ ruleTester.run('no-duplicates', rule, {
2525
code: "import { x } from './foo'; import type { y } from './foo'",
2626
parser: require.resolve('babel-eslint'),
2727
}),
28+
29+
// #1107: Using different query strings that trigger different webpack loaders.
30+
test({
31+
code: "import x from './bar?optionX'; import y from './bar?optionY';",
32+
options: [{'considerQueryString': true}],
33+
settings: { 'import/resolver': 'webpack' },
34+
}),
35+
test({
36+
code: "import x from './foo'; import y from './bar';",
37+
options: [{'considerQueryString': true}],
38+
settings: { 'import/resolver': 'webpack' },
39+
}),
2840
],
2941
invalid: [
3042
test({
@@ -50,6 +62,26 @@ ruleTester.run('no-duplicates', rule, {
5062
errors: 2, // path ends up hardcoded
5163
}),
5264

65+
// #1107: Using different query strings that trigger different webpack loaders.
66+
test({
67+
code: "import x from './bar.js?optionX'; import y from './bar?optionX';",
68+
settings: { 'import/resolver': 'webpack' },
69+
errors: 2, // path ends up hardcoded
70+
}),
71+
test({
72+
code: "import x from './bar?optionX'; import y from './bar?optionY';",
73+
settings: { 'import/resolver': 'webpack' },
74+
errors: 2, // path ends up hardcoded
75+
}),
76+
77+
// #1107: Using same query strings that trigger the same loader.
78+
test({
79+
code: "import x from './bar?optionX'; import y from './bar.js?optionX';",
80+
options: [{'considerQueryString': true}],
81+
settings: { 'import/resolver': 'webpack' },
82+
errors: 2, // path ends up hardcoded
83+
}),
84+
5385
// #86: duplicate unresolved modules should be flagged
5486
test({
5587
code: "import foo from 'non-existent'; import bar from 'non-existent';",

0 commit comments

Comments
 (0)
Please sign in to comment.