@xanahlight/component-forge 0.1.0 β 1.4.0
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/README.md +8 -0
- package/dist/commands/check/check.test.d.ts +1 -0
- package/dist/commands/check/check.test.js +151 -0
- package/dist/commands/check/check.test.js.map +1 -0
- package/dist/commands/check/index.d.ts +15 -0
- package/dist/commands/check/index.js +186 -0
- package/dist/commands/check/index.js.map +1 -0
- package/dist/commands/explain/explain.test.d.ts +1 -0
- package/dist/commands/explain/explain.test.js +57 -0
- package/dist/commands/explain/explain.test.js.map +1 -0
- package/dist/commands/explain/index.d.ts +12 -0
- package/dist/commands/explain/index.js +37 -0
- package/dist/commands/explain/index.js.map +1 -0
- package/dist/commands/explain/topics.d.ts +3 -0
- package/dist/commands/explain/topics.js +138 -0
- package/dist/commands/explain/topics.js.map +1 -0
- package/dist/commands/generate/index.d.ts +6 -0
- package/dist/commands/generate/index.js +100 -0
- package/dist/commands/generate/index.js.map +1 -0
- package/dist/commands/init/index.d.ts +29 -0
- package/dist/commands/init/index.js +157 -0
- package/dist/commands/init/index.js.map +1 -0
- package/dist/commands/migrate/classifier.d.ts +22 -0
- package/dist/commands/migrate/classifier.js +48 -0
- package/dist/commands/migrate/classifier.js.map +1 -0
- package/dist/commands/migrate/index.d.ts +8 -0
- package/dist/commands/migrate/index.js +33 -0
- package/dist/commands/migrate/index.js.map +1 -0
- package/dist/commands/migrate/migrate.test.d.ts +1 -0
- package/dist/commands/migrate/migrate.test.js +176 -0
- package/dist/commands/migrate/migrate.test.js.map +1 -0
- package/dist/commands/migrate/plan-builder.d.ts +27 -0
- package/dist/commands/migrate/plan-builder.js +106 -0
- package/dist/commands/migrate/plan-builder.js.map +1 -0
- package/dist/commands/migrate/printer.d.ts +6 -0
- package/dist/commands/migrate/printer.js +46 -0
- package/dist/commands/migrate/printer.js.map +1 -0
- package/dist/commands/validate/index.d.ts +26 -0
- package/dist/commands/validate/index.js +122 -0
- package/dist/commands/validate/index.js.map +1 -0
- package/dist/commands/validate/validate.test.d.ts +1 -0
- package/dist/commands/validate/validate.test.js +163 -0
- package/dist/commands/validate/validate.test.js.map +1 -0
- package/dist/index.js +54 -10
- package/dist/index.js.map +1 -1
- package/dist/shared/format.d.ts +26 -0
- package/dist/shared/format.js +40 -0
- package/dist/shared/format.js.map +1 -0
- package/dist/templates/files.d.ts +1 -1
- package/dist/templates/files.js +61 -9
- package/dist/templates/files.js.map +1 -1
- package/dist/templates/files.test.d.ts +1 -0
- package/dist/templates/files.test.js +174 -0
- package/dist/templates/files.test.js.map +1 -0
- package/dist/types/folder-tree.d.ts +31 -2
- package/dist/types/folder-tree.js +18 -0
- package/dist/types/folder-tree.js.map +1 -1
- package/dist/utils/config.d.ts +19 -6
- package/dist/utils/config.js +76 -14
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/config.test.d.ts +1 -0
- package/dist/utils/config.test.js +117 -0
- package/dist/utils/config.test.js.map +1 -0
- package/dist/utils/template-resolver.d.ts +1 -1
- package/dist/utils/template-resolver.js +1 -1
- package/dist/utils/template-resolver.js.map +1 -1
- package/dist/utils/template-resolver.test.d.ts +1 -0
- package/dist/utils/template-resolver.test.js +86 -0
- package/dist/utils/template-resolver.test.js.map +1 -0
- package/package.json +13 -5
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
π English | [Π ΡΡΡΠΊΠΈΠΉ](README.ru.md)
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@xanahlight/component-forge)
|
|
6
|
+
[](https://github.com/vladislavprozorov/component-forge/actions/workflows/ci.yml)
|
|
6
7
|
[](https://nodejs.org)
|
|
7
8
|
[](LICENSE)
|
|
8
9
|
|
|
@@ -236,6 +237,13 @@ npm run build
|
|
|
236
237
|
node dist/index.js init fsd
|
|
237
238
|
```
|
|
238
239
|
|
|
240
|
+
Run tests:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
npm test
|
|
244
|
+
npm run test:coverage
|
|
245
|
+
```
|
|
246
|
+
|
|
239
247
|
> Node.js 20+ required.
|
|
240
248
|
|
|
241
249
|
---
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const vitest_1 = require("vitest");
|
|
10
|
+
const index_1 = require("./index");
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Helpers
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
let tmpDir;
|
|
15
|
+
function write(relPath, content) {
|
|
16
|
+
const full = node_path_1.default.join(tmpDir, relPath);
|
|
17
|
+
fs_extra_1.default.ensureDirSync(node_path_1.default.dirname(full));
|
|
18
|
+
fs_extra_1.default.writeFileSync(full, content);
|
|
19
|
+
}
|
|
20
|
+
(0, vitest_1.beforeEach)(() => {
|
|
21
|
+
tmpDir = fs_extra_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'cf-check-'));
|
|
22
|
+
});
|
|
23
|
+
(0, vitest_1.afterEach)(() => {
|
|
24
|
+
fs_extra_1.default.removeSync(tmpDir);
|
|
25
|
+
});
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// parseImports
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
(0, vitest_1.describe)('parseImports', () => {
|
|
30
|
+
(0, vitest_1.it)('extracts named imports', () => {
|
|
31
|
+
const src = `import { foo } from './foo'\nimport type { Bar } from '../bar'`;
|
|
32
|
+
(0, vitest_1.expect)((0, index_1.parseImports)(src)).toEqual(['./foo', '../bar']);
|
|
33
|
+
});
|
|
34
|
+
(0, vitest_1.it)('extracts default imports', () => {
|
|
35
|
+
(0, vitest_1.expect)((0, index_1.parseImports)(`import React from 'react'`)).toEqual(['react']);
|
|
36
|
+
});
|
|
37
|
+
(0, vitest_1.it)('extracts re-exports', () => {
|
|
38
|
+
(0, vitest_1.expect)((0, index_1.parseImports)(`export { foo } from './foo'`)).toEqual(['./foo']);
|
|
39
|
+
});
|
|
40
|
+
(0, vitest_1.it)('ignores non-import lines', () => {
|
|
41
|
+
(0, vitest_1.expect)((0, index_1.parseImports)(`const x = 1\nconsole.log(x)`)).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// resolveLayer
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
(0, vitest_1.describe)('resolveLayer', () => {
|
|
48
|
+
(0, vitest_1.it)('returns first path segment as layer', () => {
|
|
49
|
+
(0, vitest_1.expect)((0, index_1.resolveLayer)('features/auth/index.ts')).toBe('features');
|
|
50
|
+
(0, vitest_1.expect)((0, index_1.resolveLayer)('shared/ui/Button.tsx')).toBe('shared');
|
|
51
|
+
});
|
|
52
|
+
(0, vitest_1.it)('returns single-segment layer', () => {
|
|
53
|
+
(0, vitest_1.expect)((0, index_1.resolveLayer)('app')).toBe('app');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// collectSourceFiles
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
(0, vitest_1.describe)('collectSourceFiles', () => {
|
|
60
|
+
(0, vitest_1.it)('finds ts and tsx files', () => {
|
|
61
|
+
write('features/auth/index.ts', '');
|
|
62
|
+
write('shared/ui/Button.tsx', '');
|
|
63
|
+
write('shared/ui/Button.test.ts', ''); // should be excluded
|
|
64
|
+
const files = (0, index_1.collectSourceFiles)(tmpDir);
|
|
65
|
+
(0, vitest_1.expect)(files).toContain(node_path_1.default.join('features', 'auth', 'index.ts'));
|
|
66
|
+
(0, vitest_1.expect)(files).toContain(node_path_1.default.join('shared', 'ui', 'Button.tsx'));
|
|
67
|
+
(0, vitest_1.expect)(files.some((f) => f.endsWith('.test.ts'))).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
(0, vitest_1.it)('returns empty array for non-existent dir', () => {
|
|
70
|
+
(0, vitest_1.expect)((0, index_1.collectSourceFiles)('/non/existent/path')).toEqual([]);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// FSD violations
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
(0, vitest_1.describe)('runCheck β FSD', () => {
|
|
77
|
+
(0, vitest_1.it)('passes when lower layer imports from even lower layer (entities β shared)', () => {
|
|
78
|
+
write('entities/user/index.ts', `import { Button } from '../../shared/ui/Button'`);
|
|
79
|
+
const result = (0, index_1.runCheck)(tmpDir, 'fsd');
|
|
80
|
+
(0, vitest_1.expect)(result.violations).toHaveLength(0);
|
|
81
|
+
(0, vitest_1.expect)(result.checkedFiles).toBe(1);
|
|
82
|
+
});
|
|
83
|
+
(0, vitest_1.it)('detects feature importing from feature (same level)', () => {
|
|
84
|
+
write('features/auth/index.ts', `import { foo } from '../../features/search/index'`);
|
|
85
|
+
const result = (0, index_1.runCheck)(tmpDir, 'fsd');
|
|
86
|
+
(0, vitest_1.expect)(result.violations).toHaveLength(1);
|
|
87
|
+
(0, vitest_1.expect)(result.violations[0].message).toMatch(/must not import from "features"/);
|
|
88
|
+
});
|
|
89
|
+
(0, vitest_1.it)('detects entity importing from feature (higher level)', () => {
|
|
90
|
+
write('entities/user/index.ts', `import { login } from '../../features/auth/index'`);
|
|
91
|
+
const result = (0, index_1.runCheck)(tmpDir, 'fsd');
|
|
92
|
+
(0, vitest_1.expect)(result.violations).toHaveLength(1);
|
|
93
|
+
(0, vitest_1.expect)(result.violations[0].message).toMatch(/must not import from "features"/);
|
|
94
|
+
});
|
|
95
|
+
(0, vitest_1.it)('detects shared importing from entities (higher level)', () => {
|
|
96
|
+
write('shared/ui/Avatar.ts', `import { User } from '../../entities/user/model'`);
|
|
97
|
+
const result = (0, index_1.runCheck)(tmpDir, 'fsd');
|
|
98
|
+
(0, vitest_1.expect)(result.violations).toHaveLength(1);
|
|
99
|
+
(0, vitest_1.expect)(result.violations[0].message).toMatch(/must not import from "entities"/);
|
|
100
|
+
});
|
|
101
|
+
(0, vitest_1.it)('passes when feature imports from entities (lower level)', () => {
|
|
102
|
+
write('features/auth/index.ts', `import { User } from '../../entities/user/index'`);
|
|
103
|
+
const result = (0, index_1.runCheck)(tmpDir, 'fsd');
|
|
104
|
+
(0, vitest_1.expect)(result.violations).toHaveLength(0);
|
|
105
|
+
});
|
|
106
|
+
(0, vitest_1.it)('passes when pages imports from widgets (lower level)', () => {
|
|
107
|
+
write('pages/home/index.ts', `import { Header } from '../../widgets/header/index'`);
|
|
108
|
+
const result = (0, index_1.runCheck)(tmpDir, 'fsd');
|
|
109
|
+
(0, vitest_1.expect)(result.violations).toHaveLength(0);
|
|
110
|
+
});
|
|
111
|
+
(0, vitest_1.it)('ignores external (non-relative) imports', () => {
|
|
112
|
+
write('features/auth/index.ts', `import React from 'react'`);
|
|
113
|
+
const result = (0, index_1.runCheck)(tmpDir, 'fsd');
|
|
114
|
+
(0, vitest_1.expect)(result.violations).toHaveLength(0);
|
|
115
|
+
});
|
|
116
|
+
(0, vitest_1.it)('reports multiple violations across multiple files', () => {
|
|
117
|
+
write('entities/user/index.ts', `import { login } from '../../features/auth/index'`);
|
|
118
|
+
write('shared/ui/Button.ts', `import { User } from '../../entities/user/model'`);
|
|
119
|
+
const result = (0, index_1.runCheck)(tmpDir, 'fsd');
|
|
120
|
+
(0, vitest_1.expect)(result.violations).toHaveLength(2);
|
|
121
|
+
(0, vitest_1.expect)(result.checkedFiles).toBe(2);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// Modular violations
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
(0, vitest_1.describe)('runCheck β modular', () => {
|
|
128
|
+
(0, vitest_1.it)('passes when module imports from another module', () => {
|
|
129
|
+
write('modules/auth/index.ts', `import { foo } from '../../modules/ui/index'`);
|
|
130
|
+
const result = (0, index_1.runCheck)(tmpDir, 'modular');
|
|
131
|
+
(0, vitest_1.expect)(result.violations).toHaveLength(0);
|
|
132
|
+
});
|
|
133
|
+
(0, vitest_1.it)('detects shared importing from modules', () => {
|
|
134
|
+
write('shared/utils/helper.ts', `import { login } from '../../modules/auth/index'`);
|
|
135
|
+
const result = (0, index_1.runCheck)(tmpDir, 'modular');
|
|
136
|
+
(0, vitest_1.expect)(result.violations).toHaveLength(1);
|
|
137
|
+
(0, vitest_1.expect)(result.violations[0].message).toMatch(/must not import from "modules"/);
|
|
138
|
+
});
|
|
139
|
+
(0, vitest_1.it)('detects core importing from modules', () => {
|
|
140
|
+
write('core/router.ts', `import { AuthModule } from '../modules/auth/index'`);
|
|
141
|
+
const result = (0, index_1.runCheck)(tmpDir, 'modular');
|
|
142
|
+
(0, vitest_1.expect)(result.violations).toHaveLength(1);
|
|
143
|
+
(0, vitest_1.expect)(result.violations[0].message).toMatch(/must not import from "modules"/);
|
|
144
|
+
});
|
|
145
|
+
(0, vitest_1.it)('passes when module imports from shared', () => {
|
|
146
|
+
write('modules/auth/index.ts', `import { Button } from '../../shared/ui/Button'`);
|
|
147
|
+
const result = (0, index_1.runCheck)(tmpDir, 'modular');
|
|
148
|
+
(0, vitest_1.expect)(result.violations).toHaveLength(0);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
//# sourceMappingURL=check.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.test.js","sourceRoot":"","sources":["../../../src/commands/check/check.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Architecture } from '../../types/folder-tree';
|
|
2
|
+
export interface CheckViolation {
|
|
3
|
+
file: string;
|
|
4
|
+
importPath: string;
|
|
5
|
+
message: string;
|
|
6
|
+
}
|
|
7
|
+
export interface CheckResult {
|
|
8
|
+
violations: CheckViolation[];
|
|
9
|
+
checkedFiles: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function parseImports(source: string): string[];
|
|
12
|
+
export declare function resolveLayer(relPath: string): string | null;
|
|
13
|
+
export declare function collectSourceFiles(dir: string, base?: string): string[];
|
|
14
|
+
export declare function runCheck(srcPath: string, architecture: Architecture): CheckResult;
|
|
15
|
+
export declare function checkCommand(): void;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseImports = parseImports;
|
|
7
|
+
exports.resolveLayer = resolveLayer;
|
|
8
|
+
exports.collectSourceFiles = collectSourceFiles;
|
|
9
|
+
exports.runCheck = runCheck;
|
|
10
|
+
exports.checkCommand = checkCommand;
|
|
11
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
13
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
14
|
+
const config_1 = require("../../utils/config");
|
|
15
|
+
const logger_1 = require("../../utils/logger");
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// FSD layer hierarchy β higher index = higher layer
|
|
18
|
+
// A layer may only import from layers with a LOWER index.
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
const FSD_LAYER_ORDER = [
|
|
21
|
+
'shared',
|
|
22
|
+
'entities',
|
|
23
|
+
'features',
|
|
24
|
+
'widgets',
|
|
25
|
+
'pages',
|
|
26
|
+
'processes',
|
|
27
|
+
'app',
|
|
28
|
+
];
|
|
29
|
+
// For modular architecture β no strict hierarchy between modules,
|
|
30
|
+
// but modules must not import from app-level dirs and shared cannot
|
|
31
|
+
// import from modules.
|
|
32
|
+
const MODULAR_FORBIDDEN = {
|
|
33
|
+
shared: ['modules', 'core'],
|
|
34
|
+
core: ['modules'],
|
|
35
|
+
};
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Import parsing β extracts all static import paths from a TS/TSX file
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
const IMPORT_RE = /^\s*(?:import|export)\s+(?:[^'"]*\s+from\s+)?['"]([^'"]+)['"]/gm;
|
|
40
|
+
function parseImports(source) {
|
|
41
|
+
const imports = [];
|
|
42
|
+
let match;
|
|
43
|
+
const re = new RegExp(IMPORT_RE.source, IMPORT_RE.flags);
|
|
44
|
+
while ((match = re.exec(source)) !== null) {
|
|
45
|
+
imports.push(match[1]);
|
|
46
|
+
}
|
|
47
|
+
return imports;
|
|
48
|
+
}
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Layer resolution β given a file path relative to srcDir, returns its layer
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
function resolveLayer(relPath) {
|
|
53
|
+
// relPath looks like "features/auth/index.ts" or "shared/ui/Button.ts"
|
|
54
|
+
const parts = relPath.split(node_path_1.default.sep);
|
|
55
|
+
return parts[0] ?? null;
|
|
56
|
+
}
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// FSD violation check
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
function checkFsdViolations(srcPath, relFilePath, imports) {
|
|
61
|
+
const violations = [];
|
|
62
|
+
const fileLayer = resolveLayer(relFilePath);
|
|
63
|
+
if (!fileLayer)
|
|
64
|
+
return violations;
|
|
65
|
+
const fileLayerIdx = FSD_LAYER_ORDER.indexOf(fileLayer);
|
|
66
|
+
if (fileLayerIdx === -1)
|
|
67
|
+
return violations; // unknown layer β skip
|
|
68
|
+
for (const imp of imports) {
|
|
69
|
+
// Only check relative imports that could cross layer boundaries
|
|
70
|
+
// e.g. "../../features/auth" β resolve to get the target layer
|
|
71
|
+
if (!imp.startsWith('.'))
|
|
72
|
+
continue;
|
|
73
|
+
const fileDir = node_path_1.default.dirname(node_path_1.default.join(srcPath, relFilePath));
|
|
74
|
+
const resolved = node_path_1.default.resolve(fileDir, imp);
|
|
75
|
+
const relResolved = node_path_1.default.relative(srcPath, resolved);
|
|
76
|
+
const targetLayer = resolveLayer(relResolved);
|
|
77
|
+
if (!targetLayer)
|
|
78
|
+
continue;
|
|
79
|
+
const targetLayerIdx = FSD_LAYER_ORDER.indexOf(targetLayer);
|
|
80
|
+
if (targetLayerIdx === -1)
|
|
81
|
+
continue; // unknown target layer
|
|
82
|
+
// Violation: importing from the same layer OR a higher layer
|
|
83
|
+
if (targetLayerIdx >= fileLayerIdx) {
|
|
84
|
+
violations.push({
|
|
85
|
+
file: relFilePath,
|
|
86
|
+
importPath: imp,
|
|
87
|
+
message: `Layer "${fileLayer}" must not import from "${targetLayer}" ` +
|
|
88
|
+
`(${targetLayer} is at the same level or higher in FSD hierarchy)`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return violations;
|
|
93
|
+
}
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Modular violation check
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
function checkModularViolations(srcPath, relFilePath, imports) {
|
|
98
|
+
const violations = [];
|
|
99
|
+
const fileLayer = resolveLayer(relFilePath);
|
|
100
|
+
if (!fileLayer)
|
|
101
|
+
return violations;
|
|
102
|
+
const forbidden = MODULAR_FORBIDDEN[fileLayer];
|
|
103
|
+
if (!forbidden)
|
|
104
|
+
return violations;
|
|
105
|
+
for (const imp of imports) {
|
|
106
|
+
if (!imp.startsWith('.'))
|
|
107
|
+
continue;
|
|
108
|
+
const fileDir = node_path_1.default.dirname(node_path_1.default.join(srcPath, relFilePath));
|
|
109
|
+
const resolved = node_path_1.default.resolve(fileDir, imp);
|
|
110
|
+
const relResolved = node_path_1.default.relative(srcPath, resolved);
|
|
111
|
+
const targetLayer = resolveLayer(relResolved);
|
|
112
|
+
if (!targetLayer)
|
|
113
|
+
continue;
|
|
114
|
+
if (forbidden.includes(targetLayer)) {
|
|
115
|
+
violations.push({
|
|
116
|
+
file: relFilePath,
|
|
117
|
+
importPath: imp,
|
|
118
|
+
message: `Layer "${fileLayer}" must not import from "${targetLayer}" in modular architecture`,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return violations;
|
|
123
|
+
}
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// File collector β recursively finds .ts / .tsx files under srcPath
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
function collectSourceFiles(dir, base = dir) {
|
|
128
|
+
const results = [];
|
|
129
|
+
if (!node_fs_1.default.existsSync(dir))
|
|
130
|
+
return results;
|
|
131
|
+
for (const entry of node_fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
132
|
+
const full = node_path_1.default.join(dir, entry.name);
|
|
133
|
+
if (entry.isDirectory()) {
|
|
134
|
+
results.push(...collectSourceFiles(full, base));
|
|
135
|
+
}
|
|
136
|
+
else if (/\.(ts|tsx)$/.test(entry.name) && !entry.name.endsWith('.test.ts')) {
|
|
137
|
+
results.push(node_path_1.default.relative(base, full));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return results;
|
|
141
|
+
}
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// Core check logic β exported for testing
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
function runCheck(srcPath, architecture) {
|
|
146
|
+
const files = collectSourceFiles(srcPath);
|
|
147
|
+
const violations = [];
|
|
148
|
+
for (const relFile of files) {
|
|
149
|
+
const fullPath = node_path_1.default.join(srcPath, relFile);
|
|
150
|
+
const source = node_fs_1.default.readFileSync(fullPath, 'utf8');
|
|
151
|
+
const imports = parseImports(source);
|
|
152
|
+
if (architecture === 'fsd') {
|
|
153
|
+
violations.push(...checkFsdViolations(srcPath, relFile, imports));
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
violations.push(...checkModularViolations(srcPath, relFile, imports));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return { violations, checkedFiles: files.length };
|
|
160
|
+
}
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
// Command entry point
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
function checkCommand() {
|
|
165
|
+
const config = (0, config_1.loadProjectConfig)();
|
|
166
|
+
const srcPath = node_path_1.default.join(process.cwd(), config.srcDir);
|
|
167
|
+
logger_1.logger.info(`Checking architecture boundaries (${config.architecture})β¦\n`);
|
|
168
|
+
const { violations, checkedFiles } = runCheck(srcPath, config.architecture);
|
|
169
|
+
if (violations.length === 0) {
|
|
170
|
+
logger_1.logger.success(`β No violations found in ${checkedFiles} file(s). Architecture looks clean!`);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
console.log(chalk_1.default.red(`β Found ${violations.length} violation(s) in ${checkedFiles} file(s):\n`));
|
|
174
|
+
for (const v of violations) {
|
|
175
|
+
console.log(chalk_1.default.bold(chalk_1.default.white(` ${v.file}`)));
|
|
176
|
+
console.log(chalk_1.default.gray(` import "${v.importPath}"`));
|
|
177
|
+
console.log(chalk_1.default.red(` ${v.message}\n`));
|
|
178
|
+
}
|
|
179
|
+
logger_1.logger.info(chalk_1.default.yellow('Tip: ') +
|
|
180
|
+
'Each layer should only import from layers below it in the hierarchy.');
|
|
181
|
+
if (config.architecture === 'fsd') {
|
|
182
|
+
logger_1.logger.info(chalk_1.default.gray(' FSD order (low β high): shared β entities β features β widgets β pages β app'));
|
|
183
|
+
}
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/check/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const index_1 = require("./index");
|
|
5
|
+
(0, vitest_1.describe)('renderTopic', () => {
|
|
6
|
+
(0, vitest_1.it)('renders fsd topic with key sections', () => {
|
|
7
|
+
const output = (0, index_1.renderTopic)('fsd');
|
|
8
|
+
(0, vitest_1.expect)(output).toContain('Feature-Sliced Design');
|
|
9
|
+
(0, vitest_1.expect)(output).toContain('shared');
|
|
10
|
+
(0, vitest_1.expect)(output).toContain('entities');
|
|
11
|
+
(0, vitest_1.expect)(output).toContain('features');
|
|
12
|
+
(0, vitest_1.expect)(output).toContain('widgets');
|
|
13
|
+
(0, vitest_1.expect)(output).toContain('pages');
|
|
14
|
+
(0, vitest_1.expect)(output).toContain('app');
|
|
15
|
+
(0, vitest_1.expect)(output).toContain('Import rules');
|
|
16
|
+
});
|
|
17
|
+
(0, vitest_1.it)('renders modular topic with key sections', () => {
|
|
18
|
+
const output = (0, index_1.renderTopic)('modular');
|
|
19
|
+
(0, vitest_1.expect)(output).toContain('Modular Architecture');
|
|
20
|
+
(0, vitest_1.expect)(output).toContain('modules');
|
|
21
|
+
(0, vitest_1.expect)(output).toContain('core');
|
|
22
|
+
(0, vitest_1.expect)(output).toContain('shared');
|
|
23
|
+
(0, vitest_1.expect)(output).toContain('Import rules');
|
|
24
|
+
});
|
|
25
|
+
(0, vitest_1.it)('renders layers topic with FSD layer reference', () => {
|
|
26
|
+
const output = (0, index_1.renderTopic)('layers');
|
|
27
|
+
(0, vitest_1.expect)(output).toContain('FSD Layer Reference');
|
|
28
|
+
(0, vitest_1.expect)(output).toContain('shared');
|
|
29
|
+
(0, vitest_1.expect)(output).toContain('entities');
|
|
30
|
+
(0, vitest_1.expect)(output).toContain('features');
|
|
31
|
+
});
|
|
32
|
+
(0, vitest_1.it)('renders all topic combining all sections', () => {
|
|
33
|
+
const output = (0, index_1.renderTopic)('all');
|
|
34
|
+
(0, vitest_1.expect)(output).toContain('Feature-Sliced Design');
|
|
35
|
+
(0, vitest_1.expect)(output).toContain('Modular Architecture');
|
|
36
|
+
(0, vitest_1.expect)(output).toContain('FSD Layer Reference');
|
|
37
|
+
});
|
|
38
|
+
(0, vitest_1.it)('returns error message for unknown topic', () => {
|
|
39
|
+
const output = (0, index_1.renderTopic)('unknown-topic');
|
|
40
|
+
(0, vitest_1.expect)(output).toContain('Unknown topic');
|
|
41
|
+
(0, vitest_1.expect)(output).toContain('unknown-topic');
|
|
42
|
+
(0, vitest_1.expect)(output).toContain('Available topics');
|
|
43
|
+
});
|
|
44
|
+
(0, vitest_1.it)('is case-insensitive', () => {
|
|
45
|
+
const lower = (0, index_1.renderTopic)('fsd');
|
|
46
|
+
const upper = (0, index_1.renderTopic)('FSD');
|
|
47
|
+
// Same length means same content (ANSI codes are identical)
|
|
48
|
+
(0, vitest_1.expect)(lower.length).toBe(upper.length);
|
|
49
|
+
});
|
|
50
|
+
(0, vitest_1.it)('AVAILABLE_TOPICS contains expected topics', () => {
|
|
51
|
+
(0, vitest_1.expect)(index_1.AVAILABLE_TOPICS).toContain('fsd');
|
|
52
|
+
(0, vitest_1.expect)(index_1.AVAILABLE_TOPICS).toContain('modular');
|
|
53
|
+
(0, vitest_1.expect)(index_1.AVAILABLE_TOPICS).toContain('layers');
|
|
54
|
+
(0, vitest_1.expect)(index_1.AVAILABLE_TOPICS).toContain('all');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=explain.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.test.js","sourceRoot":"","sources":["../../../src/commands/explain/explain.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TOPICS } from './topics';
|
|
2
|
+
export { TOPICS };
|
|
3
|
+
export declare const AVAILABLE_TOPICS: readonly string[];
|
|
4
|
+
/**
|
|
5
|
+
* Renders the requested topic to a string.
|
|
6
|
+
* Pure function β no stdout side-effects β making it fully testable.
|
|
7
|
+
*/
|
|
8
|
+
export declare function renderTopic(topic: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* CLI entry point β writes the rendered topic to stdout.
|
|
11
|
+
*/
|
|
12
|
+
export declare function explainCommand(topic: string): void;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AVAILABLE_TOPICS = exports.TOPICS = void 0;
|
|
7
|
+
exports.renderTopic = renderTopic;
|
|
8
|
+
exports.explainCommand = explainCommand;
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const format_1 = require("../../shared/format");
|
|
11
|
+
const topics_1 = require("./topics");
|
|
12
|
+
Object.defineProperty(exports, "TOPICS", { enumerable: true, get: function () { return topics_1.TOPICS; } });
|
|
13
|
+
exports.AVAILABLE_TOPICS = Object.keys(topics_1.TOPICS);
|
|
14
|
+
/**
|
|
15
|
+
* Renders the requested topic to a string.
|
|
16
|
+
* Pure function β no stdout side-effects β making it fully testable.
|
|
17
|
+
*/
|
|
18
|
+
function renderTopic(topic) {
|
|
19
|
+
const renderer = topics_1.TOPICS[topic.toLowerCase()];
|
|
20
|
+
if (!renderer) {
|
|
21
|
+
return [
|
|
22
|
+
chalk_1.default.red(`\n Unknown topic: "${topic}"\n`),
|
|
23
|
+
format_1.fmt.line(` Available topics: ${chalk_1.default.cyan(exports.AVAILABLE_TOPICS.join(', '))}`),
|
|
24
|
+
format_1.fmt.line(''),
|
|
25
|
+
format_1.fmt.line(` Example: component-forge explain fsd`),
|
|
26
|
+
format_1.fmt.line(''),
|
|
27
|
+
].join('\n');
|
|
28
|
+
}
|
|
29
|
+
return renderer();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* CLI entry point β writes the rendered topic to stdout.
|
|
33
|
+
*/
|
|
34
|
+
function explainCommand(topic) {
|
|
35
|
+
process.stdout.write(renderTopic(topic));
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/explain/index.ts"],"names":[],"mappings":";;;;;;AAaA,kCAcC;AAKD,wCAEC;AAlCD,kDAAyB;AAEzB,gDAAyC;AAEzC,qCAAiC;AAExB,uFAFA,eAAM,OAEA;AACF,QAAA,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,eAAM,CAAsB,CAAA;AAExE;;;GAGG;AACH,SAAgB,WAAW,CAAC,KAAa;IACvC,MAAM,QAAQ,GAAG,eAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAA;IAE5C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO;YACL,eAAK,CAAC,GAAG,CAAC,uBAAuB,KAAK,KAAK,CAAC;YAC5C,YAAG,CAAC,IAAI,CAAC,uBAAuB,eAAK,CAAC,IAAI,CAAC,wBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAC1E,YAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACZ,YAAG,CAAC,IAAI,CAAC,wCAAwC,CAAC;YAClD,YAAG,CAAC,IAAI,CAAC,EAAE,CAAC;SACb,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACd,CAAC;IAED,OAAO,QAAQ,EAAE,CAAA;AACnB,CAAC;AAED;;GAEG;AACH,SAAgB,cAAc,CAAC,KAAa;IAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAA;AAC1C,CAAC"}
|