eslint-plugin-fsd-paths-check 0.0.17 → 0.0.18
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.
|
@@ -1,103 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const path = require("path");
|
|
4
|
-
const fs = require("fs");
|
|
5
|
-
const { allFsdLayers } = require("../constants/constants");
|
|
6
|
-
|
|
7
|
-
const calculateRelativePath = (fromFile, toFile) => {
|
|
8
|
-
const fromDir = path.dirname(fromFile);
|
|
9
|
-
let relative = path.relative(fromDir, toFile);
|
|
10
|
-
|
|
11
|
-
if (!relative.startsWith('.') && !relative.startsWith('/')) {
|
|
12
|
-
relative = `./${relative}`;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
relative = relative.replace(/\\/g, '/');
|
|
16
|
-
|
|
17
|
-
return relative;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const getFsdSlice = (filePath) => {
|
|
21
|
-
const normalized = filePath.replace(/\\/g, '/');
|
|
22
|
-
const segments = normalized.split('/');
|
|
23
|
-
const layerIndex = segments.findIndex(segment => allFsdLayers.includes(segment));
|
|
24
|
-
|
|
25
|
-
if (layerIndex === -1) return null;
|
|
26
|
-
if (layerIndex + 1 >= segments.length) return segments[layerIndex];
|
|
27
|
-
|
|
28
|
-
return `${segments[layerIndex]}/${segments[layerIndex + 1]}`;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
3
|
const isOnlyParentDirs = (importPath) => {
|
|
32
4
|
const normalized = importPath.replace(/\\/g, '/');
|
|
33
5
|
const segments = normalized.split('/').filter(s => s.length > 0);
|
|
34
6
|
return segments.every(s => s === '..');
|
|
35
7
|
};
|
|
36
8
|
|
|
37
|
-
const resolveTargetDirectory = (importPath, currentFile) => {
|
|
38
|
-
const currentDir = path.dirname(currentFile);
|
|
39
|
-
const normalized = importPath.replace(/\\/g, '/');
|
|
40
|
-
const parentCount = normalized.split('/').filter(s => s === '..').length;
|
|
41
|
-
|
|
42
|
-
let targetDir = currentDir;
|
|
43
|
-
for (let i = 0; i < parentCount; i++) {
|
|
44
|
-
targetDir = path.dirname(targetDir);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return targetDir;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const findReExportInIndex = (indexFilePath, exportedName) => {
|
|
51
|
-
try {
|
|
52
|
-
if (!fs.existsSync(indexFilePath)) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const content = fs.readFileSync(indexFilePath, 'utf-8');
|
|
57
|
-
|
|
58
|
-
const exportFromRegex = /export\s*\{\s*[^}]*?\b(\w+)\s*(?:as\s*\w+)?\s*\}\s*from\s*['"]([^'"]+)['"]/g;
|
|
59
|
-
|
|
60
|
-
let match;
|
|
61
|
-
while ((match = exportFromRegex.exec(content)) !== null) {
|
|
62
|
-
const [_, exportName, exportPath] = match;
|
|
63
|
-
if (exportName === exportedName) {
|
|
64
|
-
return exportPath;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const exportAllRegex = /export\s*\*\s*from\s*['"]([^'"]+)['"]/g;
|
|
69
|
-
while ((match = exportAllRegex.exec(content)) !== null) {
|
|
70
|
-
const [_, exportPath] = match;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return null;
|
|
74
|
-
} catch (error) {
|
|
75
|
-
console.warn(`Failed to read index.ts: ${error.message}`);
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const resolveActualFilePath = (basePath, relativeImport) => {
|
|
81
|
-
const resolved = path.resolve(basePath, relativeImport);
|
|
82
|
-
|
|
83
|
-
const extensions = ['', '.ts', '.tsx', '.js', '.jsx', '/index.ts', '/index.tsx'];
|
|
84
|
-
|
|
85
|
-
for (const ext of extensions) {
|
|
86
|
-
const fullPath = resolved + ext;
|
|
87
|
-
if (fs.existsSync(fullPath)) {
|
|
88
|
-
return fullPath;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return resolved;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const isSameFsdModule = (file1, file2) => {
|
|
96
|
-
const slice1 = getFsdSlice(file1);
|
|
97
|
-
const slice2 = getFsdSlice(file2);
|
|
98
|
-
return slice1 !== null && slice1 === slice2;
|
|
99
|
-
};
|
|
100
|
-
|
|
101
9
|
module.exports = {
|
|
102
10
|
meta: {
|
|
103
11
|
type: `suggestion`,
|
|
@@ -106,7 +14,7 @@ module.exports = {
|
|
|
106
14
|
recommended: false,
|
|
107
15
|
url: null,
|
|
108
16
|
},
|
|
109
|
-
fixable:
|
|
17
|
+
fixable: null,
|
|
110
18
|
schema: [],
|
|
111
19
|
messages: {
|
|
112
20
|
resolvePublicApiImport: `Use direct relative import instead of public API within the same module`,
|
|
@@ -117,48 +25,13 @@ module.exports = {
|
|
|
117
25
|
return {
|
|
118
26
|
ImportDeclaration(node) {
|
|
119
27
|
const importPath = node.source.value;
|
|
120
|
-
const currentFile = context.getFilename();
|
|
121
|
-
|
|
122
|
-
if (!isOnlyParentDirs(importPath)) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const targetDir = resolveTargetDirectory(importPath, currentFile);
|
|
127
|
-
const indexPath = path.join(targetDir, 'index.ts');
|
|
128
28
|
|
|
129
|
-
|
|
130
|
-
.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const reExportPath = findReExportInIndex(indexPath, importedNames[0]);
|
|
138
|
-
|
|
139
|
-
if (!reExportPath) {
|
|
140
|
-
return;
|
|
29
|
+
if (isOnlyParentDirs(importPath)) {
|
|
30
|
+
context.report({
|
|
31
|
+
node,
|
|
32
|
+
messageId: `resolvePublicApiImport`,
|
|
33
|
+
});
|
|
141
34
|
}
|
|
142
|
-
|
|
143
|
-
const actualFilePath = resolveActualFilePath(targetDir, reExportPath);
|
|
144
|
-
|
|
145
|
-
if (!isSameFsdModule(currentFile, actualFilePath)) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const directRelativePath = calculateRelativePath(currentFile, actualFilePath);
|
|
150
|
-
|
|
151
|
-
if (directRelativePath === importPath) {
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
context.report({
|
|
156
|
-
node,
|
|
157
|
-
messageId: `resolvePublicApiImport`,
|
|
158
|
-
fix(fixer) {
|
|
159
|
-
return fixer.replaceText(node.source, `'${directRelativePath}'`);
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
35
|
}
|
|
163
36
|
};
|
|
164
37
|
},
|