@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.
Files changed (70) hide show
  1. package/README.md +8 -0
  2. package/dist/commands/check/check.test.d.ts +1 -0
  3. package/dist/commands/check/check.test.js +151 -0
  4. package/dist/commands/check/check.test.js.map +1 -0
  5. package/dist/commands/check/index.d.ts +15 -0
  6. package/dist/commands/check/index.js +186 -0
  7. package/dist/commands/check/index.js.map +1 -0
  8. package/dist/commands/explain/explain.test.d.ts +1 -0
  9. package/dist/commands/explain/explain.test.js +57 -0
  10. package/dist/commands/explain/explain.test.js.map +1 -0
  11. package/dist/commands/explain/index.d.ts +12 -0
  12. package/dist/commands/explain/index.js +37 -0
  13. package/dist/commands/explain/index.js.map +1 -0
  14. package/dist/commands/explain/topics.d.ts +3 -0
  15. package/dist/commands/explain/topics.js +138 -0
  16. package/dist/commands/explain/topics.js.map +1 -0
  17. package/dist/commands/generate/index.d.ts +6 -0
  18. package/dist/commands/generate/index.js +100 -0
  19. package/dist/commands/generate/index.js.map +1 -0
  20. package/dist/commands/init/index.d.ts +29 -0
  21. package/dist/commands/init/index.js +157 -0
  22. package/dist/commands/init/index.js.map +1 -0
  23. package/dist/commands/migrate/classifier.d.ts +22 -0
  24. package/dist/commands/migrate/classifier.js +48 -0
  25. package/dist/commands/migrate/classifier.js.map +1 -0
  26. package/dist/commands/migrate/index.d.ts +8 -0
  27. package/dist/commands/migrate/index.js +33 -0
  28. package/dist/commands/migrate/index.js.map +1 -0
  29. package/dist/commands/migrate/migrate.test.d.ts +1 -0
  30. package/dist/commands/migrate/migrate.test.js +176 -0
  31. package/dist/commands/migrate/migrate.test.js.map +1 -0
  32. package/dist/commands/migrate/plan-builder.d.ts +27 -0
  33. package/dist/commands/migrate/plan-builder.js +106 -0
  34. package/dist/commands/migrate/plan-builder.js.map +1 -0
  35. package/dist/commands/migrate/printer.d.ts +6 -0
  36. package/dist/commands/migrate/printer.js +46 -0
  37. package/dist/commands/migrate/printer.js.map +1 -0
  38. package/dist/commands/validate/index.d.ts +26 -0
  39. package/dist/commands/validate/index.js +122 -0
  40. package/dist/commands/validate/index.js.map +1 -0
  41. package/dist/commands/validate/validate.test.d.ts +1 -0
  42. package/dist/commands/validate/validate.test.js +163 -0
  43. package/dist/commands/validate/validate.test.js.map +1 -0
  44. package/dist/index.js +54 -10
  45. package/dist/index.js.map +1 -1
  46. package/dist/shared/format.d.ts +26 -0
  47. package/dist/shared/format.js +40 -0
  48. package/dist/shared/format.js.map +1 -0
  49. package/dist/templates/files.d.ts +1 -1
  50. package/dist/templates/files.js +61 -9
  51. package/dist/templates/files.js.map +1 -1
  52. package/dist/templates/files.test.d.ts +1 -0
  53. package/dist/templates/files.test.js +174 -0
  54. package/dist/templates/files.test.js.map +1 -0
  55. package/dist/types/folder-tree.d.ts +31 -2
  56. package/dist/types/folder-tree.js +18 -0
  57. package/dist/types/folder-tree.js.map +1 -1
  58. package/dist/utils/config.d.ts +19 -6
  59. package/dist/utils/config.js +76 -14
  60. package/dist/utils/config.js.map +1 -1
  61. package/dist/utils/config.test.d.ts +1 -0
  62. package/dist/utils/config.test.js +117 -0
  63. package/dist/utils/config.test.js.map +1 -0
  64. package/dist/utils/template-resolver.d.ts +1 -1
  65. package/dist/utils/template-resolver.js +1 -1
  66. package/dist/utils/template-resolver.js.map +1 -1
  67. package/dist/utils/template-resolver.test.d.ts +1 -0
  68. package/dist/utils/template-resolver.test.js +86 -0
  69. package/dist/utils/template-resolver.test.js.map +1 -0
  70. package/package.json +13 -5
