@xanahlight/component-forge 0.1.0 → 1.5.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 (55) hide show
  1. package/README.md +8 -0
  2. package/dist/commands/check/index.d.ts +30 -0
  3. package/dist/commands/check/index.js +255 -0
  4. package/dist/commands/check/index.js.map +1 -0
  5. package/dist/commands/explain/index.d.ts +12 -0
  6. package/dist/commands/explain/index.js +37 -0
  7. package/dist/commands/explain/index.js.map +1 -0
  8. package/dist/commands/explain/topics.d.ts +3 -0
  9. package/dist/commands/explain/topics.js +138 -0
  10. package/dist/commands/explain/topics.js.map +1 -0
  11. package/dist/commands/{generate.d.ts → generate/index.d.ts} +1 -1
  12. package/dist/commands/{generate.js → generate/index.js} +21 -12
  13. package/dist/commands/generate/index.js.map +1 -0
  14. package/dist/commands/init/index.d.ts +29 -0
  15. package/dist/commands/init/index.js +157 -0
  16. package/dist/commands/init/index.js.map +1 -0
  17. package/dist/commands/migrate/classifier.d.ts +22 -0
  18. package/dist/commands/migrate/classifier.js +48 -0
  19. package/dist/commands/migrate/classifier.js.map +1 -0
  20. package/dist/commands/migrate/index.d.ts +8 -0
  21. package/dist/commands/migrate/index.js +33 -0
  22. package/dist/commands/migrate/index.js.map +1 -0
  23. package/dist/commands/migrate/plan-builder.d.ts +27 -0
  24. package/dist/commands/migrate/plan-builder.js +106 -0
  25. package/dist/commands/migrate/plan-builder.js.map +1 -0
  26. package/dist/commands/migrate/printer.d.ts +6 -0
  27. package/dist/commands/migrate/printer.js +46 -0
  28. package/dist/commands/migrate/printer.js.map +1 -0
  29. package/dist/commands/validate/index.d.ts +26 -0
  30. package/dist/commands/{validate.js → validate/index.js} +8 -5
  31. package/dist/commands/validate/index.js.map +1 -0
  32. package/dist/index.js +106 -10
  33. package/dist/index.js.map +1 -1
  34. package/dist/shared/format.d.ts +26 -0
  35. package/dist/shared/format.js +40 -0
  36. package/dist/shared/format.js.map +1 -0
  37. package/dist/templates/files.d.ts +1 -1
  38. package/dist/templates/files.js +61 -9
  39. package/dist/templates/files.js.map +1 -1
  40. package/dist/types/folder-tree.d.ts +31 -2
  41. package/dist/types/folder-tree.js +18 -0
  42. package/dist/types/folder-tree.js.map +1 -1
  43. package/dist/utils/config.d.ts +19 -6
  44. package/dist/utils/config.js +76 -14
  45. package/dist/utils/config.js.map +1 -1
  46. package/dist/utils/template-resolver.d.ts +1 -1
  47. package/dist/utils/template-resolver.js +1 -1
  48. package/dist/utils/template-resolver.js.map +1 -1
  49. package/package.json +14 -6
  50. package/dist/commands/generate.js.map +0 -1
  51. package/dist/commands/init.d.ts +0 -10
  52. package/dist/commands/init.js +0 -54
  53. package/dist/commands/init.js.map +0 -1
  54. package/dist/commands/validate.d.ts +0 -1
  55. package/dist/commands/validate.js.map +0 -1
