Skip to content

Commit 9ecd059

Browse files
authored
Merge branch 'main' into no-cycle-skip-scc-docs
2 parents 72db115 + 5c9757c commit 9ecd059

19 files changed

+179
-86
lines changed

.nycrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"resolvers/*/test",
1515
"scripts",
1616
"memo-parser",
17-
"lib"
17+
"lib",
18+
"examples"
1819
]
1920
}

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1212
### Fixed
1313
- `ExportMap` / flat config: include `languageOptions` in context ([#3052], thanks [@michaelfaith])
1414
- [`no-named-as-default`]: Allow using an identifier if the export is both a named and a default export ([#3032], thanks [@akwodkiewicz])
15+
- [`export`]: False positive for exported overloaded functions in TS ([#3065], thanks [@liuxingbaoyu])
16+
17+
### Changed
18+
- [Docs] [`no-relative-packages`]: fix typo ([#3066], thanks [@joshuaobrien])
1519

1620
## [2.30.0] - 2024-09-02
1721

@@ -1136,6 +1140,8 @@ for info on changes for earlier releases.
11361140

11371141
[`memo-parser`]: ./memo-parser/README.md
11381142

1143+
[#3066]: https://github.com/import-js/eslint-plugin-import/pull/3066
1144+
[#3065]: https://github.com/import-js/eslint-plugin-import/pull/3065
11391145
[#3052]: https://github.com/import-js/eslint-plugin-import/pull/3052
11401146
[#3043]: https://github.com/import-js/eslint-plugin-import/pull/3043
11411147
[#3036]: https://github.com/import-js/eslint-plugin-import/pull/3036
@@ -1840,6 +1846,7 @@ for info on changes for earlier releases.
18401846
[@johnthagen]: https://github.com/johnthagen
18411847
[@jonboiser]: https://github.com/jonboiser
18421848
[@josh]: https://github.com/josh
1849+
[@joshuaobrien]: https://github.com/joshuaobrien
18431850
[@JounQin]: https://github.com/JounQin
18441851
[@jquense]: https://github.com/jquense
18451852
[@jseminck]: https://github.com/jseminck
@@ -1870,6 +1877,7 @@ for info on changes for earlier releases.
18701877
[@lilling]: https://github.com/lilling
18711878
[@ljharb]: https://github.com/ljharb
18721879
[@ljqx]: https://github.com/ljqx
1880+
[@liuxingbaoyu]: https://github.com/liuxingbaoyu
18731881
[@lo1tuma]: https://github.com/lo1tuma
18741882
[@loganfsmyth]: https://github.com/loganfsmyth
18751883
[@luczsoma]: https://github.com/luczsoma

docs/rules/no-relative-packages.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66

77
Use this rule to prevent importing packages through relative paths.
88

9-
It's useful in Yarn/Lerna workspaces, were it's possible to import a sibling
10-
package using `../package` relative path, while direct `package` is the correct one.
9+
It's useful in Yarn/Lerna workspaces, where it's possible to import a sibling package using `../package` relative path, while direct `package` is the correct one.
1110

1211
## Examples
1312

examples/flat/eslint.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export default [
2020
'import/no-dynamic-require': 'warn',
2121
'import/no-nodejs-modules': 'warn',
2222
'import/no-unused-modules': ['warn', { unusedExports: true }],
23+
'import/no-cycle': 'warn',
2324
},
2425
},
2526
];

examples/flat/src/depth-zero.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { foo } from "./es6/depth-one-dynamic";
2+
3+
foo();
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function foo() {}
2+
3+
export const bar = () => import("../depth-zero").then(({foo}) => foo);

examples/legacy/.eslintrc.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ module.exports = {
2020
'import/no-dynamic-require': 'warn',
2121
'import/no-nodejs-modules': 'warn',
2222
'import/no-unused-modules': ['warn', { unusedExports: true }],
23+
'import/no-cycle': 'warn',
2324
},
2425
};

examples/legacy/src/depth-zero.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { foo } from "./es6/depth-one-dynamic";
2+
3+
foo();
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function foo() {}
2+
3+
export const bar = () => import("../depth-zero").then(({ foo }) => foo);

src/rules/consistent-type-specifier-style.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ function isComma(token) {
66
return token.type === 'Punctuator' && token.value === ',';
77
}
88

9+
/**
10+
* @param {import('eslint').Rule.Fix[]} fixes
11+
* @param {import('eslint').Rule.RuleFixer} fixer
12+
* @param {import('eslint').SourceCode.SourceCode} sourceCode
13+
* @param {(ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier)[]} specifiers
14+
* */
915
function removeSpecifiers(fixes, fixer, sourceCode, specifiers) {
1016
for (const specifier of specifiers) {
1117
// remove the trailing comma
@@ -17,6 +23,7 @@ function removeSpecifiers(fixes, fixer, sourceCode, specifiers) {
1723
}
1824
}
1925

26+
/** @type {(node: import('estree').Node, sourceCode: import('eslint').SourceCode.SourceCode, specifiers: (ImportSpecifier | ImportNamespaceSpecifier)[], kind: 'type' | 'typeof') => string} */
2027
function getImportText(
2128
node,
2229
sourceCode,
@@ -38,6 +45,7 @@ function getImportText(
3845
return `import ${kind} {${names.join(', ')}} from ${sourceString};`;
3946
}
4047

48+
/** @type {import('eslint').Rule.RuleModule} */
4149
module.exports = {
4250
meta: {
4351
type: 'suggestion',
@@ -102,6 +110,7 @@ module.exports = {
102110

103111
// prefer-top-level
104112
return {
113+
/** @param {import('estree').ImportDeclaration} node */
105114
ImportDeclaration(node) {
106115
if (
107116
// already top-level is valid
@@ -120,9 +129,13 @@ module.exports = {
120129
return;
121130
}
122131

132+
/** @type {typeof node.specifiers} */
123133
const typeSpecifiers = [];
134+
/** @type {typeof node.specifiers} */
124135
const typeofSpecifiers = [];
136+
/** @type {typeof node.specifiers} */
125137
const valueSpecifiers = [];
138+
/** @type {typeof node.specifiers[number]} */
126139
let defaultSpecifier = null;
127140
for (const specifier of node.specifiers) {
128141
if (specifier.type === 'ImportDefaultSpecifier') {
@@ -144,6 +157,7 @@ module.exports = {
144157
const newImports = `${typeImport}\n${typeofImport}`.trim();
145158

146159
if (typeSpecifiers.length + typeofSpecifiers.length === node.specifiers.length) {
160+
/** @type {('type' | 'typeof')[]} */
147161
// all specifiers have inline specifiers - so we replace the entire import
148162
const kind = [].concat(
149163
typeSpecifiers.length > 0 ? 'type' : [],
@@ -162,14 +176,15 @@ module.exports = {
162176
});
163177
} else {
164178
// remove specific specifiers and insert new imports for them
165-
for (const specifier of typeSpecifiers.concat(typeofSpecifiers)) {
179+
typeSpecifiers.concat(typeofSpecifiers).forEach((specifier) => {
166180
context.report({
167181
node: specifier,
168182
message: 'Prefer using a top-level {{kind}}-only import instead of inline {{kind}} specifiers.',
169183
data: {
170184
kind: specifier.importKind,
171185
},
172186
fix(fixer) {
187+
/** @type {import('eslint').Rule.Fix[]} */
173188
const fixes = [];
174189

175190
// if there are no value specifiers, then the other report fixer will be called, not this one
@@ -215,7 +230,7 @@ module.exports = {
215230
);
216231
},
217232
});
218-
}
233+
});
219234
}
220235
},
221236
};

src/rules/export.js

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import ExportMapBuilder from '../exportMap/builder';
22
import recursivePatternCapture from '../exportMap/patternCapture';
33
import docsUrl from '../docsUrl';
44
import includes from 'array-includes';
5-
import flatMap from 'array.prototype.flatmap';
65

76
/*
87
Notes on TypeScript namespaces aka TSModuleDeclaration:
@@ -27,42 +26,25 @@ const rootProgram = 'root';
2726
const tsTypePrefix = 'type:';
2827

2928
/**
30-
* Detect function overloads like:
29+
* remove function overloads like:
3130
* ```ts
3231
* export function foo(a: number);
3332
* export function foo(a: string);
34-
* export function foo(a: number|string) { return a; }
3533
* ```
3634
* @param {Set<Object>} nodes
37-
* @returns {boolean}
3835
*/
39-
function isTypescriptFunctionOverloads(nodes) {
40-
const nodesArr = Array.from(nodes);
41-
42-
const idents = flatMap(
43-
nodesArr,
44-
(node) => node.declaration && (
45-
node.declaration.type === 'TSDeclareFunction' // eslint 6+
46-
|| node.declaration.type === 'TSEmptyBodyFunctionDeclaration' // eslint 4-5
47-
)
48-
? node.declaration.id.name
49-
: [],
50-
);
51-
if (new Set(idents).size !== idents.length) {
52-
return true;
53-
}
54-
55-
const types = new Set(nodesArr.map((node) => node.parent.type));
56-
if (!types.has('TSDeclareFunction')) {
57-
return false;
58-
}
59-
if (types.size === 1) {
60-
return true;
61-
}
62-
if (types.size === 2 && types.has('FunctionDeclaration')) {
63-
return true;
64-
}
65-
return false;
36+
function removeTypescriptFunctionOverloads(nodes) {
37+
nodes.forEach((node) => {
38+
const declType = node.type === 'ExportDefaultDeclaration' ? node.declaration.type : node.parent.type;
39+
if (
40+
// eslint 6+
41+
declType === 'TSDeclareFunction'
42+
// eslint 4-5
43+
|| declType === 'TSEmptyBodyFunctionDeclaration'
44+
) {
45+
nodes.delete(node);
46+
}
47+
});
6648
}
6749

6850
/**
@@ -227,9 +209,11 @@ module.exports = {
227209
'Program:exit'() {
228210
for (const [, named] of namespace) {
229211
for (const [name, nodes] of named) {
212+
removeTypescriptFunctionOverloads(nodes);
213+
230214
if (nodes.size <= 1) { continue; }
231215

232-
if (isTypescriptFunctionOverloads(nodes) || isTypescriptNamespaceMerging(nodes)) { continue; }
216+
if (isTypescriptNamespaceMerging(nodes)) { continue; }
233217

234218
for (const node of nodes) {
235219
if (shouldSkipTypescriptNamespace(node, nodes)) { continue; }

src/rules/no-cycle.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,9 @@ module.exports = {
152152
*/
153153
if (path === myPath && toTraverse.length > 0) { return true; }
154154
if (route.length + 1 < maxDepth) {
155-
for (const { source } of toTraverse) {
155+
toTraverse.forEach(({ source }) => {
156156
untraversed.push({ mget: getter, route: route.concat(source) });
157-
}
157+
});
158158
}
159159
}
160160
}

0 commit comments

Comments
 (0)