eslint-plugin-stratified-design 0.7.0 → 0.8.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/helpers/{lowerLevelImports/4 layer.js → common.js} +14 -2
- package/lib/helpers/lowerLevelImports/1 layer.js +5 -5
- package/lib/helpers/lowerLevelImports/2 layer.js +4 -4
- package/lib/helpers/lowerLevelImports/3 layer.js +34 -9
- package/lib/helpers/stratifiedImports/1 layer.js +86 -0
- package/lib/helpers/stratifiedImports/2 layer.js +156 -0
- package/lib/helpers/stratifiedImports/index.js +24 -0
- package/lib/helpers/type.js +8 -0
- package/lib/rules/lower-level-imports.js +3 -2
- package/lib/rules/no-same-level-funcs.js +6 -12
- package/lib/rules/stratified-imports.js +172 -0
- package/mocked/stratified-imports/.stratified.json +12 -0
- package/mocked/stratified-imports/layerB/.stratified.json +1 -0
- package/mocked/stratified-imports/layerJ/.stratified.json +1 -0
- package/package.json +1 -1
- package/tests/lib/helpers/stratified-imports.js +138 -0
- package/tests/lib/rules/stratified-imports.js +228 -0
|
@@ -29,8 +29,9 @@ const resolvePath = p.resolve;
|
|
|
29
29
|
const parsePath = p.parse;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* @
|
|
33
|
-
* @param {
|
|
32
|
+
* @template T
|
|
33
|
+
* @param {T[]} array
|
|
34
|
+
* @param {(item: T) => boolean} callback
|
|
34
35
|
* @returns {number}
|
|
35
36
|
*/
|
|
36
37
|
const findLastIndex = (array, callback) => {
|
|
@@ -49,6 +50,16 @@ const equal = (array1, array2) => {
|
|
|
49
50
|
return array1.every((item, index) => item === array2[index]);
|
|
50
51
|
};
|
|
51
52
|
|
|
53
|
+
/**
|
|
54
|
+
* @template T
|
|
55
|
+
* @param {T[]} array
|
|
56
|
+
* @param {number} index
|
|
57
|
+
* @returns {T}
|
|
58
|
+
*/
|
|
59
|
+
const readArray = (array, index) => {
|
|
60
|
+
return index >= 0 ? array[index] : array[array.length + index];
|
|
61
|
+
};
|
|
62
|
+
|
|
52
63
|
module.exports = {
|
|
53
64
|
toRelative,
|
|
54
65
|
toSegments,
|
|
@@ -58,4 +69,5 @@ module.exports = {
|
|
|
58
69
|
parsePath,
|
|
59
70
|
findLastIndex,
|
|
60
71
|
equal,
|
|
72
|
+
readArray,
|
|
61
73
|
};
|
|
@@ -3,7 +3,7 @@ const { report: reportError } = require("./2 layer");
|
|
|
3
3
|
const {
|
|
4
4
|
isNodeModule,
|
|
5
5
|
findLevel: findLayerLevel,
|
|
6
|
-
|
|
6
|
+
hasBarrier: hasBarrierBetween,
|
|
7
7
|
removeAlias: removeAliasFromModuleSource,
|
|
8
8
|
} = require("./3 layer");
|
|
9
9
|
const {
|
|
@@ -12,7 +12,7 @@ const {
|
|
|
12
12
|
toPath,
|
|
13
13
|
resolvePath,
|
|
14
14
|
parsePath,
|
|
15
|
-
} = require("
|
|
15
|
+
} = require("../common");
|
|
16
16
|
|
|
17
17
|
const FINISHED = "finished";
|
|
18
18
|
|
|
@@ -191,10 +191,10 @@ const reportHasProperLevel = (
|
|
|
191
191
|
filePath
|
|
192
192
|
) => {
|
|
193
193
|
const findLevel = findLayerLevel(structure);
|
|
194
|
-
const
|
|
194
|
+
const hasBarrier = hasBarrierBetween(structure, fileLevel);
|
|
195
195
|
|
|
196
196
|
/**
|
|
197
|
-
* @param {import('
|
|
197
|
+
* @param {import('../type').Node} node
|
|
198
198
|
* @param {string} modulePath
|
|
199
199
|
*/
|
|
200
200
|
return (node, modulePath) => {
|
|
@@ -222,7 +222,7 @@ const reportHasProperLevel = (
|
|
|
222
222
|
return FINISHED;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
if (
|
|
225
|
+
if (hasBarrier(moduleLevel)) {
|
|
226
226
|
report("barrier");
|
|
227
227
|
return FINISHED;
|
|
228
228
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
const { isNodeModule } = require("./3 layer");
|
|
2
|
-
const { toRelative } = require("
|
|
2
|
+
const { toRelative } = require("../common");
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Report eslint error
|
|
6
|
-
* @param {import('
|
|
6
|
+
* @param {import('../type.js').Context} context
|
|
7
7
|
* @param {string} rootDir
|
|
8
|
-
*
|
|
8
|
+
* @param {string} filePath
|
|
9
9
|
*/
|
|
10
10
|
const report = (context, rootDir, filePath) => {
|
|
11
11
|
/**
|
|
12
|
-
* @param {import('
|
|
12
|
+
* @param {import('../type').Node} node
|
|
13
13
|
* @param {string} messageId
|
|
14
14
|
* @param {string} modulePath
|
|
15
15
|
*/
|
|
@@ -4,11 +4,36 @@ const {
|
|
|
4
4
|
joinPath,
|
|
5
5
|
resolvePath,
|
|
6
6
|
toRelative,
|
|
7
|
-
} = require("
|
|
7
|
+
} = require("../common");
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* @
|
|
10
|
+
* @typedef {{
|
|
11
|
+
* structure: Array<
|
|
12
|
+
* string | { name: string, barrier?: boolean, interface?: boolean, nodeModule?: boolean, isNodeModule?: boolean }
|
|
13
|
+
* >,
|
|
14
|
+
* root: string,
|
|
15
|
+
* aliases: Record<string, string>,
|
|
16
|
+
* exclude: string[],
|
|
17
|
+
* include: string[],
|
|
18
|
+
* useLevelNumber: boolean
|
|
19
|
+
* isIndexHighest: boolean
|
|
20
|
+
* }} Options
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {{
|
|
25
|
+
* name: string;
|
|
26
|
+
* barrier?: boolean | undefined;
|
|
27
|
+
* interface?: boolean | undefined;
|
|
28
|
+
* nodeModule?: boolean | undefined;
|
|
29
|
+
* isNodeModule?: boolean | undefined;
|
|
30
|
+
* }[]} Structure
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {Options} options
|
|
11
35
|
* @param {string} rootDir
|
|
36
|
+
* @returns {Structure}
|
|
12
37
|
*/
|
|
13
38
|
const createStructure = (options, rootDir) => {
|
|
14
39
|
return options.structure.map((layer) => {
|
|
@@ -20,7 +45,7 @@ const createStructure = (options, rootDir) => {
|
|
|
20
45
|
};
|
|
21
46
|
|
|
22
47
|
/**
|
|
23
|
-
* @param {
|
|
48
|
+
* @param {Options} options
|
|
24
49
|
*/
|
|
25
50
|
const createAliases = (options) => {
|
|
26
51
|
return Object.keys(options.aliases)
|
|
@@ -63,7 +88,7 @@ const isNodeModule = (rootDir) => {
|
|
|
63
88
|
|
|
64
89
|
/**
|
|
65
90
|
* Find the layer level for a module
|
|
66
|
-
* @param {
|
|
91
|
+
* @param {Structure} structure
|
|
67
92
|
* @returns the level of the module with `path`
|
|
68
93
|
*/
|
|
69
94
|
const findLevel = (structure) => {
|
|
@@ -83,25 +108,25 @@ const findLevel = (structure) => {
|
|
|
83
108
|
|
|
84
109
|
/**
|
|
85
110
|
* Check if there is an interface between file layer and module layer
|
|
86
|
-
* @param {
|
|
111
|
+
* @param {Structure} structure
|
|
87
112
|
* @param {number} fileLevel
|
|
88
113
|
*/
|
|
89
|
-
const
|
|
114
|
+
const hasBarrier = (structure, fileLevel) => {
|
|
90
115
|
/**
|
|
91
116
|
* @param {number} moduleLevel
|
|
92
117
|
*/
|
|
93
118
|
return (moduleLevel) => {
|
|
94
|
-
const
|
|
119
|
+
const layerBarrier = structure
|
|
95
120
|
.slice(fileLevel + 1, moduleLevel)
|
|
96
121
|
.find((layer) => layer.barrier || layer.interface);
|
|
97
|
-
return Boolean(
|
|
122
|
+
return Boolean(layerBarrier);
|
|
98
123
|
};
|
|
99
124
|
};
|
|
100
125
|
|
|
101
126
|
module.exports = {
|
|
102
127
|
isNodeModule,
|
|
103
128
|
findLevel,
|
|
104
|
-
|
|
129
|
+
hasBarrier,
|
|
105
130
|
createStructure,
|
|
106
131
|
createAliases,
|
|
107
132
|
removeAlias,
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
resolvePath,
|
|
5
|
+
joinPath,
|
|
6
|
+
toSegments,
|
|
7
|
+
readArray,
|
|
8
|
+
findLastIndex,
|
|
9
|
+
} = require("../common");
|
|
10
|
+
const {
|
|
11
|
+
toStructure,
|
|
12
|
+
replaceAlias,
|
|
13
|
+
readRawStructure,
|
|
14
|
+
findLevel,
|
|
15
|
+
findLayerWithSimilarPath,
|
|
16
|
+
} = require("./2 layer");
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} fileDir
|
|
20
|
+
*/
|
|
21
|
+
const createStructure = (fileDir) => {
|
|
22
|
+
const parentFileDir = joinPath(fileDir, "..");
|
|
23
|
+
|
|
24
|
+
const rawStructure = readRawStructure(fileDir);
|
|
25
|
+
const parentRawStructure = readRawStructure(parentFileDir);
|
|
26
|
+
|
|
27
|
+
const fileDirname = `${readArray(toSegments(fileDir), -1)}/`;
|
|
28
|
+
const theIndex = findLastIndex(parentRawStructure, (rawLayers) => {
|
|
29
|
+
return Boolean(
|
|
30
|
+
rawLayers.find((rawLayer) => {
|
|
31
|
+
if (typeof rawLayer === "string")
|
|
32
|
+
return `${rawLayer}/`.startsWith(fileDirname);
|
|
33
|
+
return `${rawLayer.name}/`.startsWith(fileDirname);
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const structure = toStructure(rawStructure, fileDir);
|
|
39
|
+
const parentStructure = toStructure(parentRawStructure, parentFileDir);
|
|
40
|
+
|
|
41
|
+
for (let i = theIndex + 1; i <= parentRawStructure.length - 1; i++) {
|
|
42
|
+
structure.push(parentStructure[i]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return structure;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} cwd
|
|
50
|
+
* @param {string} fileDir
|
|
51
|
+
* @param {import('./2 layer').Aliases} aliases
|
|
52
|
+
*/
|
|
53
|
+
const createModulePath = (cwd, fileDir, aliases) => {
|
|
54
|
+
/**
|
|
55
|
+
* @param {string} moduleSourceWithAlias
|
|
56
|
+
*/
|
|
57
|
+
return (moduleSourceWithAlias) => {
|
|
58
|
+
const moduleSource = replaceAlias(
|
|
59
|
+
cwd,
|
|
60
|
+
fileDir,
|
|
61
|
+
aliases
|
|
62
|
+
)(moduleSourceWithAlias);
|
|
63
|
+
const isNodeModule = moduleSource.startsWith(".") === false;
|
|
64
|
+
return isNodeModule ? moduleSource : resolvePath(fileDir, moduleSource);
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {import('./2 layer').Structure} structure
|
|
70
|
+
*/
|
|
71
|
+
const findLevelInChild = (structure) => {
|
|
72
|
+
/**
|
|
73
|
+
* @param {string} path
|
|
74
|
+
*/
|
|
75
|
+
return (path) => {
|
|
76
|
+
const layer = findLayerWithSimilarPath(structure)(path);
|
|
77
|
+
if (!layer) return null;
|
|
78
|
+
const fileDir = layer.name;
|
|
79
|
+
const rawChildStructure = readRawStructure(fileDir);
|
|
80
|
+
const childStructure = toStructure(rawChildStructure, fileDir);
|
|
81
|
+
const childLevel = findLevel(childStructure)(path);
|
|
82
|
+
return childLevel !== null ? findLevel(structure)(fileDir) : null;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
module.exports = { createStructure, createModulePath, findLevelInChild };
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { readFileSync } = require("fs");
|
|
4
|
+
const {
|
|
5
|
+
resolvePath,
|
|
6
|
+
toRelative,
|
|
7
|
+
joinPath,
|
|
8
|
+
toSegments,
|
|
9
|
+
toPath,
|
|
10
|
+
} = require("../common");
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {{
|
|
14
|
+
* name: string;
|
|
15
|
+
* barrier?: boolean;
|
|
16
|
+
* nodeModule?: boolean;
|
|
17
|
+
* }} Layer
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @typedef {string|Layer} RawLayer
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {Layer[][]} Structure
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {RawLayer[][]} RawStructure
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {{alias: string, path: string}[]} Aliases
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} fileDir
|
|
38
|
+
* @returns {RawStructure}
|
|
39
|
+
*/
|
|
40
|
+
const readRawStructure = (fileDir) => {
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(
|
|
43
|
+
readFileSync(resolvePath(fileDir, "./.stratified.json"), "utf-8")
|
|
44
|
+
);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {RawStructure} rawStructure
|
|
52
|
+
* @param {string} fileDir
|
|
53
|
+
* @return {Structure}
|
|
54
|
+
*/
|
|
55
|
+
const toStructure = (rawStructure, fileDir) => {
|
|
56
|
+
return rawStructure.map((rawLayers) => {
|
|
57
|
+
return rawLayers.map((rawLayer) => {
|
|
58
|
+
const layer =
|
|
59
|
+
typeof rawLayer === "string" ? { name: rawLayer } : rawLayer;
|
|
60
|
+
return layer.nodeModule
|
|
61
|
+
? layer
|
|
62
|
+
: { ...layer, name: joinPath(fileDir, layer.name) };
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {Record<string, string>} rawAliases
|
|
69
|
+
* @returns {Aliases}
|
|
70
|
+
*/
|
|
71
|
+
const createAliases = (rawAliases) => {
|
|
72
|
+
return Object.keys(rawAliases)
|
|
73
|
+
.sort((a, b) => b.length - a.length)
|
|
74
|
+
.map((alias) => ({ alias, path: rawAliases[alias] }));
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Replace an alias into the corresponding path
|
|
79
|
+
* @param {string} cwd
|
|
80
|
+
* @param {string} fileDir
|
|
81
|
+
* @param {Aliases} aliases
|
|
82
|
+
*/
|
|
83
|
+
const replaceAlias = (cwd, fileDir, aliases) => {
|
|
84
|
+
/**
|
|
85
|
+
* @param {string} moduleSource
|
|
86
|
+
*/
|
|
87
|
+
return (moduleSource) => {
|
|
88
|
+
const { alias, path } =
|
|
89
|
+
aliases.find(({ alias }) => moduleSource.startsWith(alias)) || {};
|
|
90
|
+
if (!alias) return moduleSource;
|
|
91
|
+
const modulePath = resolvePath(cwd, moduleSource.replace(alias, path));
|
|
92
|
+
return toRelative(fileDir, modulePath);
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Find the layer level for a module
|
|
98
|
+
* @param {Structure} structure
|
|
99
|
+
* @returns the level of the module with `path`
|
|
100
|
+
*/
|
|
101
|
+
const findLevel = (structure) => {
|
|
102
|
+
/**
|
|
103
|
+
* @param {string} path
|
|
104
|
+
*/
|
|
105
|
+
return (path) => {
|
|
106
|
+
const level = structure.findIndex((layers) =>
|
|
107
|
+
Boolean(layers.find((layer) => layer.name === path))
|
|
108
|
+
);
|
|
109
|
+
return level >= 0 ? level : null;
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {import('./2 layer').Structure} structure
|
|
115
|
+
*/
|
|
116
|
+
const findLayerWithSimilarPath = (structure) => {
|
|
117
|
+
/**
|
|
118
|
+
* @param {string} path
|
|
119
|
+
* @return {Layer | null}
|
|
120
|
+
*/
|
|
121
|
+
return (path) => {
|
|
122
|
+
return (
|
|
123
|
+
toSegments(path).reduce((theLayer, _, index, segments) => {
|
|
124
|
+
if (theLayer) return theLayer;
|
|
125
|
+
const similarPath = toPath(segments.slice(0, segments.length - index));
|
|
126
|
+
/**
|
|
127
|
+
* @type {Layer}
|
|
128
|
+
*/
|
|
129
|
+
return structure.reduce((foundLayer, layers) => {
|
|
130
|
+
if (foundLayer) return foundLayer;
|
|
131
|
+
return layers.find((layer) => layer.name === similarPath);
|
|
132
|
+
}, undefined);
|
|
133
|
+
}, undefined) || null
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @param {string} cwd
|
|
140
|
+
*/
|
|
141
|
+
const isNotRegisteredNodeModule = (cwd) => {
|
|
142
|
+
/**
|
|
143
|
+
* @param {string} modulePath
|
|
144
|
+
*/
|
|
145
|
+
return (modulePath) => modulePath.startsWith(cwd) === false;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
module.exports = {
|
|
149
|
+
readRawStructure,
|
|
150
|
+
toStructure,
|
|
151
|
+
createAliases,
|
|
152
|
+
replaceAlias,
|
|
153
|
+
findLevel,
|
|
154
|
+
findLayerWithSimilarPath,
|
|
155
|
+
isNotRegisteredNodeModule,
|
|
156
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
createStructure,
|
|
5
|
+
createModulePath,
|
|
6
|
+
findLevelInChild,
|
|
7
|
+
} = require("./1 layer");
|
|
8
|
+
const {
|
|
9
|
+
createAliases,
|
|
10
|
+
findLevel,
|
|
11
|
+
isNotRegisteredNodeModule,
|
|
12
|
+
} = require("./2 layer");
|
|
13
|
+
|
|
14
|
+
const { parseFileSource } = require("../lowerLevelImports");
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
createStructure,
|
|
18
|
+
createModulePath,
|
|
19
|
+
createAliases,
|
|
20
|
+
parseFileSource,
|
|
21
|
+
findLevel,
|
|
22
|
+
isNotRegisteredNodeModule,
|
|
23
|
+
findLevelInChild,
|
|
24
|
+
};
|
|
@@ -117,15 +117,16 @@ module.exports = {
|
|
|
117
117
|
include: ["**/*.{js,ts,jsx,tsx}"],
|
|
118
118
|
aliases: {},
|
|
119
119
|
useLevelNumber: false,
|
|
120
|
+
isIndexHighest: false,
|
|
120
121
|
...(context.options[0] || {}),
|
|
121
122
|
};
|
|
122
123
|
|
|
123
|
-
const cwd = context.
|
|
124
|
+
const cwd = context.cwd;
|
|
124
125
|
const rootDir = createRootDir(cwd, options);
|
|
125
126
|
|
|
126
127
|
const { fileDir, filePath, isExcludedFile } = parseFileSource(
|
|
127
128
|
options,
|
|
128
|
-
context.
|
|
129
|
+
context.filename
|
|
129
130
|
);
|
|
130
131
|
|
|
131
132
|
const structure = createStructure(options, rootDir);
|
|
@@ -35,17 +35,11 @@ const deriveLevel = (sourceCode, nodeOrToken) => {
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
38
|
+
* @param {SourceCode} sourceCode
|
|
39
39
|
* @param {Node} node
|
|
40
40
|
*/
|
|
41
|
-
const traceAncestor = (node) => {
|
|
42
|
-
|
|
43
|
-
let nextParent = node.parent;
|
|
44
|
-
while (nextParent.type !== "Program") {
|
|
45
|
-
parent = parent.parent;
|
|
46
|
-
nextParent = parent.parent;
|
|
47
|
-
}
|
|
48
|
-
return parent;
|
|
41
|
+
const traceAncestor = (sourceCode, node) => {
|
|
42
|
+
return sourceCode.getAncestors(node)[1];
|
|
49
43
|
};
|
|
50
44
|
|
|
51
45
|
/** @type {import('eslint').Rule.RuleModule} */
|
|
@@ -83,7 +77,7 @@ module.exports = {
|
|
|
83
77
|
...(context.options[0] || {}),
|
|
84
78
|
};
|
|
85
79
|
|
|
86
|
-
const fileSource = path.resolve(context.
|
|
80
|
+
const fileSource = path.resolve(context.filename);
|
|
87
81
|
|
|
88
82
|
const isIncludedFile = options.include.find((pattern) =>
|
|
89
83
|
minimatch(fileSource, pattern)
|
|
@@ -100,7 +94,7 @@ module.exports = {
|
|
|
100
94
|
*/
|
|
101
95
|
const levels = {};
|
|
102
96
|
|
|
103
|
-
const sourceCode = context.
|
|
97
|
+
const sourceCode = context.sourceCode;
|
|
104
98
|
|
|
105
99
|
return {
|
|
106
100
|
Program(node) {
|
|
@@ -127,7 +121,7 @@ module.exports = {
|
|
|
127
121
|
const calleeLevel = levels[node.callee.name];
|
|
128
122
|
if (calleeLevel === undefined) return;
|
|
129
123
|
if (calleeLevel !== null) {
|
|
130
|
-
const ancestor = traceAncestor(node);
|
|
124
|
+
const ancestor = traceAncestor(sourceCode, node);
|
|
131
125
|
const ancestorLevel = deriveLevel(sourceCode, ancestor);
|
|
132
126
|
if (ancestorLevel !== null && ancestorLevel < calleeLevel) return;
|
|
133
127
|
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Require that lower level modules be imported (stratified-imports)
|
|
3
|
+
* @author Hodoug Joung
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
const { parsePath } = require("../helpers/common");
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
// Requirements
|
|
11
|
+
//------------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
const helper = require("../helpers/stratifiedImports");
|
|
14
|
+
|
|
15
|
+
//------------------------------------------------------------------------------
|
|
16
|
+
// Rule Definition
|
|
17
|
+
//------------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
20
|
+
module.exports = {
|
|
21
|
+
meta: {
|
|
22
|
+
type: "problem",
|
|
23
|
+
fixable: "code",
|
|
24
|
+
schema: {
|
|
25
|
+
type: "array",
|
|
26
|
+
items: [
|
|
27
|
+
{
|
|
28
|
+
type: "object",
|
|
29
|
+
properties: {
|
|
30
|
+
aliases: {
|
|
31
|
+
type: "object",
|
|
32
|
+
patternProperties: {
|
|
33
|
+
["^.+$"]: {
|
|
34
|
+
type: "string",
|
|
35
|
+
pattern: "^\\.{1,2}(/[^/]+)*/?$",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
additionalProperties: false,
|
|
39
|
+
},
|
|
40
|
+
exclude: {
|
|
41
|
+
type: "array",
|
|
42
|
+
items: [{ type: "string" }],
|
|
43
|
+
},
|
|
44
|
+
include: {
|
|
45
|
+
type: "array",
|
|
46
|
+
items: [{ type: "string" }],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
additionalProperties: false,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
additionalItems: false,
|
|
53
|
+
},
|
|
54
|
+
messages: {
|
|
55
|
+
"not-lower-level": "'{{module}}' is NOT LOWER level than '{{file}}'",
|
|
56
|
+
barrier:
|
|
57
|
+
"An ABSTRACT BARRIER prevents '{{file}}' from importing '{{module}}'",
|
|
58
|
+
"not-registered": "'{{file}}' does NOT registered at .stratified.json",
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
create(context) {
|
|
62
|
+
const options = {
|
|
63
|
+
exclude: ["**/*.{test,spec}.{js,ts,jsx,tsx}"],
|
|
64
|
+
include: ["**/*.{js,ts,jsx,tsx}"],
|
|
65
|
+
aliases: {},
|
|
66
|
+
...(context.options[0] || {}),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const { fileDir, filePath, isExcludedFile } = helper.parseFileSource(
|
|
70
|
+
options,
|
|
71
|
+
context.filename
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (isExcludedFile) return {};
|
|
75
|
+
|
|
76
|
+
const structure = helper.createStructure(fileDir);
|
|
77
|
+
|
|
78
|
+
const fileLevel = helper.findLevel(structure)(filePath);
|
|
79
|
+
|
|
80
|
+
const createModulePath = helper.createModulePath(
|
|
81
|
+
context.cwd,
|
|
82
|
+
fileDir,
|
|
83
|
+
helper.createAliases(options.aliases)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const findLevel = helper.findLevel(structure);
|
|
87
|
+
|
|
88
|
+
const findLevelInChild = helper.findLevelInChild(structure);
|
|
89
|
+
|
|
90
|
+
const isNotRegisteredNodeModule = helper.isNotRegisteredNodeModule(
|
|
91
|
+
context.cwd
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @param {number} moduleLevel
|
|
96
|
+
*/
|
|
97
|
+
const hasBarrier = (moduleLevel) => {
|
|
98
|
+
const layerBarrier = structure
|
|
99
|
+
.slice(fileLevel + 1, moduleLevel)
|
|
100
|
+
.find((layers) => layers.find((layer) => layer.barrier));
|
|
101
|
+
return Boolean(layerBarrier);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @param {import('../helpers/type').Node} node
|
|
106
|
+
* @param {'not-lower-level'|'barrier'|'not-registered'} messageId
|
|
107
|
+
* @param {string | undefined} modulePath
|
|
108
|
+
*/
|
|
109
|
+
const reportError = (node, messageId, modulePath) => {
|
|
110
|
+
context.report({
|
|
111
|
+
node,
|
|
112
|
+
messageId,
|
|
113
|
+
data: {
|
|
114
|
+
file: parsePath(filePath).name,
|
|
115
|
+
...(modulePath ? { module: parsePath(modulePath).name } : {}),
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {import('../helpers/type').Node} node
|
|
122
|
+
* @returns
|
|
123
|
+
*/
|
|
124
|
+
const report = (node) => {
|
|
125
|
+
// TODO: invalid .stratified.json
|
|
126
|
+
|
|
127
|
+
if (fileLevel === null) {
|
|
128
|
+
reportError(node, "not-registered");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const modulePath = createModulePath(node.source.value);
|
|
133
|
+
const moduleLevel = (() => {
|
|
134
|
+
const level = findLevel(modulePath);
|
|
135
|
+
return level !== null
|
|
136
|
+
? level
|
|
137
|
+
: isNotRegisteredNodeModule(modulePath)
|
|
138
|
+
? "notRegisteredNodeModule"
|
|
139
|
+
: findLevelInChild(modulePath);
|
|
140
|
+
})();
|
|
141
|
+
|
|
142
|
+
if (moduleLevel === "notRegisteredNodeModule") return;
|
|
143
|
+
|
|
144
|
+
if (moduleLevel === null) {
|
|
145
|
+
reportError(node, "not-lower-level", modulePath);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (hasBarrier(moduleLevel)) {
|
|
150
|
+
reportError(node, "barrier", modulePath);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (moduleLevel <= fileLevel) {
|
|
155
|
+
reportError(node, "not-lower-level", modulePath);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
ImportDeclaration(node) {
|
|
162
|
+
report(node);
|
|
163
|
+
},
|
|
164
|
+
ExportNamedDeclaration(node) {
|
|
165
|
+
if (node.source) report(node);
|
|
166
|
+
},
|
|
167
|
+
ExportAllDeclaration(node) {
|
|
168
|
+
if (node.source) report(node);
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[["layerBA"], ["layerBB"]]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[["entryJA", "entryJB"], ["layerJC"]]
|
package/package.json
CHANGED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview test for helpers/stratified-imports
|
|
3
|
+
* @author Hodoug Joung
|
|
4
|
+
*/
|
|
5
|
+
"use strict";
|
|
6
|
+
|
|
7
|
+
//------------------------------------------------------------------------------
|
|
8
|
+
// Requirements
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
const assert = require("assert");
|
|
12
|
+
const {
|
|
13
|
+
findLevel,
|
|
14
|
+
findLayerWithSimilarPath,
|
|
15
|
+
toStructure,
|
|
16
|
+
createAliases,
|
|
17
|
+
replaceAlias,
|
|
18
|
+
} = require("../../../lib/helpers/stratifiedImports/2 layer");
|
|
19
|
+
const {
|
|
20
|
+
createModulePath,
|
|
21
|
+
} = require("../../../lib/helpers/stratifiedImports/1 layer");
|
|
22
|
+
|
|
23
|
+
//------------------------------------------------------------------------------
|
|
24
|
+
// Tests
|
|
25
|
+
//------------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
describe("helpers/stratified-imports", () => {
|
|
28
|
+
describe("toStructure()", () => {
|
|
29
|
+
const fileDir = "/src";
|
|
30
|
+
const testCases = [
|
|
31
|
+
{ rawStructure: [["layer"]], structure: [[{ name: "/src/layer" }]] },
|
|
32
|
+
{
|
|
33
|
+
rawStructure: [[{ name: "layer" }]],
|
|
34
|
+
structure: [[{ name: "/src/layer" }]],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
rawStructure: [[{ name: "layer", barrier: true }]],
|
|
38
|
+
structure: [[{ name: "/src/layer", barrier: true }]],
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
testCases.forEach(({ rawStructure, structure }) => {
|
|
42
|
+
it(`${rawStructure} -> ${structure}`, () => {
|
|
43
|
+
assert.deepEqual(toStructure(rawStructure, fileDir), structure);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("createAliases()", () => {
|
|
49
|
+
const testCases = [
|
|
50
|
+
{
|
|
51
|
+
rawAliases: { "@/": "./src/" },
|
|
52
|
+
aliases: [{ alias: "@/", path: "./src/" }],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
rawAliases: { "@/": "./src/", "@layer/": "./src/layer/" },
|
|
56
|
+
aliases: [
|
|
57
|
+
{ alias: "@layer/", path: "./src/layer/" },
|
|
58
|
+
{ alias: "@/", path: "./src/" },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
testCases.forEach(({ rawAliases, aliases }) => {
|
|
63
|
+
it(`${rawAliases} -> ${aliases}`, () => {
|
|
64
|
+
assert.deepEqual(createAliases(rawAliases), aliases);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("replaceAlias()", () => {
|
|
70
|
+
const cwd = "/proj";
|
|
71
|
+
const fileDir = "/proj/src/layerA";
|
|
72
|
+
const aliases = [{ alias: "@/", path: "./src/" }];
|
|
73
|
+
const testCases = [
|
|
74
|
+
{ moduleSource: "@/layerA/layerAA", relPath: "./layerAA" },
|
|
75
|
+
{ moduleSource: "nodeModule", relPath: "nodeModule" },
|
|
76
|
+
];
|
|
77
|
+
testCases.forEach(({ moduleSource, relPath }) => {
|
|
78
|
+
it(`${moduleSource} -> ${relPath}`, () => {
|
|
79
|
+
assert.equal(
|
|
80
|
+
replaceAlias(cwd, fileDir, aliases)(moduleSource),
|
|
81
|
+
relPath
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("findLevel()", () => {
|
|
88
|
+
const structure = [[{ name: "/src/layerA" }], [{ name: "/src/layerB" }]];
|
|
89
|
+
const testCases = [
|
|
90
|
+
{ path: "/src/layerA", level: 0 },
|
|
91
|
+
{ path: "/src/layerB", level: 1 },
|
|
92
|
+
{ path: "/src/layerA/entry", level: null },
|
|
93
|
+
];
|
|
94
|
+
testCases.forEach(({ path, level }) => {
|
|
95
|
+
it(`The level of ${path} is ${level}`, () => {
|
|
96
|
+
assert.equal(findLevel(structure)(path), level);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("findLayerWithSimilarPath()", () => {
|
|
102
|
+
const structure = [[{ name: "/src/layerA" }], [{ name: "/src/layerB" }]];
|
|
103
|
+
const testCases = [
|
|
104
|
+
{ path: "/src/layerA", layer: structure[0][0] },
|
|
105
|
+
{ path: "/src/layerB", layer: structure[1][0] },
|
|
106
|
+
{ path: "/src/layerA/entry", layer: structure[0][0] },
|
|
107
|
+
];
|
|
108
|
+
testCases.forEach(({ path, layer }) => {
|
|
109
|
+
it(`The level of ${path} is ${JSON.stringify(layer)}`, () => {
|
|
110
|
+
assert.equal(findLayerWithSimilarPath(structure)(path), layer);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe("createModulePath()", () => {
|
|
116
|
+
const cwd = "/proj";
|
|
117
|
+
const fileDir = "/proj/src/layerA";
|
|
118
|
+
const aliases = [{ alias: "@/", path: "./src/" }];
|
|
119
|
+
const testCases = [
|
|
120
|
+
{
|
|
121
|
+
moduleSource: "@/layerA/layerAA",
|
|
122
|
+
modulePath: "/proj/src/layerA/layerAA",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
moduleSource: "nodeModule",
|
|
126
|
+
modulePath: "nodeModule",
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
testCases.forEach(({ moduleSource, modulePath }) => {
|
|
130
|
+
it(`${moduleSource} -> ${modulePath}`, () => {
|
|
131
|
+
assert.equal(
|
|
132
|
+
createModulePath(cwd, fileDir, aliases)(moduleSource),
|
|
133
|
+
modulePath
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview test for lower-level-imports
|
|
3
|
+
* @author Hodoug Joung
|
|
4
|
+
*/
|
|
5
|
+
"use strict";
|
|
6
|
+
|
|
7
|
+
//------------------------------------------------------------------------------
|
|
8
|
+
// Requirements
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
const rule = require("../../../lib/rules/stratified-imports");
|
|
12
|
+
const RuleTester = require("eslint").RuleTester;
|
|
13
|
+
|
|
14
|
+
//------------------------------------------------------------------------------
|
|
15
|
+
// Tests
|
|
16
|
+
//------------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
// .stratified.json
|
|
20
|
+
[
|
|
21
|
+
["layerA"],
|
|
22
|
+
["layerB"],
|
|
23
|
+
[{ "name": "layerC", "barrier": true }],
|
|
24
|
+
["layerD"],
|
|
25
|
+
[{ "name": "nodeModuleE", "nodeModule": "true" }],
|
|
26
|
+
["layerF"],
|
|
27
|
+
["layerG", "layerH"],
|
|
28
|
+
["layerI"],
|
|
29
|
+
["layerJ"],
|
|
30
|
+
["layerK"]
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
// layerB/.stratified.json
|
|
34
|
+
[
|
|
35
|
+
["layerBA"],
|
|
36
|
+
["layerBB"]
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
// layerJ/.stratified.json
|
|
40
|
+
[
|
|
41
|
+
["entryJA", "entryJB"],
|
|
42
|
+
["layerJC"]
|
|
43
|
+
]
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
const ruleTester = new RuleTester({
|
|
47
|
+
parserOptions: { ecmaVersion: 2022, sourceType: "module" },
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
ruleTester.run("stratified-imports", rule, {
|
|
51
|
+
valid: [
|
|
52
|
+
{
|
|
53
|
+
code: "import { func } from './layerB'",
|
|
54
|
+
filename: "./mocked/stratified-imports/layerA.js",
|
|
55
|
+
options: [],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
code: "import { func } from './layerC'",
|
|
59
|
+
filename: "./mocked/stratified-imports/layerA.js",
|
|
60
|
+
options: [],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
code: "import { func } from 'notRegisteredNodeModule'",
|
|
64
|
+
filename: "./mocked/stratified-imports/layerA.js",
|
|
65
|
+
options: [],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
code: "import { func } from 'nodeModuleE'",
|
|
69
|
+
filename: "./mocked/stratified-imports/layerD.js",
|
|
70
|
+
options: [],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
code: "import { func } from './layerH'",
|
|
74
|
+
filename: "./mocked/stratified-imports/layerF.js",
|
|
75
|
+
options: [],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
code: "import { func } from './layerI'",
|
|
79
|
+
filename: "./mocked/stratified-imports/layerH.js",
|
|
80
|
+
options: [],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
code: "import { func } from './layerBB'",
|
|
84
|
+
filename: "./mocked/stratified-imports/layerB/layerBA.js",
|
|
85
|
+
options: [],
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
code: "import { func } from '../layerC'",
|
|
89
|
+
filename: "./mocked/stratified-imports/layerB/layerBA.js",
|
|
90
|
+
options: [],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
code: "import { func } from './layerJC'",
|
|
94
|
+
filename: "./mocked/stratified-imports/layerJ/entryJA.js",
|
|
95
|
+
options: [],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
code: "import { func } from './layerJC'",
|
|
99
|
+
filename: "./mocked/stratified-imports/layerJ/entryJB.js",
|
|
100
|
+
options: [],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
code: "import { func } from '../layerK'",
|
|
104
|
+
filename: "./mocked/stratified-imports/layerJ/layerJC.js",
|
|
105
|
+
options: [],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
code: "import { func } from '@/layerB'",
|
|
109
|
+
filename: "./mocked/stratified-imports/layerA.js",
|
|
110
|
+
options: [{ aliases: { "@/": "./mocked/stratified-imports/" } }],
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
code: "import { func } from './notRegisteredLayer'",
|
|
114
|
+
filename: "./mocked/stratified-imports/layerA.test.js",
|
|
115
|
+
options: [],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
code: "import { func } from './notRegisteredLayer'",
|
|
119
|
+
filename: "./mocked/stratified-imports/layerA.js",
|
|
120
|
+
options: [{ include: ["**/*.ts"] }],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
code: "import { func } from './notRegisteredLayer'",
|
|
124
|
+
filename: "./mocked/stratified-imports/layerA.js",
|
|
125
|
+
options: [{ exclude: ["**/*.js"] }],
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
code: "import { func } from './notRegisteredLayer'",
|
|
129
|
+
filename: "./mocked/stratified-imports/layerA.js",
|
|
130
|
+
options: [{ include: ["**/*.js"], exclude: ["**/layerA.js"] }],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
code: "import { func } from './layerJ/entryJA'",
|
|
134
|
+
filename: "./mocked/stratified-imports/layerI.js",
|
|
135
|
+
options: [],
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
invalid: [
|
|
139
|
+
{
|
|
140
|
+
code: "import { func } from './layerA'",
|
|
141
|
+
filename: "./mocked/stratified-imports/notRegisteredLayer.js",
|
|
142
|
+
options: [],
|
|
143
|
+
errors: [
|
|
144
|
+
{
|
|
145
|
+
messageId: "not-registered",
|
|
146
|
+
data: { file: "notRegisteredLayer" },
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
code: "import { func } from './layerA'",
|
|
152
|
+
filename: "./mocked/stratified-imports/layerB.js",
|
|
153
|
+
options: [],
|
|
154
|
+
errors: [
|
|
155
|
+
{
|
|
156
|
+
messageId: "not-lower-level",
|
|
157
|
+
data: { module: "layerA", file: "layerB" },
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
code: "import { func } from './layerD'",
|
|
163
|
+
filename: "./mocked/stratified-imports/layerB.js",
|
|
164
|
+
options: [],
|
|
165
|
+
errors: [
|
|
166
|
+
{
|
|
167
|
+
messageId: "barrier",
|
|
168
|
+
data: { module: "layerD", file: "layerB" },
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
code: "import { func } from './layerBA'",
|
|
174
|
+
filename: "./mocked/stratified-imports/layerB/layerBB.js",
|
|
175
|
+
options: [],
|
|
176
|
+
errors: [
|
|
177
|
+
{
|
|
178
|
+
messageId: "not-lower-level",
|
|
179
|
+
data: { module: "layerBA", file: "layerBB" },
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
code: "import { func } from '../layerA'",
|
|
185
|
+
filename: "./mocked/stratified-imports/layerB/layerBA.js",
|
|
186
|
+
options: [],
|
|
187
|
+
errors: [
|
|
188
|
+
{
|
|
189
|
+
messageId: "not-lower-level",
|
|
190
|
+
data: { module: "layerA", file: "layerBA" },
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
code: "import { func } from './entryJA'",
|
|
196
|
+
filename: "./mocked/stratified-imports/layerJ/layerJC.js",
|
|
197
|
+
options: [],
|
|
198
|
+
errors: [
|
|
199
|
+
{
|
|
200
|
+
messageId: "not-lower-level",
|
|
201
|
+
data: { module: "entryJA", file: "layerJC" },
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
code: "import { func } from '../layerI'",
|
|
207
|
+
filename: "./mocked/stratified-imports/layerJ/layerJC.js",
|
|
208
|
+
options: [],
|
|
209
|
+
errors: [
|
|
210
|
+
{
|
|
211
|
+
messageId: "not-lower-level",
|
|
212
|
+
data: { module: "layerI", file: "layerJC" },
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
code: "import { func } from './layerJ/notRegisteredEntry'",
|
|
218
|
+
filename: "./mocked/stratified-imports/layerI.js",
|
|
219
|
+
options: [],
|
|
220
|
+
errors: [
|
|
221
|
+
{
|
|
222
|
+
messageId: "not-lower-level",
|
|
223
|
+
data: { module: "notRegisteredEntry", file: "layerI" },
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
});
|