@@ -0,0 +1,176 @@
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 mkdir(relPath) {
16
+ fs_extra_1.default.ensureDirSync(node_path_1.default.join(tmpDir, relPath));
17
+ }
18
+ (0, vitest_1.beforeEach)(() => {
19
+ tmpDir = fs_extra_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'cf-migrate-'));
20
+ });
21
+ (0, vitest_1.afterEach)(() => {
22
+ fs_extra_1.default.removeSync(tmpDir);
23
+ });
24
+ // ---------------------------------------------------------------------------
25
+ // scanTopLevelDirs
26
+ // ---------------------------------------------------------------------------
27
+ (0, vitest_1.describe)('scanTopLevelDirs', () => {
28
+ (0, vitest_1.it)('returns only directory names, not files', () => {
29
+ mkdir('components');
30
+ mkdir('utils');
31
+ fs_extra_1.default.writeFileSync(node_path_1.default.join(tmpDir, 'index.ts'), '');
32
+ const dirs = (0, index_1.scanTopLevelDirs)(tmpDir);
33
+ (0, vitest_1.expect)(dirs).toContain('components');
34
+ (0, vitest_1.expect)(dirs).toContain('utils');
35
+ (0, vitest_1.expect)(dirs).not.toContain('index.ts');
36
+ });
37
+ (0, vitest_1.it)('returns empty array for non-existent path', () => {
38
+ (0, vitest_1.expect)((0, index_1.scanTopLevelDirs)('/non/existent/path')).toEqual([]);
39
+ });
40
+ (0, vitest_1.it)('returns empty array for empty directory', () => {
41
+ (0, vitest_1.expect)((0, index_1.scanTopLevelDirs)(tmpDir)).toEqual([]);
42
+ });
43
+ });
44
+ // ---------------------------------------------------------------------------
45
+ // classifyDir
46
+ // ---------------------------------------------------------------------------
47
+ (0, vitest_1.describe)('classifyDir', () => {
48
+ (0, vitest_1.it)('classifies "components" as shared/ui', () => {
49
+ const result = (0, index_1.classifyDir)('components');
50
+ (0, vitest_1.expect)(result).not.toBeNull();
51
+ (0, vitest_1.expect)(result.layer).toBe('shared');
52
+ (0, vitest_1.expect)(result.subdir).toBe('ui');
53
+ });
54
+ (0, vitest_1.it)('classifies "utils" as shared', () => {
55
+ const result = (0, index_1.classifyDir)('utils');
56
+ (0, vitest_1.expect)(result).not.toBeNull();
57
+ (0, vitest_1.expect)(result.layer).toBe('shared');
58
+ });
59
+ (0, vitest_1.it)('classifies "pages" as pages', () => {
60
+ const result = (0, index_1.classifyDir)('pages');
61
+ (0, vitest_1.expect)(result).not.toBeNull();
62
+ (0, vitest_1.expect)(result.layer).toBe('pages');
63
+ });
64
+ (0, vitest_1.it)('classifies "views" as pages', () => {
65
+ const result = (0, index_1.classifyDir)('views');
66
+ (0, vitest_1.expect)(result).not.toBeNull();
67
+ (0, vitest_1.expect)(result.layer).toBe('pages');
68
+ });
69
+ (0, vitest_1.it)('classifies "store" as app', () => {
70
+ const result = (0, index_1.classifyDir)('store');
71
+ (0, vitest_1.expect)(result).not.toBeNull();
72
+ (0, vitest_1.expect)(result.layer).toBe('app');
73
+ });
74
+ (0, vitest_1.it)('classifies "api" as shared/api', () => {
75
+ const result = (0, index_1.classifyDir)('api');
76
+ (0, vitest_1.expect)(result).not.toBeNull();
77
+ (0, vitest_1.expect)(result.layer).toBe('shared');
78
+ (0, vitest_1.expect)(result.subdir).toBe('api');
79
+ });
80
+ (0, vitest_1.it)('classifies "hooks" as shared/hooks', () => {
81
+ const result = (0, index_1.classifyDir)('hooks');
82
+ (0, vitest_1.expect)(result).not.toBeNull();
83
+ (0, vitest_1.expect)(result.layer).toBe('shared');
84
+ (0, vitest_1.expect)(result.subdir).toBe('hooks');
85
+ });
86
+ (0, vitest_1.it)('returns null for unrecognised directory', () => {
87
+ (0, vitest_1.expect)((0, index_1.classifyDir)('my-weird-custom-dir')).toBeNull();
88
+ });
89
+ });
90
+ // ---------------------------------------------------------------------------
91
+ // buildMigrationPlan — FSD target
92
+ // ---------------------------------------------------------------------------
93
+ (0, vitest_1.describe)('buildMigrationPlan → fsd', () => {
94
+ (0, vitest_1.it)('proposes moves for standard React project structure', () => {
95
+ mkdir('components');
96
+ mkdir('pages');
97
+ mkdir('utils');
98
+ mkdir('api');
99
+ mkdir('hooks');
100
+ const plan = (0, index_1.buildMigrationPlan)(tmpDir, 'fsd');
101
+ // pages is already an FSD layer — should be skipped
102
+ const froms = plan.proposals.map((p) => p.from);
103
+ (0, vitest_1.expect)(froms).not.toContain('pages');
104
+ // components, utils, api, hooks should get proposals
105
+ (0, vitest_1.expect)(froms).toContain('components');
106
+ (0, vitest_1.expect)(froms).toContain('utils');
107
+ (0, vitest_1.expect)(froms).toContain('api');
108
+ (0, vitest_1.expect)(froms).toContain('hooks');
109
+ });
110
+ (0, vitest_1.it)('skips directories already in correct FSD layers', () => {
111
+ mkdir('features');
112
+ mkdir('entities');
113
+ mkdir('shared');
114
+ mkdir('widgets');
115
+ mkdir('pages');
116
+ mkdir('app');
117
+ const plan = (0, index_1.buildMigrationPlan)(tmpDir, 'fsd');
118
+ (0, vitest_1.expect)(plan.proposals).toHaveLength(0);
119
+ });
120
+ (0, vitest_1.it)('puts unrecognised dirs in unknownFiles', () => {
121
+ mkdir('super-weird-module');
122
+ const plan = (0, index_1.buildMigrationPlan)(tmpDir, 'fsd');
123
+ (0, vitest_1.expect)(plan.unknownFiles).toContain('super-weird-module');
124
+ (0, vitest_1.expect)(plan.proposals).toHaveLength(0);
125
+ });
126
+ (0, vitest_1.it)('proposal has correct from/to/reason shape', () => {
127
+ mkdir('components');
128
+ const plan = (0, index_1.buildMigrationPlan)(tmpDir, 'fsd');
129
+ const proposal = plan.proposals.find((p) => p.from === 'components');
130
+ (0, vitest_1.expect)(proposal).toBeDefined();
131
+ (0, vitest_1.expect)(proposal.to).toContain('shared');
132
+ (0, vitest_1.expect)(proposal.to).toContain('ui');
133
+ (0, vitest_1.expect)(proposal.reason).toContain('shared');
134
+ });
135
+ (0, vitest_1.it)('summary reflects proposal count', () => {
136
+ mkdir('components');
137
+ mkdir('utils');
138
+ const plan = (0, index_1.buildMigrationPlan)(tmpDir, 'fsd');
139
+ (0, vitest_1.expect)(plan.summary).toMatch(/\d+ director/);
140
+ });
141
+ (0, vitest_1.it)('summary says no changes when already FSD', () => {
142
+ mkdir('features');
143
+ mkdir('shared');
144
+ const plan = (0, index_1.buildMigrationPlan)(tmpDir, 'fsd');
145
+ (0, vitest_1.expect)(plan.summary).toContain('No changes needed');
146
+ });
147
+ });
148
+ // ---------------------------------------------------------------------------
149
+ // buildMigrationPlan — modular target
150
+ // ---------------------------------------------------------------------------
151
+ (0, vitest_1.describe)('buildMigrationPlan → modular', () => {
152
+ (0, vitest_1.it)('wraps unknown dirs as modules/', () => {
153
+ mkdir('auth');
154
+ mkdir('cart');
155
+ const plan = (0, index_1.buildMigrationPlan)(tmpDir, 'modular');
156
+ const froms = plan.proposals.map((p) => p.from);
157
+ (0, vitest_1.expect)(froms).toContain('auth');
158
+ (0, vitest_1.expect)(froms).toContain('cart');
159
+ const authProp = plan.proposals.find((p) => p.from === 'auth');
160
+ (0, vitest_1.expect)(authProp.to).toMatch(/^modules/);
161
+ });
162
+ (0, vitest_1.it)('puts utils-like dirs under shared/', () => {
163
+ mkdir('utils');
164
+ const plan = (0, index_1.buildMigrationPlan)(tmpDir, 'modular');
165
+ const utilsProp = plan.proposals.find((p) => p.from === 'utils');
166
+ (0, vitest_1.expect)(utilsProp.to).toMatch(/^shared/);
167
+ });
168
+ (0, vitest_1.it)('skips already-correct modular dirs', () => {
169
+ mkdir('modules');
170
+ mkdir('shared');
171
+ mkdir('core');
172
+ const plan = (0, index_1.buildMigrationPlan)(tmpDir, 'modular');
173
+ (0, vitest_1.expect)(plan.proposals).toHaveLength(0);
174
+ });
175
+ });
176
+ //# sourceMappingURL=migrate.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.test.js","sourceRoot":"","sources":["../../../src/commands/migrate/migrate.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { Architecture } from '../../types/folder-tree';
2
+ export interface FileMoveProposal {
3
+ /** Current directory name relative to srcDir */
4
+ from: string;
5
+ /** Proposed path relative to srcDir */
6
+ to: string;
7
+ /** Human-readable reason for the move */
8
+ reason: string;
9
+ }
10
+ export interface MigrationPlan {
11
+ targetArchitecture: Architecture;
12
+ sourceDir: string;
13
+ proposals: FileMoveProposal[];
14
+ /** Directories that didn't match any known pattern — need manual review */
15
+ unknownFiles: string[];
16
+ summary: string;
17
+ }
18
+ /**
19
+ * Returns the names of top-level directories inside srcPath.
20
+ * Files at the root level are intentionally ignored.
21
+ */
22
+ export declare function scanTopLevelDirs(srcPath: string): string[];
23
+ /**
24
+ * Builds a migration plan by analysing top-level directories in srcPath.
25
+ * Pure function — no filesystem writes, safe to call at any time.
26
+ */
27
+ export declare function buildMigrationPlan(srcPath: string, targetArch: Architecture): MigrationPlan;
@@ -0,0 +1,106 @@
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.scanTopLevelDirs = scanTopLevelDirs;
7
+ exports.buildMigrationPlan = buildMigrationPlan;
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const classifier_1 = require("./classifier");
11
+ // ---------------------------------------------------------------------------
12
+ // Directory scanner
13
+ // ---------------------------------------------------------------------------
14
+ /**
15
+ * Returns the names of top-level directories inside srcPath.
16
+ * Files at the root level are intentionally ignored.
17
+ */
18
+ function scanTopLevelDirs(srcPath) {
19
+ if (!fs_extra_1.default.existsSync(srcPath))
20
+ return [];
21
+ return fs_extra_1.default
22
+ .readdirSync(srcPath, { withFileTypes: true })
23
+ .filter((e) => e.isDirectory())
24
+ .map((e) => e.name);
25
+ }
26
+ // ---------------------------------------------------------------------------
27
+ // Plan builder
28
+ // ---------------------------------------------------------------------------
29
+ const FSD_LAYERS = new Set([
30
+ 'app', 'processes', 'pages', 'widgets', 'features', 'entities', 'shared',
31
+ ]);
32
+ const MODULAR_BASE = new Set(['modules', 'shared', 'core']);
33
+ function buildFsdProposals(dirs) {
34
+ const proposals = [];
35
+ const unknownFiles = [];
36
+ for (const dir of dirs) {
37
+ if (FSD_LAYERS.has(dir))
38
+ continue; // already correct
39
+ const classification = (0, classifier_1.classifyDir)(dir);
40
+ if (!classification) {
41
+ unknownFiles.push(dir);
42
+ continue;
43
+ }
44
+ const { layer, subdir } = classification;
45
+ const to = subdir ? node_path_1.default.join(layer, subdir, dir) : node_path_1.default.join(layer, dir);
46
+ proposals.push({
47
+ from: dir,
48
+ to,
49
+ reason: `"${dir}" maps to FSD layer "${layer}"${subdir ? `/${subdir}` : ''}`,
50
+ });
51
+ }
52
+ return { proposals, unknownFiles };
53
+ }
54
+ function buildModularProposals(dirs) {
55
+ const proposals = [];
56
+ for (const dir of dirs) {
57
+ if (MODULAR_BASE.has(dir))
58
+ continue;
59
+ const classification = (0, classifier_1.classifyDir)(dir);
60
+ const isSharedCandidate = classification?.layer === 'shared' &&
61
+ !['api', 'services'].includes(dir.toLowerCase());
62
+ if (isSharedCandidate) {
63
+ proposals.push({
64
+ from: dir,
65
+ to: node_path_1.default.join('shared', dir),
66
+ reason: `"${dir}" is a shared utility/UI — belongs under shared/`,
67
+ });
68
+ }
69
+ else {
70
+ proposals.push({
71
+ from: dir,
72
+ to: node_path_1.default.join('modules', dir),
73
+ reason: `"${dir}" is a feature domain — wrap as a module`,
74
+ });
75
+ }
76
+ }
77
+ return proposals;
78
+ }
79
+ function buildSummary(proposals, unknownFiles, target) {
80
+ if (proposals.length === 0 && unknownFiles.length === 0) {
81
+ return `No changes needed — project already looks like ${target.toUpperCase()}.`;
82
+ }
83
+ const moved = proposals.length;
84
+ const unknown = unknownFiles.length;
85
+ return (`${moved} director${moved === 1 ? 'y' : 'ies'} to move` +
86
+ (unknown > 0 ? `, ${unknown} unknown (manual review needed)` : '') +
87
+ '.');
88
+ }
89
+ /**
90
+ * Builds a migration plan by analysing top-level directories in srcPath.
91
+ * Pure function — no filesystem writes, safe to call at any time.
92
+ */
93
+ function buildMigrationPlan(srcPath, targetArch) {
94
+ const dirs = scanTopLevelDirs(srcPath);
95
+ const { proposals, unknownFiles } = targetArch === 'fsd'
96
+ ? buildFsdProposals(dirs)
97
+ : { proposals: buildModularProposals(dirs), unknownFiles: [] };
98
+ return {
99
+ targetArchitecture: targetArch,
100
+ sourceDir: srcPath,
101
+ proposals,
102
+ unknownFiles,
103
+ summary: buildSummary(proposals, unknownFiles, targetArch),
104
+ };
105
+ }
106
+ //# sourceMappingURL=plan-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-builder.js","sourceRoot":"","sources":["../../../src/commands/migrate/plan-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { MigrationPlan } from './plan-builder';
2
+ /**
3
+ * Renders a MigrationPlan as colour-coded CLI output.
4
+ * Pure function — receives the plan, writes to stdout.
5
+ */
6
+ export declare function printMigrationPlan(plan: MigrationPlan): void;
@@ -0,0 +1,46 @@
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.printMigrationPlan = printMigrationPlan;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ /**
10
+ * Renders a MigrationPlan as colour-coded CLI output.
11
+ * Pure function — receives the plan, writes to stdout.
12
+ */
13
+ function printMigrationPlan(plan) {
14
+ const { proposals, unknownFiles, summary, targetArchitecture, sourceDir } = plan;
15
+ const rel = node_path_1.default.relative(process.cwd(), sourceDir) || '.';
16
+ console.log(chalk_1.default.bold(`\n Migration plan → ${chalk_1.default.cyan(targetArchitecture.toUpperCase())}\n`));
17
+ console.log(chalk_1.default.gray(` Analysed: ${rel}\n`));
18
+ console.log(chalk_1.default.gray(` ${'─'.repeat(58)}`));
19
+ if (proposals.length === 0 && unknownFiles.length === 0) {
20
+ console.log(chalk_1.default.green(`\n ✓ ${summary}\n`));
21
+ return;
22
+ }
23
+ if (proposals.length > 0) {
24
+ console.log(chalk_1.default.bold(`\n Proposed moves (${proposals.length}):\n`));
25
+ for (const p of proposals) {
26
+ const fromFmt = chalk_1.default.yellow(p.from.padEnd(24));
27
+ const toFmt = chalk_1.default.green(p.to);
28
+ console.log(` ${fromFmt} → ${toFmt}`);
29
+ console.log(chalk_1.default.gray(` ↳ ${p.reason}`));
30
+ }
31
+ }
32
+ if (unknownFiles.length > 0) {
33
+ console.log(chalk_1.default.bold(`\n Needs manual review (${unknownFiles.length}):\n`));
34
+ for (const f of unknownFiles) {
35
+ console.log(` ${chalk_1.default.red('?')} ${chalk_1.default.white(f)}`);
36
+ }
37
+ console.log(chalk_1.default.gray("\n These directories don't match known patterns."));
38
+ console.log(chalk_1.default.gray(` Run ${chalk_1.default.white('component-forge explain fsd')} to understand the target structure.`));
39
+ }
40
+ console.log(chalk_1.default.gray(`\n ${'─'.repeat(58)}`));
41
+ console.log(chalk_1.default.bold(`\n Summary: ${summary}\n`));
42
+ console.log(chalk_1.default.yellow(' ⚠ This is a dry-run analysis — no files were moved.\n'));
43
+ console.log(chalk_1.default.gray(" To apply: move each directory manually or use your IDE's refactor tools.\n"));
44
+ console.log(chalk_1.default.gray(` After moving, run ${chalk_1.default.white('component-forge validate')} and ${chalk_1.default.white('component-forge check')} to verify.\n`));
45
+ }
46
+ //# sourceMappingURL=printer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"printer.js","sourceRoot":"","sources":["../../../src/commands/migrate/printer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { Architecture } from '../../types/folder-tree';
2
+ interface LayerRule {
3
+ /** Directories that MUST exist */
4
+ required: string[];
5
+ /** All allowed directories (required + optional) */
6
+ allowed: string[];
7
+ }
8
+ type Severity = 'error' | 'warning';
9
+ export interface ValidationIssue {
10
+ severity: Severity;
11
+ message: string;
12
+ }
13
+ /**
14
+ * Checks that all required layers exist under srcDir
15
+ */
16
+ export declare function checkRequiredLayers(srcPath: string, rule: LayerRule, srcDir: string): ValidationIssue[];
17
+ /**
18
+ * Checks that no unknown (forbidden) directories exist directly under srcDir
19
+ */
20
+ export declare function checkUnknownLayers(srcPath: string, rule: LayerRule, architecture: Architecture, srcDir: string): ValidationIssue[];
21
+ /**
22
+ * Checks each slice for a public API index.ts file
23
+ */
24
+ export declare function checkPublicApiFiles(srcPath: string, rule: LayerRule, srcDir: string): ValidationIssue[];
25
+ export declare function validateCommand(): void;
26
+ export {};
@@ -0,0 +1,122 @@
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.checkRequiredLayers = checkRequiredLayers;
7
+ exports.checkUnknownLayers = checkUnknownLayers;
8
+ exports.checkPublicApiFiles = checkPublicApiFiles;
9
+ exports.validateCommand = validateCommand;
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const fs_extra_1 = __importDefault(require("fs-extra"));
12
+ const config_1 = require("../../utils/config");
13
+ const logger_1 = require("../../utils/logger");
14
+ const layerRulesByArchitecture = {
15
+ fsd: {
16
+ required: ['app', 'shared'],
17
+ allowed: ['app', 'processes', 'pages', 'widgets', 'features', 'entities', 'shared'],
18
+ },
19
+ modular: {
20
+ required: ['shared', 'core'],
21
+ allowed: ['modules', 'shared', 'core'],
22
+ },
23
+ };
24
+ // ---------------------------------------------------------------------------
25
+ // Validation rules — exported for unit testing
26
+ // ---------------------------------------------------------------------------
27
+ /**
28
+ * Checks that all required layers exist under srcDir
29
+ */
30
+ function checkRequiredLayers(srcPath, rule, srcDir) {
31
+ return rule.required
32
+ .filter((layer) => !fs_extra_1.default.existsSync(node_path_1.default.join(srcPath, layer)))
33
+ .map((layer) => ({
34
+ severity: 'error',
35
+ message: `Missing required layer: ${srcDir}/${layer}`,
36
+ }));
37
+ }
38
+ /**
39
+ * Checks that no unknown (forbidden) directories exist directly under srcDir
40
+ */
41
+ function checkUnknownLayers(srcPath, rule, architecture, srcDir) {
42
+ if (!fs_extra_1.default.existsSync(srcPath))
43
+ return [];
44
+ return fs_extra_1.default
45
+ .readdirSync(srcPath, { withFileTypes: true })
46
+ .filter((entry) => entry.isDirectory())
47
+ .filter((entry) => !rule.allowed.includes(entry.name))
48
+ .map((entry) => ({
49
+ severity: 'warning',
50
+ message: `"${srcDir}/${entry.name}" is not a recognised layer in ${architecture.toUpperCase()} architecture`,
51
+ }));
52
+ }
53
+ /**
54
+ * Checks each slice for a public API index.ts file
55
+ */
56
+ function checkPublicApiFiles(srcPath, rule, srcDir) {
57
+ const issues = [];
58
+ const sliceLayers = rule.allowed.filter((l) => l !== 'app' && l !== 'shared' && l !== 'core');
59
+ for (const layer of sliceLayers) {
60
+ const layerPath = node_path_1.default.join(srcPath, layer);
61
+ if (!fs_extra_1.default.existsSync(layerPath))
62
+ continue;
63
+ const slices = fs_extra_1.default
64
+ .readdirSync(layerPath, { withFileTypes: true })
65
+ .filter((e) => e.isDirectory());
66
+ for (const slice of slices) {
67
+ const indexPath = node_path_1.default.join(layerPath, slice.name, 'index.ts');
68
+ if (!fs_extra_1.default.existsSync(indexPath)) {
69
+ issues.push({
70
+ severity: 'warning',
71
+ message: `Missing public API: ${srcDir}/${layer}/${slice.name}/index.ts`,
72
+ });
73
+ }
74
+ }
75
+ }
76
+ return issues;
77
+ }
78
+ // ---------------------------------------------------------------------------
79
+ // Output formatting
80
+ // ---------------------------------------------------------------------------
81
+ function printIssues(issues) {
82
+ const errors = issues.filter((i) => i.severity === 'error');
83
+ const warnings = issues.filter((i) => i.severity === 'warning');
84
+ // Print errors first — they are blocking
85
+ for (const issue of errors) {
86
+ logger_1.logger.error(issue.message);
87
+ }
88
+ for (const issue of warnings) {
89
+ logger_1.logger.warning(issue.message);
90
+ }
91
+ const parts = [];
92
+ if (errors.length > 0)
93
+ parts.push(`${errors.length} error(s)`);
94
+ if (warnings.length > 0)
95
+ parts.push(`${warnings.length} warning(s)`);
96
+ logger_1.logger.info(`Found ${parts.join(', ')}.`);
97
+ }
98
+ // ---------------------------------------------------------------------------
99
+ // Command entry point
100
+ // ---------------------------------------------------------------------------
101
+ function validateCommand() {
102
+ const config = (0, config_1.loadProjectConfig)();
103
+ const { architecture, srcDir } = config;
104
+ const srcPath = node_path_1.default.join(process.cwd(), srcDir);
105
+ const rule = layerRulesByArchitecture[architecture];
106
+ logger_1.logger.info(`Validating ${architecture.toUpperCase()} architecture…`);
107
+ const issues = [
108
+ ...checkRequiredLayers(srcPath, rule, srcDir),
109
+ ...checkUnknownLayers(srcPath, rule, architecture, srcDir),
110
+ ...checkPublicApiFiles(srcPath, rule, srcDir),
111
+ ];
112
+ if (issues.length === 0) {
113
+ logger_1.logger.success('Architecture is valid. No issues found.');
114
+ return;
115
+ }
116
+ printIssues(issues);
117
+ const hasErrors = issues.some((i) => i.severity === 'error');
118
+ if (hasErrors) {
119
+ process.exit(1);
120
+ }
121
+ }
122
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/validate/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};