eslint-plugin-stratified-design
Version:
ESlint rules for stratified design
283 lines (230 loc) • 6.23 kB
Markdown
# Require that lower-level modules be imported (lower-level-imports)
This rule enforces the requirement for importing lower-level modules. Here's an explanation:
- Functions in the same file within the same folder are considered to be at the same level.
- Child folders are considered to be at a lower level than the parent folder.
- Installed modules (node modules) are considered to be at the lowest level.
However, you can modify this hierarchy using the `structure` option.
This rule works correctly on POSIX systems, where the path segment separator is `/`. It will be updated to work well on Windows systems in the future.
### Options
The syntax to specify the level structure is as follows:
```json
{
"stratified-design/lower-level-imports": [
"error",
{
"structure": ["layer1", "layer2", "layer3"]
}
]
}
```
In the folder array, the file or folder on the left is considered to be at a higher level than the one on the right.
To designate a layer as an abstract barrier, set `barrier` to `true`:
```json
{
"stratified-design/lower-level-imports": [
"error",
{
"structure": ["layer1", { "name": "layer2", "barrier": true }, "layer3"]
}
]
}
```
A language layer can 'pass' any abstract barrier:
```json
{
"stratified-design/lower-level-imports": [
"error",
{
"structure": [
"layer1",
{ "name": "layer2", "barrier": true },
{ "name": "layer3", "language": true }
]
}
]
}
```
For the 'abstract barrier,' refer to "[Grokking Simplicity](https://grokkingsimplicity.com)."
To locate a node module in the structure, set `nodeModule` to `true`:
```json
{
"stratified-design/lower-level-imports": [
"error",
{
"structure": [
"layer1",
{ "name": "nodeModule", "nodeModule": true },
"layer3"
]
}
]
}
```
The default root directory is the current working directory. To change the root directory, use the `root` option:
```json
{
"stratified-design/lower-level-imports": [
"error",
{
"structure": ["layer1", "layer2", "layer3"],
"root": "./src"
}
]
}
```
If the name of an imported module has an alias, register the alias using the `aliases` option:
```json
{
"stratified-design/lower-level-imports": [
"error",
{
"structure": ["layer1", "layer2", "layer3"],
"aliases": { "@": "./src" }
}
]
}
```
If you want to register the level of a layer by 'number,' set the option `useLevelNumber` to `true`:
```json
{
"stratified-design/lower-level-imports": ["error", { "useLevelNumber": true }]
}
```
The options `structure` and `useLevelNumber` can be used together.
An `index.xxx` file can be the highest level layer of sibling files when the option `isIndexHighest` is set to `true`:
```json
{
"stratified-design/lower-level-imports": ["error", { "isIndexHighest": true }]
}
```
You can register the files to apply the rule (`lower-level-imports`) using the `include` and `exclude` options:
```json
{
"stratified-design/lower-level-imports": [
"error",
{ "include": ["**/*.js"], "exclude": ["**/*.test.js"] }
]
}
```
The default is as follows:
```json
{
"include": ["**/*.{js,ts,jsx,tsx}"],
"exclude": ["**/*.{spec,test}.{js,ts,jsx,tsx}"]
}
```
Imported modules are excluded from the rule (`lower-level-imports`) using the `excludeImports` option:
```json
{
"stratified-design/lower-level-imports": [
"error",
{ "excludeImports": ["**/*.css"] }
]
}
```
Explicitly imported types are excluded using `ignoreType` option:
```json
{
"stratified-design/stratified-imports": ["error", { "ignoreType": true }]
}
```
```js
import type { SomeType } from "./type"; // Explicitly imported types are excluded
```
## Rule Details
If a file structure is as follows:
```
src/
┣ layer1.js
┣ layer2/
┃ ┣ index.js
┃ ┣ file.js
┃ ┣ otherFile.js
┃ ┗ subFolder/
┃ layer3/
┃ ┣ entry.js
┃ ┣ 1 layer.js
┃ ┗ 2 layer.js
┗ layer4.js
```
Examples of **incorrect** code for this rule:
```js
/* "lower-level-imports": ["error"] */
// ./src/layer1.js
import { func } from "../layer2/file";
```
```js
/* "lower-level-imports": ["error"] */
// ./src/layer2/file.js
import { func } from "./otherFile";
```
```js
/* "lower-level-imports": ["error", { "structure": ["layer1", "layer2"] }] */
// ./src/layer2/file.js
import { func } from "../layer1";
```
```js
/* "lower-level-imports": ["error", {
"structure": ["layer1", { name: "layer2", barrier: true }, "layer3"]
}] */
// ./src/layer1.js
import { func } from "../layer3/entry";
```
```js
/* "lower-level-imports": ["error", {
"structure": ["layer1", { "name": "nodeModule", "nodeModule": true }, "layer3"],
}] */
// ./src/layer3/entry.js
import { func } from "nodeModule";
```
```js
/* "lower-level-imports": ["error", { "useLevelNumber": true }}] */
// ./src/layer1.js
import { func } from "layer3/1 layer";
```
Examples of **correct** code for this rule:
```js
/* "lower-level-imports": ["error"] */
// ./src/layer2/file.js
import { func } from "./subFolder/file";
```
```js
/* "lower-level-imports": ["error", { "structure": ["layer1", "layer2"] }] */
// ./src/layer1.js
import { func } from "../layer2/file";
```
```js
/* "lower-level-imports": ["error", {
"structure": ["layer1", "layer2"],
"alias": { "@/": "./src/" }
}] */
// ./src/layer1.js
import { func } from "@/layer2/file";
```
```js
/* "lower-level-imports": ["error", { "useLevelNumber": true }] */
// ./src/layer3/1 layer.js
import { func } from "./2 layer";
```
```js
/* "lower-level-imports": ["error", { "useLevelNumber": true }] */
// ./src/layer3/entry.js
import { func } from "./1 layer";
```
```js
/* "lower-level-imports": ["error", { "structure": ["layer1", "layer3"], "useLevelNumber": true }] */
// ./src/layer.js
import { func } from "../layer3/entry";
```
```js
/* "lower-level-imports": ["error", { "isIndexHighest": true }] */
// ./src/layer2/index.js
import { func } from "./file";
```
```js
/* "lower-level-imports": ["error", {
"structure": ["layer1", { name: "layer2", barrier: true }, "layer3", { name: "layer4", language: true }]
}] */
// ./src/layer1.js
import { func } from "./layer4";
```