Skip to content

Commit 335977a

Browse files
committed
no-duplicates: add a query-string option to handle false positives when using some webpack loaders. Fixes import-js#1107.
1 parent ebafcbf commit 335977a

File tree

4 files changed

+65
-1
lines changed

4 files changed

+65
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
55

66
## [Unreleased]
77
- Add [`no-relative-parent-imports`] rule: disallow relative imports from parent directories.
8+
- Add `query-string` option to [`no-duplicates`] rule: allow duplicate imports with different query strings ([#1107], thanks [@pcorpet]).
89

910
## [2.12.0] - 2018-05-17
1011
### Added

docs/rules/no-duplicates.md

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

38+
### Query Strings
39+
40+
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 `query-string` to handle them as different (usually because a webpack loader would actually resolve those imports differently).
41+
42+
Config:
43+
44+
```json
45+
"import/no-duplicates": ["error", {"query-string": "different"}]
46+
```
47+
48+
And then the following code becomes valid:
49+
```js
50+
import minifiedMod from './mod?minify'
51+
import noCommentsMod from './mod?comments=0'
52+
import originalMod from './mod'
53+
```
54+
55+
It will still catch duplicates when using the same module and the exact same query string:
56+
```js
57+
import SomeDefaultClass from './mod?minify'
58+
59+
// This is invalid, assuming `./mod` and `./mod.js` are the same target:
60+
import * from './mod.js?minify'
61+
```
62+
3863
## When Not To Use It
3964

4065
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.

src/rules/no-duplicates.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,23 @@ module.exports = {
1919
},
2020

2121
create: function (context) {
22+
const queryStringOption = context.options[0] && context.options[0]['query-string']
2223
const imported = new Map()
2324
const typesImported = new Map()
25+
26+
const defaultResolver = sourcePath => resolve(sourcePath, context) || sourcePath
27+
const resolver = queryStringOption === 'different' ? (sourcePath => {
28+
const parts = sourcePath.match(/^([^?]*)\?(.*)$/)
29+
if (!parts) {
30+
return defaultResolver(sourcePath)
31+
}
32+
return defaultResolver(parts[1]) + '?' + parts[2]
33+
}) : defaultResolver
34+
2435
return {
2536
'ImportDeclaration': function (n) {
2637
// resolved path will cover aliased duplicates
27-
const resolvedPath = resolve(n.source.value, context) || n.source.value
38+
const resolvedPath = resolver(n.source.value)
2839
const importMap = n.importKind === 'type' ? typesImported : imported
2940

3041
if (importMap.has(resolvedPath)) {

tests/src/rules/no-duplicates.js

+27
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ ruleTester.run('no-duplicates', rule, {
2121
code: "import { x } from './foo'; import type { y } from './foo'",
2222
parser: 'babel-eslint',
2323
}),
24+
25+
// #1107: Using different query strings that trigger different webpack loaders.
26+
test({
27+
code: "import x from './bar?optionX'; import y from './bar?optionY';",
28+
options: [{'query-string': 'different'}],
29+
settings: { 'import/resolver': 'webpack' },
30+
}),
2431
],
2532
invalid: [
2633
test({
@@ -43,6 +50,26 @@ ruleTester.run('no-duplicates', rule, {
4350
errors: 2, // path ends up hardcoded
4451
}),
4552

53+
// #1107: Using different query strings that trigger different webpack loaders.
54+
test({
55+
code: "import x from './bar.js?optionX'; import y from './bar?optionX';",
56+
settings: { 'import/resolver': 'webpack' },
57+
errors: 2, // path ends up hardcoded
58+
}),
59+
test({
60+
code: "import x from './bar?optionX'; import y from './bar?optionY';",
61+
settings: { 'import/resolver': 'webpack' },
62+
errors: 2, // path ends up hardcoded
63+
}),
64+
65+
// #1107: Using same query strings that trigger the same loader.
66+
test({
67+
code: "import x from './bar?optionX'; import y from './bar.js?optionX';",
68+
options: [{'query-string': 'different'}],
69+
settings: { 'import/resolver': 'webpack' },
70+
errors: 2, // path ends up hardcoded
71+
}),
72+
4673
// #86: duplicate unresolved modules should be flagged
4774
test({
4875
code: "import foo from 'non-existent'; import bar from 'non-existent';",

0 commit comments

Comments
 (0)