Skip to content

Commit

Permalink
Merge branch 'main' into no-cycle-skip-scc-docs
Browse files Browse the repository at this point in the history
  • Loading branch information
soryy708 authored Sep 25, 2024
2 parents 72db115 + 5c9757c commit 9ecd059
Show file tree
Hide file tree
Showing 19 changed files with 179 additions and 86 deletions.
3 changes: 2 additions & 1 deletion .nycrc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"resolvers/*/test",
"scripts",
"memo-parser",
"lib"
"lib",
"examples"
]
}
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
### Fixed
- `ExportMap` / flat config: include `languageOptions` in context ([#3052], thanks [@michaelfaith])
- [`no-named-as-default`]: Allow using an identifier if the export is both a named and a default export ([#3032], thanks [@akwodkiewicz])
- [`export`]: False positive for exported overloaded functions in TS ([#3065], thanks [@liuxingbaoyu])

### Changed
- [Docs] [`no-relative-packages`]: fix typo ([#3066], thanks [@joshuaobrien])

## [2.30.0] - 2024-09-02

Expand Down Expand Up @@ -1136,6 +1140,8 @@ for info on changes for earlier releases.

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

[#3066]: https://github.com/import-js/eslint-plugin-import/pull/3066
[#3065]: https://github.com/import-js/eslint-plugin-import/pull/3065
[#3052]: https://github.com/import-js/eslint-plugin-import/pull/3052
[#3043]: https://github.com/import-js/eslint-plugin-import/pull/3043
[#3036]: https://github.com/import-js/eslint-plugin-import/pull/3036
Expand Down Expand Up @@ -1840,6 +1846,7 @@ for info on changes for earlier releases.
[@johnthagen]: https://github.com/johnthagen
[@jonboiser]: https://github.com/jonboiser
[@josh]: https://github.com/josh
[@joshuaobrien]: https://github.com/joshuaobrien
[@JounQin]: https://github.com/JounQin
[@jquense]: https://github.com/jquense
[@jseminck]: https://github.com/jseminck
Expand Down Expand Up @@ -1870,6 +1877,7 @@ for info on changes for earlier releases.
[@lilling]: https://github.com/lilling
[@ljharb]: https://github.com/ljharb
[@ljqx]: https://github.com/ljqx
[@liuxingbaoyu]: https://github.com/liuxingbaoyu
[@lo1tuma]: https://github.com/lo1tuma
[@loganfsmyth]: https://github.com/loganfsmyth
[@luczsoma]: https://github.com/luczsoma
Expand Down
3 changes: 1 addition & 2 deletions docs/rules/no-relative-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

Use this rule to prevent importing packages through relative paths.

It's useful in Yarn/Lerna workspaces, were it's possible to import a sibling
package using `../package` relative path, while direct `package` is the correct one.
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.

## Examples

Expand Down
1 change: 1 addition & 0 deletions examples/flat/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default [
'import/no-dynamic-require': 'warn',
'import/no-nodejs-modules': 'warn',
'import/no-unused-modules': ['warn', { unusedExports: true }],
'import/no-cycle': 'warn',
},
},
];
3 changes: 3 additions & 0 deletions examples/flat/src/depth-zero.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { foo } from "./es6/depth-one-dynamic";

foo();
3 changes: 3 additions & 0 deletions examples/flat/src/es6/depth-one-dynamic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function foo() {}

export const bar = () => import("../depth-zero").then(({foo}) => foo);
1 change: 1 addition & 0 deletions examples/legacy/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ module.exports = {
'import/no-dynamic-require': 'warn',
'import/no-nodejs-modules': 'warn',
'import/no-unused-modules': ['warn', { unusedExports: true }],
'import/no-cycle': 'warn',
},
};
3 changes: 3 additions & 0 deletions examples/legacy/src/depth-zero.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { foo } from "./es6/depth-one-dynamic";

foo();
3 changes: 3 additions & 0 deletions examples/legacy/src/es6/depth-one-dynamic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function foo() {}

export const bar = () => import("../depth-zero").then(({ foo }) => foo);
19 changes: 17 additions & 2 deletions src/rules/consistent-type-specifier-style.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ function isComma(token) {
return token.type === 'Punctuator' && token.value === ',';
}

/**
* @param {import('eslint').Rule.Fix[]} fixes
* @param {import('eslint').Rule.RuleFixer} fixer
* @param {import('eslint').SourceCode.SourceCode} sourceCode
* @param {(ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier)[]} specifiers
* */
function removeSpecifiers(fixes, fixer, sourceCode, specifiers) {
for (const specifier of specifiers) {
// remove the trailing comma
Expand All @@ -17,6 +23,7 @@ function removeSpecifiers(fixes, fixer, sourceCode, specifiers) {
}
}

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

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'suggestion',
Expand Down Expand Up @@ -102,6 +110,7 @@ module.exports = {

// prefer-top-level
return {
/** @param {import('estree').ImportDeclaration} node */
ImportDeclaration(node) {
if (
// already top-level is valid
Expand All @@ -120,9 +129,13 @@ module.exports = {
return;
}

/** @type {typeof node.specifiers} */
const typeSpecifiers = [];
/** @type {typeof node.specifiers} */
const typeofSpecifiers = [];
/** @type {typeof node.specifiers} */
const valueSpecifiers = [];
/** @type {typeof node.specifiers[number]} */
let defaultSpecifier = null;
for (const specifier of node.specifiers) {
if (specifier.type === 'ImportDefaultSpecifier') {
Expand All @@ -144,6 +157,7 @@ module.exports = {
const newImports = `${typeImport}\n${typeofImport}`.trim();

if (typeSpecifiers.length + typeofSpecifiers.length === node.specifiers.length) {
/** @type {('type' | 'typeof')[]} */
// all specifiers have inline specifiers - so we replace the entire import
const kind = [].concat(
typeSpecifiers.length > 0 ? 'type' : [],
Expand All @@ -162,14 +176,15 @@ module.exports = {
});
} else {
// remove specific specifiers and insert new imports for them
for (const specifier of typeSpecifiers.concat(typeofSpecifiers)) {
typeSpecifiers.concat(typeofSpecifiers).forEach((specifier) => {
context.report({
node: specifier,
message: 'Prefer using a top-level {{kind}}-only import instead of inline {{kind}} specifiers.',
data: {
kind: specifier.importKind,
},
fix(fixer) {
/** @type {import('eslint').Rule.Fix[]} */
const fixes = [];

// if there are no value specifiers, then the other report fixer will be called, not this one
Expand Down Expand Up @@ -215,7 +230,7 @@ module.exports = {
);
},
});
}
});
}
},
};
Expand Down
48 changes: 16 additions & 32 deletions src/rules/export.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import ExportMapBuilder from '../exportMap/builder';
import recursivePatternCapture from '../exportMap/patternCapture';
import docsUrl from '../docsUrl';
import includes from 'array-includes';
import flatMap from 'array.prototype.flatmap';

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

/**
* Detect function overloads like:
* remove function overloads like:
* ```ts
* export function foo(a: number);
* export function foo(a: string);
* export function foo(a: number|string) { return a; }
* ```
* @param {Set<Object>} nodes
* @returns {boolean}
*/
function isTypescriptFunctionOverloads(nodes) {
const nodesArr = Array.from(nodes);

const idents = flatMap(
nodesArr,
(node) => node.declaration && (
node.declaration.type === 'TSDeclareFunction' // eslint 6+
|| node.declaration.type === 'TSEmptyBodyFunctionDeclaration' // eslint 4-5
)
? node.declaration.id.name
: [],
);
if (new Set(idents).size !== idents.length) {
return true;
}

const types = new Set(nodesArr.map((node) => node.parent.type));
if (!types.has('TSDeclareFunction')) {
return false;
}
if (types.size === 1) {
return true;
}
if (types.size === 2 && types.has('FunctionDeclaration')) {
return true;
}
return false;
function removeTypescriptFunctionOverloads(nodes) {
nodes.forEach((node) => {
const declType = node.type === 'ExportDefaultDeclaration' ? node.declaration.type : node.parent.type;
if (
// eslint 6+
declType === 'TSDeclareFunction'
// eslint 4-5
|| declType === 'TSEmptyBodyFunctionDeclaration'
) {
nodes.delete(node);
}
});
}

/**
Expand Down Expand Up @@ -227,9 +209,11 @@ module.exports = {
'Program:exit'() {
for (const [, named] of namespace) {
for (const [name, nodes] of named) {
removeTypescriptFunctionOverloads(nodes);

if (nodes.size <= 1) { continue; }

if (isTypescriptFunctionOverloads(nodes) || isTypescriptNamespaceMerging(nodes)) { continue; }
if (isTypescriptNamespaceMerging(nodes)) { continue; }

for (const node of nodes) {
if (shouldSkipTypescriptNamespace(node, nodes)) { continue; }
Expand Down
4 changes: 2 additions & 2 deletions src/rules/no-cycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ module.exports = {
*/
if (path === myPath && toTraverse.length > 0) { return true; }
if (route.length + 1 < maxDepth) {
for (const { source } of toTraverse) {
toTraverse.forEach(({ source }) => {
untraversed.push({ mget: getter, route: route.concat(source) });
}
});
}
}
}
Expand Down
Loading

0 comments on commit 9ecd059

Please sign in to comment.