Skip to content

Commit 72b9c3d

Browse files
cherryblossom000ljharb
authored andcommitted
[Fix] no-cycle: fix false negative when file imports a type after importing a value in Flow
This fixes this situation: `a.js`: ```js import { foo } from './b' ``` `b.js`: ```js // @flow import { bar, type Baz } from './a' ``` Previously, `no-cycle` would have not reported a dependency cycle for the import in `a.js`, even though `b.js` is importing `bar`, which is not a type import, from `a.js`. This commit fixes that.
1 parent 30bba6a commit 72b9c3d

File tree

4 files changed

+15
-5
lines changed

4 files changed

+15
-5
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
99
### Fixed
1010
- [`no-restricted-paths`]: fix false positive matches ([#2090], thanks [@malykhinvi])
1111
- [`no-cycle`]: ignore imports where imported file only imports types of importing file ([#2083], thanks [@cherryblossom000])
12+
- [`no-cycle`]: fix false negative when file imports a type after importing a value in Flow ([#2083], thanks [@cherryblossom000])
1213

1314
### Changed
1415
- [Docs] Add `no-relative-packages` to list of to the list of rules ([#2075], thanks [@arvigeus])

src/ExportMap.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,9 @@ ExportMap.parse = function (path, content, context) {
495495
if (n.type === 'ImportDeclaration') {
496496
// import type { Foo } (TS and Flow)
497497
const declarationIsType = n.importKind === 'type';
498-
let isOnlyImportingTypes = declarationIsType;
498+
// import './foo' or import {} from './foo' (both 0 specifiers) is a side effect and
499+
// shouldn't be considered to be just importing types
500+
let specifiersOnlyImportingTypes = n.specifiers.length;
499501
const importedSpecifiers = new Set();
500502
n.specifiers.forEach(specifier => {
501503
if (supportedImportTypes.has(specifier.type)) {
@@ -506,11 +508,10 @@ ExportMap.parse = function (path, content, context) {
506508
}
507509

508510
// import { type Foo } (Flow)
509-
if (!declarationIsType) {
510-
isOnlyImportingTypes = specifier.importKind === 'type';
511-
}
511+
specifiersOnlyImportingTypes =
512+
specifiersOnlyImportingTypes && specifier.importKind === 'type';
512513
});
513-
captureDependency(n, isOnlyImportingTypes, importedSpecifiers);
514+
captureDependency(n, declarationIsType || specifiersOnlyImportingTypes, importedSpecifiers);
514515

515516
const ns = n.specifiers.find(s => s.type === 'ImportNamespaceSpecifier');
516517
if (ns) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// @flow
2+
3+
import { foo, type BarType } from './depth-zero'

tests/src/rules/no-cycle.js

+5
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ ruleTester.run('no-cycle', rule, {
8787
code: 'import { foo } from "./depth-one"',
8888
errors: [error(`Dependency cycle detected.`)],
8989
}),
90+
test({
91+
code: 'import { bar } from "./flow-types-some-type-imports"',
92+
parser: require.resolve('babel-eslint'),
93+
errors: [error(`Dependency cycle detected.`)],
94+
}),
9095
test({
9196
code: 'import { foo } from "cycles/external/depth-one"',
9297
errors: [error(`Dependency cycle detected.`)],

0 commit comments

Comments
 (0)