Skip to content

Commit d228129

Browse files
authored
fix: backports upstream changes (#148)
* fix: backports import-js#3033 * ci: cancel-in-progress when pr is updated backports import-js#2997 * fix: backports import-js#2952 * docs: improve `order` rule docs Backports: - import-js#2640 - import-js#3036
1 parent 978ae88 commit d228129

File tree

8 files changed

+153
-33
lines changed

8 files changed

+153
-33
lines changed

.changeset/chilled-pandas-tickle.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-import-x": patch
3+
---
4+
5+
Fix `newline-after-import`'s `considerComments` options when linting `require`, backports https://github.com/import-js/eslint-plugin-import/pull/2952

.changeset/weak-shirts-smell.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-import-x": patch
3+
---
4+
5+
Fix `no-duplicates` for TypeScript, backports https://github.com/import-js/eslint-plugin-import/pull/3033

.github/workflows/ci.yml

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ on:
44
- push
55
- pull_request
66

7+
concurrency:
8+
group: ${{ github.workflow }}-${{ github.ref }}
9+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
10+
711
jobs:
812
ci:
913
name: Lint and Test with Node.js ${{ matrix.node }} and ESLint ${{ matrix.eslint }} on ${{ matrix.os }}

docs/rules/order.md

+22-24
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,25 @@ import foo from './foo'
7777
var path = require('path')
7878
```
7979

80+
## Limitations of `--fix`
81+
82+
Unbound imports are assumed to have side effects, and will never be moved/reordered. This can cause other imports to get "stuck" around them, and the fix to fail.
83+
84+
```javascript
85+
import b from 'b'
86+
import 'format.css' // This will prevent --fix from working.
87+
import a from 'a'
88+
```
89+
90+
As a workaround, move unbound imports to be entirely above or below bound ones.
91+
92+
```javascript
93+
import 'format1.css' // OK
94+
import b from 'b'
95+
import a from 'a'
96+
import 'format2.css' // OK
97+
```
98+
8099
## Options
81100

82101
This rule supports the following options:
@@ -180,7 +199,8 @@ Example:
180199
### `pathGroupsExcludedImportTypes: [array]`
181200

182201
This defines import types that are not handled by configured pathGroups.
183-
This is mostly needed when you want to handle path groups that look like external imports.
202+
203+
If you have added path groups with patterns that look like `"builtin"` or `"external"` imports, you have to remove this group (`"builtin"` and/or `"external"`) from the default exclusion list (e.g., `["builtin", "external", "object"]`, etc) to sort these path groups correctly.
184204

185205
Example:
186206

@@ -202,29 +222,7 @@ Example:
202222
}
203223
```
204224

205-
You can also use `patterns`(e.g., `react`, `react-router-dom`, etc).
206-
207-
Example:
208-
209-
```json
210-
{
211-
"import-x/order": [
212-
"error",
213-
{
214-
"pathGroups": [
215-
{
216-
"pattern": "react",
217-
"group": "builtin",
218-
"position": "before"
219-
}
220-
],
221-
"pathGroupsExcludedImportTypes": ["react"]
222-
}
223-
]
224-
}
225-
```
226-
227-
The default value is `["builtin", "external", "object"]`.
225+
[Import Type](https://github.com/un-ts/eslint-plugin-import-x/blob/ea7c13eb9b18357432e484b25dfa4451eca69c5b/src/utils/import-type.ts#L145) is resolved as a fixed string in predefined set, it can't be a `patterns` (e.g., `react`, `react-router-dom`, etc).
228226

229227
### `newlines-between: [ignore|always|always-and-inside-groups|never]`
230228

src/rules/newline-after-import.ts

+30-3
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ export = createRule<[Options?], MessageId>({
177177
function commentAfterImport(
178178
node: TSESTree.Node,
179179
nextComment: TSESTree.Comment,
180+
type: 'import' | 'require',
180181
) {
181182
const lineDifference = getLineDifference(node, nextComment)
182183
const EXPECTED_LINE_DIFFERENCE = options.count + 1
@@ -197,7 +198,7 @@ export = createRule<[Options?], MessageId>({
197198
data: {
198199
count: options.count,
199200
lineSuffix: options.count > 1 ? 's' : '',
200-
type: 'import',
201+
type,
201202
},
202203
fix:
203204
options.exactCount && EXPECTED_LINE_DIFFERENCE < lineDifference
@@ -253,7 +254,7 @@ export = createRule<[Options?], MessageId>({
253254
}
254255

255256
if (nextComment) {
256-
commentAfterImport(node, nextComment)
257+
commentAfterImport(node, nextComment, 'import')
257258
} else if (
258259
nextNode &&
259260
nextNode.type !== 'ImportDeclaration' &&
@@ -301,7 +302,33 @@ export = createRule<[Options?], MessageId>({
301302
(!nextRequireCall ||
302303
!containsNodeOrEqual(nextStatement, nextRequireCall))
303304
) {
304-
checkForNewLine(statementWithRequireCall, nextStatement, 'require')
305+
let nextComment
306+
if (
307+
'comments' in statementWithRequireCall.parent &&
308+
statementWithRequireCall.parent.comments !== undefined &&
309+
options.considerComments
310+
) {
311+
const endLine = node.loc.end.line
312+
nextComment = statementWithRequireCall.parent.comments.find(
313+
o =>
314+
o.loc.start.line >= endLine &&
315+
o.loc.start.line <= endLine + options.count + 1,
316+
)
317+
}
318+
319+
if (nextComment && nextComment !== undefined) {
320+
commentAfterImport(
321+
statementWithRequireCall,
322+
nextComment,
323+
'require',
324+
)
325+
} else {
326+
checkForNewLine(
327+
statementWithRequireCall,
328+
nextStatement,
329+
'require',
330+
)
331+
}
305332
}
306333
}
307334
},

src/rules/no-duplicates.ts

+26
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,32 @@ function getFix(
200200

201201
const fixes = []
202202

203+
if (shouldAddSpecifiers && preferInline && first.importKind === 'type') {
204+
// `import type {a} from './foo'` → `import {type a} from './foo'`
205+
const typeIdentifierToken = tokens.find(
206+
token => token.type === 'Identifier' && token.value === 'type',
207+
)
208+
if (typeIdentifierToken) {
209+
fixes.push(
210+
fixer.removeRange([
211+
typeIdentifierToken.range[0],
212+
typeIdentifierToken.range[1] + 1,
213+
]),
214+
)
215+
}
216+
217+
for (const identifier of tokens.filter(token =>
218+
firstExistingIdentifiers.has(token.value),
219+
)) {
220+
fixes.push(
221+
fixer.replaceTextRange(
222+
[identifier.range[0], identifier.range[1]],
223+
`type ${identifier.value}`,
224+
),
225+
)
226+
}
227+
}
228+
203229
if (openBrace == null && shouldAddSpecifiers && shouldAddDefault()) {
204230
// `import './foo'` → `import def, {...} from './foo'`
205231
fixes.push(

test/rules/newline-after-import.spec.ts

+43-6
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ ruleTester.run('newline-after-import', rule, {
260260
options: [{ count: 4, exactCount: true }],
261261
},
262262
{
263-
code: `var foo = require('foo-module');\n\n\n\n// Some random comment\nvar foo = 'bar';`,
263+
code: `var foo = require('foo-module');\n\n\n\n\n// Some random comment\nvar foo = 'bar';`,
264264
languageOptions: {
265265
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
266266
},
@@ -479,6 +479,21 @@ ruleTester.run('newline-after-import', rule, {
479479
parserOptions: { ecmaVersion: 2015, sourceType: 'module' },
480480
},
481481
},
482+
{
483+
code: `var foo = require('foo-module');\n\n\n// Some random comment\nvar foo = 'bar';`,
484+
options: [{ count: 2, considerComments: true }],
485+
},
486+
{
487+
code: `var foo = require('foo-module');\n\n\n/**\n * Test comment\n */\nvar foo = 'bar';`,
488+
options: [{ count: 2, considerComments: true }],
489+
},
490+
{
491+
code: `const foo = require('foo');\n\n\n// some random comment\nconst bar = function() {};`,
492+
options: [{ count: 2, exactCount: true, considerComments: true }],
493+
languageOptions: {
494+
parserOptions: { ecmaVersion: 2015 },
495+
},
496+
},
482497
],
483498

484499
invalid: [
@@ -1054,17 +1069,39 @@ ruleTester.run('newline-after-import', rule, {
10541069
},
10551070
},
10561071
{
1057-
code: `const foo = require('foo');\n\n\n// some random comment\nconst bar = function() {};`,
1058-
output: null,
1059-
options: [{ count: 2, exactCount: true, considerComments: true }],
1072+
code: `var foo = require('foo-module');\nvar foo = require('foo-module');\n\n// Some random comment\nvar foo = 'bar';`,
1073+
output: `var foo = require('foo-module');\nvar foo = require('foo-module');\n\n\n// Some random comment\nvar foo = 'bar';`,
1074+
errors: [
1075+
{
1076+
line: 2,
1077+
column: 1,
1078+
messageId: `newline`,
1079+
},
1080+
],
1081+
languageOptions: {
1082+
parserOptions: {
1083+
ecmaVersion: 2015,
1084+
sourceType: 'module',
1085+
},
1086+
},
1087+
options: [{ considerComments: true, count: 2 }],
1088+
},
1089+
{
1090+
code: `var foo = require('foo-module');\n\n/**\n * Test comment\n */\nvar foo = 'bar';`,
1091+
output: `var foo = require('foo-module');\n\n\n/**\n * Test comment\n */\nvar foo = 'bar';`,
10601092
errors: [
10611093
{
10621094
line: 1,
10631095
column: 1,
1064-
...getRequireError(2),
1096+
messageId: `newline`,
10651097
},
10661098
],
1067-
languageOptions: { parserOptions: { ecmaVersion: 2015 } },
1099+
languageOptions: {
1100+
parserOptions: {
1101+
ecmaVersion: 2015,
1102+
},
1103+
},
1104+
options: [{ considerComments: true, count: 2 }],
10681105
},
10691106
],
10701107
})

test/rules/no-duplicates.spec.ts

+18
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,24 @@ describe('TypeScript', () => {
757757
},
758758
],
759759
}),
760+
test({
761+
code: "import type {x} from 'foo'; import {type y} from 'foo'",
762+
...parserConfig,
763+
options: [{ 'prefer-inline': true }],
764+
output: `import {type x,type y} from 'foo'; `,
765+
errors: [
766+
{
767+
line: 1,
768+
column: 22,
769+
message: "'foo' imported multiple times.",
770+
},
771+
{
772+
line: 1,
773+
column: 50,
774+
message: "'foo' imported multiple times.",
775+
},
776+
],
777+
}),
760778
test({
761779
code: "import {type x} from 'foo'; import type {y} from 'foo'",
762780
...parserConfig,

0 commit comments

Comments
 (0)