-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add no-parent-barrel-import rule
- Loading branch information
Showing
10 changed files
with
190 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# import/no-parent-barrel-import | ||
|
||
<!-- end auto-generated rule header --> | ||
|
||
Forbid a module from importing from parent barrel file, as it often leads to runtime error `Cannot read ... of undefined`. | ||
|
||
It resolves the missing circular import check from [`no-self-import`], while being computationally cheap (see [`no-cycle`]). | ||
|
||
## Rule Details | ||
|
||
### Fail | ||
|
||
```js | ||
// @foo/index.ts | ||
export * from "./bar"; | ||
|
||
// @foo/bar/index.ts | ||
export * from "./baz"; | ||
export * from "./qux"; | ||
|
||
// @foo/bar/baz.ts (cannot read property `X` of undefined) | ||
import { T } from '../..'; // relative | ||
import { T } from '..'; // relative | ||
import { T } from '@foo'; // absolute | ||
import { T } from '@foo/bar'; // absolute | ||
|
||
export const X = T.X; | ||
|
||
// @foo/bar/qux.ts | ||
export enum T { | ||
X = "..." | ||
} | ||
``` | ||
|
||
### Pass | ||
|
||
```js | ||
// @foo/index.ts | ||
export * from "./bar"; | ||
|
||
// @foo/bar/index.ts | ||
export * from "./baz"; | ||
export * from "./qux"; | ||
|
||
// @foo/bar/baz.ts (relative import for code in `@foo`) | ||
import { T } from "./baz"; // relative | ||
import { T } from "@foo/bar/qux"; // absolute | ||
|
||
export const X = T.X; | ||
|
||
// @foo/bar/qux.ts | ||
export enum T { | ||
X = "..." | ||
} | ||
``` | ||
|
||
## Further Reading | ||
|
||
- [Related Discussion](https://github.com/import-js/eslint-plugin-import/pull/2318#issuecomment-1027807460) | ||
- Rule to detect that module imports itself: [`no-self-import`], [`no-cycle`] | ||
|
||
[`no-self-import`]: ./no-self-import.md | ||
[`no-cycle`]: ./no-cycle.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/** | ||
* @fileOverview Forbids a module from importing from parent barrel file | ||
* @author jonioni | ||
*/ | ||
|
||
const { parse } = require('path'); | ||
const resolve = require('eslint-module-utils/resolve').default; | ||
const moduleVisitor = require('eslint-module-utils/moduleVisitor').default; | ||
const docsUrl = require('../docsUrl').default; | ||
|
||
function isImportingFromParentBarrel(context, node, requireName) { | ||
let filePath; | ||
if (context.getPhysicalFilename) { | ||
filePath = context.getPhysicalFilename(); | ||
} else if (context.getFilename) { | ||
filePath = context.getFilename(); | ||
} | ||
const importPath = resolve(requireName, context); | ||
console.info(`File Path: ${filePath} and ${importPath}`); | ||
const importDetails = parse(importPath); | ||
if (importDetails.name === 'index' && filePath.startsWith(importDetails.dir)) { | ||
context.report({ | ||
node, | ||
message: 'Module imports from parent barrel file.', | ||
}); | ||
} | ||
} | ||
|
||
module.exports = { | ||
meta: { | ||
type: 'problem', | ||
docs: { | ||
category: 'Static analysis', | ||
description: 'Forbid a module from importing from parent barrel file.', | ||
recommended: true, | ||
url: docsUrl('no-parent-barrel-import'), | ||
}, | ||
schema: [], | ||
}, | ||
create(context) { | ||
return isImportingFromParentBarrel((source, node) => { | ||
moduleVisitor(context, node, source.value); | ||
}); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
// Used in `no-parent-barrel-import` tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
// Used in `no-parent-barrel-import` tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
// Used in `no-parent-barrel-import` tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Used in `no-parent-barrel-import` tests | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { test, testFilePath } from '../utils'; | ||
|
||
import { RuleTester } from 'eslint'; | ||
|
||
const ruleTester = new RuleTester(); | ||
const rule = require('rules/no-parent-barrel-import'); | ||
|
||
const error = { | ||
message: 'Module imports from parent barrel file.', | ||
}; | ||
|
||
ruleTester.run('no-parent-barrel-import', rule, { | ||
valid: [ | ||
test({ | ||
code: 'import _ from "lodash"', | ||
filename: testFilePath('./no-parent-barrel-import/index.js'), | ||
}), | ||
test({ | ||
code: 'import baz from "./baz"', | ||
filename: testFilePath('./no-parent-barrel-import/foo/bar.js'), | ||
}), | ||
test({ | ||
code: 'import bar from "./bar"', | ||
filename: testFilePath('./no-parent-barrel-import/foo/baz.js'), | ||
}), | ||
], | ||
invalid: [ | ||
test({ | ||
code: 'import baz from "."', | ||
errors: [error], | ||
filename: testFilePath('./no-parent-barrel-import/foo/bar.js'), | ||
}), | ||
test({ | ||
code: 'import baz from "../.."', | ||
errors: [error], | ||
filename: testFilePath('./no-parent-barrel-import/foo/bar.js'), | ||
}), | ||
test({ | ||
code: 'var foo = require("./index.js")', | ||
errors: [error], | ||
filename: testFilePath('./no-parent-barrel-import/foo/bar.js'), | ||
}), | ||
test({ | ||
code: 'var foo = require(".")', | ||
errors: [error], | ||
filename: testFilePath('./no-parent-barrel-import/foo/baz.js'), | ||
}), | ||
test({ | ||
code: 'var foo = require("..")', | ||
errors: [error], | ||
filename: testFilePath('./no-parent-barrel-import/foo/bar.js'), | ||
}), | ||
test({ | ||
code: 'var foo = require("././././")', | ||
errors: [error], | ||
filename: testFilePath('./no-parent-barrel-import/foo/index.js'), | ||
}), | ||
test({ | ||
code: 'var root = require("../../..")', | ||
errors: [error], | ||
filename: testFilePath('./no-parent-barrel-import/foo/index.js'), | ||
}), | ||
test({ | ||
code: 'var root = require("..")', | ||
errors: [error], | ||
filename: testFilePath('./no-parent-barrel-import/index.js'), | ||
}), | ||
|
||
], | ||
}); |