Skip to content

Commit

Permalink
[Fix] no-cycle: add ExportNamedDeclaration statements to dependencies
Browse files Browse the repository at this point in the history
Fixes #2461
  • Loading branch information
BenoitZugmeyer committed Jul 27, 2022
1 parent 0ef8cba commit 49218fa
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 19 deletions.
44 changes: 25 additions & 19 deletions src/ExportMap.js
Expand Up @@ -497,6 +497,28 @@ ExportMap.parse = function (path, content, context) {
m.reexports.set(s.exported.name, { local, getImport: () => resolveImport(nsource) });
}

function captureDependencyWithSpecifiers(n) {
// import type { Foo } (TS and Flow)
const declarationIsType = n.importKind === 'type';
// import './foo' or import {} from './foo' (both 0 specifiers) is a side effect and
// shouldn't be considered to be just importing types
let specifiersOnlyImportingTypes = n.specifiers.length;
const importedSpecifiers = new Set();
n.specifiers.forEach(specifier => {
if (supportedImportTypes.has(specifier.type)) {
importedSpecifiers.add(specifier.type);
}
if (specifier.type === 'ImportSpecifier') {
importedSpecifiers.add(specifier.imported.name || specifier.imported.value);
}

// import { type Foo } (Flow)
specifiersOnlyImportingTypes =
specifiersOnlyImportingTypes && specifier.importKind === 'type';
});
captureDependency(n, declarationIsType || specifiersOnlyImportingTypes, importedSpecifiers);
}

function captureDependency({ source }, isOnlyImportingTypes, importedSpecifiers = new Set()) {
if (source == null) return null;

Expand Down Expand Up @@ -583,25 +605,7 @@ ExportMap.parse = function (path, content, context) {

// capture namespaces in case of later export
if (n.type === 'ImportDeclaration') {
// import type { Foo } (TS and Flow)
const declarationIsType = n.importKind === 'type';
// import './foo' or import {} from './foo' (both 0 specifiers) is a side effect and
// shouldn't be considered to be just importing types
let specifiersOnlyImportingTypes = n.specifiers.length;
const importedSpecifiers = new Set();
n.specifiers.forEach(specifier => {
if (supportedImportTypes.has(specifier.type)) {
importedSpecifiers.add(specifier.type);
}
if (specifier.type === 'ImportSpecifier') {
importedSpecifiers.add(specifier.imported.name || specifier.imported.value);
}

// import { type Foo } (Flow)
specifiersOnlyImportingTypes =
specifiersOnlyImportingTypes && specifier.importKind === 'type';
});
captureDependency(n, declarationIsType || specifiersOnlyImportingTypes, importedSpecifiers);
captureDependencyWithSpecifiers(n);

const ns = n.specifiers.find(s => s.type === 'ImportNamespaceSpecifier');
if (ns) {
Expand All @@ -611,6 +615,8 @@ ExportMap.parse = function (path, content, context) {
}

if (n.type === 'ExportNamedDeclaration') {
captureDependencyWithSpecifiers(n);

// capture declaration
if (n.declaration != null) {
switch (n.declaration.type) {
Expand Down
1 change: 1 addition & 0 deletions tests/files/cycles/es6/depth-one-reexport.js
@@ -0,0 +1 @@
export { foo } from "../depth-zero";
5 changes: 5 additions & 0 deletions tests/src/rules/no-cycle.js
Expand Up @@ -166,6 +166,11 @@ ruleTester.run('no-cycle', rule, {
errors: [error(`Dependency cycle detected.`)],
options: [{ ...opts, amd: true }],
}),
test({
code: `import { foo } from "./${testDialect}/depth-one-reexport"`,
options: [{ ...opts }],
errors: [error(`Dependency cycle detected.`)],
}),
test({
code: `import { foo } from "./${testDialect}/depth-two"`,
options: [{ ...opts }],
Expand Down

0 comments on commit 49218fa

Please sign in to comment.