@@ -0,0 +1,157 @@
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.loadProjectConfig = exports.CONFIG_FILENAMES = void 0;
7
+ exports.createStructure = createStructure;
8
+ exports.promptInitAnswers = promptInitAnswers;
9
+ exports.runInit = runInit;
10
+ exports.initCommand = initCommand;
11
+ const node_path_1 = __importDefault(require("node:path"));
12
+ const prompts_1 = require("@inquirer/prompts");
13
+ const chalk_1 = __importDefault(require("chalk"));
14
+ const fs_extra_1 = __importDefault(require("fs-extra"));
15
+ const fsd_1 = require("../../templates/fsd");
16
+ const modular_1 = require("../../templates/modular");
17
+ const config_1 = require("../../utils/config");
18
+ Object.defineProperty(exports, "CONFIG_FILENAMES", { enumerable: true, get: function () { return config_1.CONFIG_FILENAMES; } });
19
+ Object.defineProperty(exports, "loadProjectConfig", { enumerable: true, get: function () { return config_1.loadProjectConfig; } });
20
+ const logger_1 = require("../../utils/logger");
21
+ // ---------------------------------------------------------------------------
22
+ // Template registry
23
+ // ---------------------------------------------------------------------------
24
+ const templates = {
25
+ fsd: fsd_1.fsdTemplate,
26
+ modular: modular_1.modularTemplate,
27
+ };
28
+ // ---------------------------------------------------------------------------
29
+ // Architecture descriptions shown in the interactive selector
30
+ // ---------------------------------------------------------------------------
31
+ const ARCHITECTURE_DESCRIPTIONS = {
32
+ fsd: 'Feature-Sliced Design — layer-based architecture for large apps',
33
+ modular: 'Modular — module-centric architecture for medium-sized apps',
34
+ };
35
+ // ---------------------------------------------------------------------------
36
+ // Folder structure creation
37
+ // ---------------------------------------------------------------------------
38
+ /**
39
+ * Recursively creates folder structure from a FolderTree definition.
40
+ * Returns the list of created paths (relative to cwd) for summary output.
41
+ */
42
+ function createStructure(tree, basePath, cwd) {
43
+ const created = [];
44
+ for (const [folderName, children] of Object.entries(tree)) {
45
+ const folderPath = node_path_1.default.join(basePath, folderName);
46
+ fs_extra_1.default.ensureDirSync(folderPath);
47
+ created.push(node_path_1.default.relative(cwd, folderPath));
48
+ created.push(...createStructure(children, folderPath, cwd));
49
+ }
50
+ return created;
51
+ }
52
+ /**
53
+ * Runs interactive prompts to collect init parameters.
54
+ * Throws if the user cancels (Ctrl+C) — caller handles the exit.
55
+ */
56
+ async function promptInitAnswers() {
57
+ console.log(chalk_1.default.bold('\n component-forge — project initialisation\n'));
58
+ const architecture = await (0, prompts_1.select)({
59
+ message: 'Choose your architecture:',
60
+ choices: [
61
+ {
62
+ name: `${chalk_1.default.cyan('FSD')} ${chalk_1.default.gray(ARCHITECTURE_DESCRIPTIONS.fsd)}`,
63
+ value: 'fsd',
64
+ short: 'FSD',
65
+ },
66
+ {
67
+ name: `${chalk_1.default.cyan('Modular')} ${chalk_1.default.gray(ARCHITECTURE_DESCRIPTIONS.modular)}`,
68
+ value: 'modular',
69
+ short: 'Modular',
70
+ },
71
+ ],
72
+ });
73
+ const srcDir = await (0, prompts_1.input)({
74
+ message: 'Source directory:',
75
+ default: 'src',
76
+ validate: (value) => {
77
+ const trimmed = value.trim();
78
+ if (!trimmed)
79
+ return 'Source directory cannot be empty';
80
+ if (/[<>:"|?*]/.test(trimmed))
81
+ return 'Directory name contains invalid characters';
82
+ return true;
83
+ },
84
+ transformer: (value) => chalk_1.default.cyan(value),
85
+ });
86
+ const confirmed = await (0, prompts_1.confirm)({
87
+ message: `Initialise ${chalk_1.default.bold(architecture.toUpperCase())} in ${chalk_1.default.bold(`./${srcDir.trim()}`)}?`,
88
+ default: true,
89
+ });
90
+ if (!confirmed) {
91
+ logger_1.logger.info('Aborted.');
92
+ process.exit(0);
93
+ }
94
+ return { architecture, srcDir: srcDir.trim() };
95
+ }
96
+ // ---------------------------------------------------------------------------
97
+ // Core init logic — pure, no side-effects on I/O apart from FS writes
98
+ // ---------------------------------------------------------------------------
99
+ /**
100
+ * Performs the actual init: creates folder structure + writes config.
101
+ * Exported so it can be called directly (non-interactive) or from tests.
102
+ */
103
+ function runInit(architecture, srcDir, projectRoot) {
104
+ const configPath = node_path_1.default.join(projectRoot, config_1.CONFIG_FILENAMES.json);
105
+ if (fs_extra_1.default.existsSync(configPath)) {
106
+ logger_1.logger.error(`Project already initialised (${config_1.CONFIG_FILENAMES.json} exists).`);
107
+ logger_1.logger.info('Remove it or delete forge.config.ts if you want to reinitialise.');
108
+ process.exit(1);
109
+ }
110
+ logger_1.logger.info(`\nInitialising ${chalk_1.default.bold(architecture.toUpperCase())} architecture in ${chalk_1.default.cyan(`./${srcDir}`)}…\n`);
111
+ const template = templates[architecture];
112
+ // Resolve the base path for structure (respects custom srcDir)
113
+ const basePath = node_path_1.default.join(projectRoot, srcDir);
114
+ const created = createStructure(template, basePath, projectRoot);
115
+ for (const p of created) {
116
+ logger_1.logger.success(`Created: ${p}`);
117
+ }
118
+ const config = { architecture, srcDir };
119
+ (0, config_1.writeProjectConfig)(config, projectRoot);
120
+ logger_1.logger.success(`\nCreated: ${config_1.CONFIG_FILENAMES.json}`);
121
+ logger_1.logger.info(`Tip: rename to forge.config.ts for TypeScript support and IntelliSense.`);
122
+ console.log(chalk_1.default.bold(`\n ✓ ${architecture.toUpperCase()} project initialised successfully!\n`));
123
+ console.log(chalk_1.default.gray(` Next steps:\n`) +
124
+ chalk_1.default.white(` component-forge generate feature <name>\n`) +
125
+ chalk_1.default.white(` component-forge validate\n`) +
126
+ chalk_1.default.white(` component-forge check\n`));
127
+ }
128
+ // ---------------------------------------------------------------------------
129
+ // Command entry point
130
+ // ---------------------------------------------------------------------------
131
+ /**
132
+ * Called from index.ts.
133
+ * If architecture is provided — runs non-interactively (backwards-compatible).
134
+ * If omitted — runs interactive prompts.
135
+ */
136
+ function initCommand(architecture) {
137
+ if (architecture) {
138
+ // Non-interactive path — keep full backwards compatibility
139
+ runInit(architecture, 'src', process.cwd());
140
+ return;
141
+ }
142
+ // Interactive path
143
+ promptInitAnswers()
144
+ .then(({ architecture: arch, srcDir }) => {
145
+ runInit(arch, srcDir, process.cwd());
146
+ })
147
+ .catch((err) => {
148
+ // ExitPromptError is thrown when user presses Ctrl+C
149
+ const isCancel = err instanceof Error && err.name === 'ExitPromptError';
150
+ if (isCancel) {
151
+ console.log(chalk_1.default.yellow('\n Cancelled.\n'));
152
+ process.exit(0);
153
+ }
154
+ throw err;
155
+ });
156
+ }
157
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/init/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Heuristic rules for classifying existing directory names
3
+ * into their FSD / modular equivalents.
4
+ *
5
+ * Patterns are evaluated in order — first match wins.
6
+ * This file owns all classification knowledge; plan-builder.ts is pure logic.
7
+ */
8
+ export interface LayerHeuristic {
9
+ pattern: RegExp;
10
+ layer: string;
11
+ /** Optional sub-directory inside the layer, e.g. "ui" → shared/ui */
12
+ subdir?: string;
13
+ }
14
+ export declare const FSD_HEURISTICS: LayerHeuristic[];
15
+ /**
16
+ * Returns the FSD destination for a given directory name,
17
+ * or `null` if no heuristic matches.
18
+ */
19
+ export declare function classifyDir(dirName: string): {
20
+ layer: string;
21
+ subdir?: string;
22
+ } | null;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ /**
3
+ * Heuristic rules for classifying existing directory names
4
+ * into their FSD / modular equivalents.
5
+ *
6
+ * Patterns are evaluated in order — first match wins.
7
+ * This file owns all classification knowledge; plan-builder.ts is pure logic.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.FSD_HEURISTICS = void 0;
11
+ exports.classifyDir = classifyDir;
12
+ exports.FSD_HEURISTICS = [
13
+ // ── App / bootstrap ─────────────────────────────────────────────────────
14
+ { pattern: /^(app|bootstrap|main|root|providers?|router|store)$/i, layer: 'app' },
15
+ // ── Pages / routes ──────────────────────────────────────────────────────
16
+ { pattern: /^(pages?|routes?|screens?|views?)$/i, layer: 'pages' },
17
+ // ── Widgets (composite blocks) ──────────────────────────────────────────
18
+ { pattern: /^(widgets?|layouts?|containers?|blocks?)$/i, layer: 'widgets' },
19
+ // ── Features (user actions) ─────────────────────────────────────────────
20
+ { pattern: /^(features?|use-?cases?|actions?|mutations?)$/i, layer: 'features' },
21
+ // ── Entities (domain models) ────────────────────────────────────────────
22
+ { pattern: /^(entities|models?|domain|types?|interfaces?)$/i, layer: 'entities' },
23
+ // ── Shared UI ───────────────────────────────────────────────────────────
24
+ { pattern: /^(ui|components?|elements?|atoms?|molecules?)$/i, layer: 'shared', subdir: 'ui' },
25
+ // ── Shared utilities ────────────────────────────────────────────────────
26
+ { pattern: /^(utils?|helpers?|lib|libs?|common|shared)$/i, layer: 'shared' },
27
+ // ── Shared API ──────────────────────────────────────────────────────────
28
+ { pattern: /^(api|services?|http|clients?|fetchers?)$/i, layer: 'shared', subdir: 'api' },
29
+ // ── Shared hooks ────────────────────────────────────────────────────────
30
+ { pattern: /^(hooks?)$/i, layer: 'shared', subdir: 'hooks' },
31
+ // ── Shared config ───────────────────────────────────────────────────────
32
+ { pattern: /^(constants?|config|configs?|settings?)$/i, layer: 'shared', subdir: 'config' },
33
+ // ── Assets (shared but no rename needed) ────────────────────────────────
34
+ { pattern: /^(assets?|images?|icons?|fonts?|static)$/i, layer: 'shared', subdir: 'assets' },
35
+ ];
36
+ /**
37
+ * Returns the FSD destination for a given directory name,
38
+ * or `null` if no heuristic matches.
39
+ */
40
+ function classifyDir(dirName) {
41
+ for (const h of exports.FSD_HEURISTICS) {
42
+ if (h.pattern.test(dirName)) {
43
+ return { layer: h.layer, subdir: h.subdir };
44
+ }
45
+ }
46
+ return null;
47
+ }
48
+ //# sourceMappingURL=classifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.js","sourceRoot":"","sources":["../../../src/commands/migrate/classifier.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAgDH,kCASC;AAhDY,QAAA,cAAc,GAAqB;IAC9C,2EAA2E;IAC3E,EAAE,OAAO,EAAE,sDAAsD,EAAE,KAAK,EAAE,KAAK,EAAE;IAEjF,2EAA2E;IAC3E,EAAE,OAAO,EAAE,qCAAqC,EAAE,KAAK,EAAE,OAAO,EAAE;IAElE,2EAA2E;IAC3E,EAAE,OAAO,EAAE,4CAA4C,EAAE,KAAK,EAAE,SAAS,EAAE;IAE3E,2EAA2E;IAC3E,EAAE,OAAO,EAAE,gDAAgD,EAAE,KAAK,EAAE,UAAU,EAAE;IAEhF,2EAA2E;IAC3E,EAAE,OAAO,EAAE,iDAAiD,EAAE,KAAK,EAAE,UAAU,EAAE;IAEjF,2EAA2E;IAC3E,EAAE,OAAO,EAAE,iDAAiD,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE;IAE7F,2EAA2E;IAC3E,EAAE,OAAO,EAAE,8CAA8C,EAAE,KAAK,EAAE,QAAQ,EAAE;IAE5E,2EAA2E;IAC3E,EAAE,OAAO,EAAE,4CAA4C,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE;IAEzF,2EAA2E;IAC3E,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE;IAE5D,2EAA2E;IAC3E,EAAE,OAAO,EAAE,2CAA2C,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE;IAE3F,2EAA2E;IAC3E,EAAE,OAAO,EAAE,2CAA2C,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE;CAC5F,CAAA;AAED;;;GAGG;AACH,SAAgB,WAAW,CACzB,OAAe;IAEf,KAAK,MAAM,CAAC,IAAI,sBAAc,EAAE,CAAC;QAC/B,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAA;QAC7C,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { Architecture } from '../../types/folder-tree';
2
+ export { buildMigrationPlan, scanTopLevelDirs } from './plan-builder';
3
+ export { classifyDir } from './classifier';
4
+ export type { FileMoveProposal, MigrationPlan } from './plan-builder';
5
+ /**
6
+ * CLI entry point for the migrate command.
7
+ */
8
+ export declare function migrateCommand(targetArchitecture: Architecture): void;
@@ -0,0 +1,33 @@
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.classifyDir = exports.scanTopLevelDirs = exports.buildMigrationPlan = void 0;
7
+ exports.migrateCommand = migrateCommand;
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const config_1 = require("../../utils/config");
11
+ const logger_1 = require("../../utils/logger");
12
+ const plan_builder_1 = require("./plan-builder");
13
+ const printer_1 = require("./printer");
14
+ var plan_builder_2 = require("./plan-builder");
15
+ Object.defineProperty(exports, "buildMigrationPlan", { enumerable: true, get: function () { return plan_builder_2.buildMigrationPlan; } });
16
+ Object.defineProperty(exports, "scanTopLevelDirs", { enumerable: true, get: function () { return plan_builder_2.scanTopLevelDirs; } });
17
+ var classifier_1 = require("./classifier");
18
+ Object.defineProperty(exports, "classifyDir", { enumerable: true, get: function () { return classifier_1.classifyDir; } });
19
+ /**
20
+ * CLI entry point for the migrate command.
21
+ */
22
+ function migrateCommand(targetArchitecture) {
23
+ const config = (0, config_1.loadProjectConfig)();
24
+ if (config.architecture === targetArchitecture) {
25
+ logger_1.logger.info(`Project is already configured as ${chalk_1.default.bold(targetArchitecture.toUpperCase())}.`);
26
+ logger_1.logger.info(`Run ${chalk_1.default.cyan('component-forge validate')} to check the current structure.`);
27
+ return;
28
+ }
29
+ const srcPath = node_path_1.default.join(process.cwd(), config.srcDir);
30
+ const plan = (0, plan_builder_1.buildMigrationPlan)(srcPath, targetArchitecture);
31
+ (0, printer_1.printMigrationPlan)(plan);
32
+ }
33
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/migrate/index.ts"],"names":[],"mappings":";;;;;;AAkBA,wCAgBC;AAlCD,0DAA4B;AAE5B,kDAAyB;AAGzB,+CAAsD;AACtD,+CAA2C;AAE3C,iDAAmD;AACnD,uCAA8C;AAE9C,+CAAqE;AAA5D,kHAAA,kBAAkB,OAAA;AAAE,gHAAA,gBAAgB,OAAA;AAC7C,2CAA0C;AAAjC,yGAAA,WAAW,OAAA;AAGpB;;GAEG;AACH,SAAgB,cAAc,CAAC,kBAAgC;IAC7D,MAAM,MAAM,GAAG,IAAA,0BAAiB,GAAE,CAAA;IAElC,IAAI,MAAM,CAAC,YAAY,KAAK,kBAAkB,EAAE,CAAC;QAC/C,eAAM,CAAC,IAAI,CACT,oCAAoC,eAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,WAAW,EAAE,CAAC,GAAG,CACpF,CAAA;QACD,eAAM,CAAC,IAAI,CACT,OAAO,eAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,kCAAkC,CAChF,CAAA;QACD,OAAM;IACR,CAAC;IAED,MAAM,OAAO,GAAG,mBAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;IACvD,MAAM,IAAI,GAAG,IAAA,iCAAkB,EAAC,OAAO,EAAE,kBAAkB,CAAC,CAAA;IAC5D,IAAA,4BAAkB,EAAC,IAAI,CAAC,CAAA;AAC1B,CAAC"}
@@ -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":";;;;;AAUA,gDAsDC;AAhED,0DAA4B;AAE5B,kDAAyB;AAIzB;;;GAGG;AACH,SAAgB,kBAAkB,CAAC,IAAmB;IACpD,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,GAAG,IAAI,CAAA;IAChF,MAAM,GAAG,GAAG,mBAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,IAAI,GAAG,CAAA;IAE1D,OAAO,CAAC,GAAG,CACT,eAAK,CAAC,IAAI,CAAC,wBAAwB,eAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CACrF,CAAA;IACD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAA;IAC/C,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IAE9C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,SAAS,OAAO,IAAI,CAAC,CAAC,CAAA;QAC9C,OAAM;IACR,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,uBAAuB,SAAS,CAAC,MAAM,MAAM,CAAC,CAAC,CAAA;QAEtE,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAG,eAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;YAC/C,MAAM,KAAK,GAAG,eAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;YAC/B,OAAO,CAAC,GAAG,CAAC,OAAO,OAAO,MAAM,KAAK,EAAE,CAAC,CAAA;YACxC,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;QAChD,CAAC;IACH,CAAC;IAED,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,4BAA4B,YAAY,CAAC,MAAM,MAAM,CAAC,CAAC,CAAA;QAE9E,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,OAAO,eAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,eAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QACxD,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC,CAAA;QAC9E,OAAO,CAAC,GAAG,CACT,eAAK,CAAC,IAAI,CACR,WAAW,eAAK,CAAC,KAAK,CAAC,6BAA6B,CAAC,sCAAsC,CAC5F,CACF,CAAA;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IAChD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,gBAAgB,OAAO,IAAI,CAAC,CAAC,CAAA;IACpD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,MAAM,CAAC,yDAAyD,CAAC,CAAC,CAAA;IACpF,OAAO,CAAC,GAAG,CACT,eAAK,CAAC,IAAI,CACR,8EAA8E,CAC/E,CACF,CAAA;IACD,OAAO,CAAC,GAAG,CACT,eAAK,CAAC,IAAI,CACR,uBAAuB,eAAK,CAAC,KAAK,CAAC,0BAA0B,CAAC,QAAQ,eAAK,CAAC,KAAK,CAAC,uBAAuB,CAAC,eAAe,CAC1H,CACF,CAAA;AACH,CAAC"}
@@ -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 {};
@@ -3,11 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.checkRequiredLayers = checkRequiredLayers;
7
+ exports.checkUnknownLayers = checkUnknownLayers;
8
+ exports.checkPublicApiFiles = checkPublicApiFiles;
6
9
  exports.validateCommand = validateCommand;
7
- const fs_extra_1 = __importDefault(require("fs-extra"));
8
10
  const node_path_1 = __importDefault(require("node:path"));
9
- const config_1 = require("../utils/config");
10
- const logger_1 = require("../utils/logger");
11
+ const fs_extra_1 = __importDefault(require("fs-extra"));
12
+ const config_1 = require("../../utils/config");
13
+ const logger_1 = require("../../utils/logger");
11
14
  const layerRulesByArchitecture = {
12
15
  fsd: {
13
16
  required: ['app', 'shared'],
@@ -19,7 +22,7 @@ const layerRulesByArchitecture = {
19
22
  },
20
23
  };
21
24
  // ---------------------------------------------------------------------------
22
- // Validation rules
25
+ // Validation rules — exported for unit testing
23
26
  // ---------------------------------------------------------------------------
24
27
  /**
25
28
  * Checks that all required layers exist under srcDir
@@ -116,4 +119,4 @@ function validateCommand() {
116
119
  process.exit(1);
117
120
  }
118
121
  }
119
- //# sourceMappingURL=validate.js.map
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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"}