@wp-typia/project-tools 0.16.11 → 0.16.13

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 (119) hide show
  1. package/README.md +9 -3
  2. package/dist/runtime/block-generator-service-core.d.ts +8 -0
  3. package/dist/runtime/block-generator-service-core.js +274 -0
  4. package/dist/runtime/block-generator-service-spec.d.ts +104 -0
  5. package/dist/runtime/block-generator-service-spec.js +139 -0
  6. package/dist/runtime/block-generator-service.d.ts +2 -110
  7. package/dist/runtime/block-generator-service.js +2 -389
  8. package/dist/runtime/built-in-block-artifact-documents.d.ts +3 -0
  9. package/dist/runtime/built-in-block-artifact-documents.js +2 -0
  10. package/dist/runtime/built-in-block-artifact-types.d.ts +51 -0
  11. package/dist/runtime/built-in-block-artifact-types.js +304 -0
  12. package/dist/runtime/built-in-block-artifacts.js +4 -803
  13. package/dist/runtime/built-in-block-attribute-emitters.d.ts +71 -0
  14. package/dist/runtime/built-in-block-attribute-emitters.js +176 -0
  15. package/dist/runtime/built-in-block-attribute-specs.d.ts +38 -0
  16. package/dist/runtime/built-in-block-attribute-specs.js +358 -0
  17. package/dist/runtime/built-in-block-code-templates/basic.d.ts +4 -0
  18. package/dist/runtime/built-in-block-code-templates/basic.js +249 -0
  19. package/dist/runtime/built-in-block-code-templates/compound-child.d.ts +4 -0
  20. package/dist/runtime/built-in-block-code-templates/compound-child.js +138 -0
  21. package/dist/runtime/built-in-block-code-templates/compound-parent.d.ts +6 -0
  22. package/dist/runtime/built-in-block-code-templates/compound-parent.js +227 -0
  23. package/dist/runtime/built-in-block-code-templates/compound-persistence.d.ts +4 -0
  24. package/dist/runtime/built-in-block-code-templates/compound-persistence.js +478 -0
  25. package/dist/runtime/built-in-block-code-templates/compound.d.ts +3 -0
  26. package/dist/runtime/built-in-block-code-templates/compound.js +3 -0
  27. package/dist/runtime/built-in-block-code-templates/interactivity.d.ts +5 -0
  28. package/dist/runtime/built-in-block-code-templates/interactivity.js +547 -0
  29. package/dist/runtime/built-in-block-code-templates/persistence.d.ts +5 -0
  30. package/dist/runtime/built-in-block-code-templates/persistence.js +550 -0
  31. package/dist/runtime/built-in-block-code-templates/shared.d.ts +16 -0
  32. package/dist/runtime/built-in-block-code-templates/shared.js +53 -0
  33. package/dist/runtime/built-in-block-code-templates.d.ts +5 -32
  34. package/dist/runtime/built-in-block-code-templates.js +5 -2230
  35. package/dist/runtime/cli-add-block-config.d.ts +6 -0
  36. package/dist/runtime/cli-add-block-config.js +143 -0
  37. package/dist/runtime/cli-add-block-legacy-validator.d.ts +4 -0
  38. package/dist/runtime/cli-add-block-legacy-validator.js +168 -0
  39. package/dist/runtime/cli-add-block.js +3 -301
  40. package/dist/runtime/cli-add-workspace-assets.d.ts +38 -0
  41. package/dist/runtime/cli-add-workspace-assets.js +399 -0
  42. package/dist/runtime/cli-add-workspace.d.ts +2 -38
  43. package/dist/runtime/cli-add-workspace.js +5 -396
  44. package/dist/runtime/cli-diagnostics.js +76 -4
  45. package/dist/runtime/cli-doctor-environment.d.ts +12 -0
  46. package/dist/runtime/cli-doctor-environment.js +123 -0
  47. package/dist/runtime/cli-doctor-workspace.d.ts +18 -0
  48. package/dist/runtime/cli-doctor-workspace.js +308 -0
  49. package/dist/runtime/cli-doctor.d.ts +4 -2
  50. package/dist/runtime/cli-doctor.js +10 -405
  51. package/dist/runtime/cli-help.js +1 -1
  52. package/dist/runtime/cli-scaffold.d.ts +8 -1
  53. package/dist/runtime/cli-scaffold.js +47 -4
  54. package/dist/runtime/migration-command-surface.d.ts +67 -0
  55. package/dist/runtime/migration-command-surface.js +189 -0
  56. package/dist/runtime/migration-diff-rename.d.ts +13 -0
  57. package/dist/runtime/migration-diff-rename.js +192 -0
  58. package/dist/runtime/migration-diff-transform.d.ts +14 -0
  59. package/dist/runtime/migration-diff-transform.js +105 -0
  60. package/dist/runtime/migration-diff.js +12 -297
  61. package/dist/runtime/migration-generated-artifacts.d.ts +3 -0
  62. package/dist/runtime/migration-generated-artifacts.js +41 -0
  63. package/dist/runtime/migration-maintenance-fixtures.d.ts +23 -0
  64. package/dist/runtime/migration-maintenance-fixtures.js +126 -0
  65. package/dist/runtime/migration-maintenance-verify.d.ts +26 -0
  66. package/dist/runtime/migration-maintenance-verify.js +262 -0
  67. package/dist/runtime/migration-maintenance.d.ts +2 -0
  68. package/dist/runtime/migration-maintenance.js +2 -0
  69. package/dist/runtime/migration-planning.d.ts +23 -0
  70. package/dist/runtime/migration-planning.js +131 -0
  71. package/dist/runtime/migration-project-config-source.d.ts +6 -0
  72. package/dist/runtime/migration-project-config-source.js +424 -0
  73. package/dist/runtime/migration-project-layout-discovery.d.ts +61 -0
  74. package/dist/runtime/migration-project-layout-discovery.js +337 -0
  75. package/dist/runtime/migration-project-layout-paths.d.ts +135 -0
  76. package/dist/runtime/migration-project-layout-paths.js +288 -0
  77. package/dist/runtime/migration-project-layout.d.ts +3 -0
  78. package/dist/runtime/migration-project-layout.js +2 -0
  79. package/dist/runtime/migration-project-workspace.d.ts +47 -0
  80. package/dist/runtime/migration-project-workspace.js +212 -0
  81. package/dist/runtime/migration-project.d.ts +4 -94
  82. package/dist/runtime/migration-project.js +3 -1101
  83. package/dist/runtime/migration-render-diff-rule.d.ts +5 -0
  84. package/dist/runtime/migration-render-diff-rule.js +120 -0
  85. package/dist/runtime/migration-render-execution.d.ts +3 -0
  86. package/dist/runtime/migration-render-execution.js +428 -0
  87. package/dist/runtime/migration-render-generated.d.ts +27 -0
  88. package/dist/runtime/migration-render-generated.js +230 -0
  89. package/dist/runtime/migration-render-support.d.ts +3 -0
  90. package/dist/runtime/migration-render-support.js +16 -0
  91. package/dist/runtime/migration-render.d.ts +3 -33
  92. package/dist/runtime/migration-render.js +3 -789
  93. package/dist/runtime/migrations.d.ts +24 -121
  94. package/dist/runtime/migrations.js +12 -700
  95. package/dist/runtime/scaffold-apply-utils.d.ts +9 -0
  96. package/dist/runtime/scaffold-apply-utils.js +27 -4
  97. package/dist/runtime/scaffold-bootstrap.d.ts +45 -0
  98. package/dist/runtime/scaffold-bootstrap.js +185 -0
  99. package/dist/runtime/scaffold-onboarding.d.ts +12 -0
  100. package/dist/runtime/scaffold-onboarding.js +42 -5
  101. package/dist/runtime/scaffold-package-manager-files.d.ts +35 -0
  102. package/dist/runtime/scaffold-package-manager-files.js +79 -0
  103. package/dist/runtime/scaffold.d.ts +1 -12
  104. package/dist/runtime/scaffold.js +11 -394
  105. package/dist/runtime/template-source-contracts.d.ts +81 -0
  106. package/dist/runtime/template-source-contracts.js +1 -0
  107. package/dist/runtime/template-source-external.d.ts +21 -0
  108. package/dist/runtime/template-source-external.js +184 -0
  109. package/dist/runtime/template-source-locators.d.ts +4 -0
  110. package/dist/runtime/template-source-locators.js +72 -0
  111. package/dist/runtime/template-source-normalization.d.ts +7 -0
  112. package/dist/runtime/template-source-normalization.js +53 -0
  113. package/dist/runtime/template-source-remote.d.ts +23 -0
  114. package/dist/runtime/template-source-remote.js +336 -0
  115. package/dist/runtime/template-source-seeds.d.ts +12 -0
  116. package/dist/runtime/template-source-seeds.js +243 -0
  117. package/dist/runtime/template-source.d.ts +4 -86
  118. package/dist/runtime/template-source.js +9 -828
  119. package/package.json +4 -4
@@ -0,0 +1,288 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { CONFIG_FILE, FIXTURES_DIR, GENERATED_DIR, MIGRATION_TODO_PREFIX, ROOT_BLOCK_JSON, ROOT_MANIFEST, RULES_DIR, SNAPSHOT_DIR, } from "./migration-constants.js";
4
+ import { compareMigrationVersionLabels, } from "./migration-utils.js";
5
+ import { ensureRelativePath, normalizeRelativePath, } from "./migration-project-config-source.js";
6
+ function toImportPath(fromDir, targetPath, stripExtension = false) {
7
+ let relativePath = normalizeRelativePath(path.relative(fromDir, targetPath));
8
+ if (!relativePath.startsWith(".")) {
9
+ relativePath = `./${relativePath}`;
10
+ }
11
+ if (stripExtension) {
12
+ relativePath = relativePath.replace(/\.[^.]+$/u, "");
13
+ }
14
+ return relativePath;
15
+ }
16
+ /**
17
+ * Resolves the canonical migration workspace paths for a project directory.
18
+ *
19
+ * @param projectDir Project directory containing the migration workspace.
20
+ * @returns Absolute filesystem paths for config, generated, fixture, rule, and snapshot roots.
21
+ */
22
+ export function getProjectPaths(projectDir) {
23
+ return {
24
+ configFile: path.join(projectDir, CONFIG_FILE),
25
+ fixturesDir: path.join(projectDir, FIXTURES_DIR),
26
+ generatedDir: path.join(projectDir, GENERATED_DIR),
27
+ rulesDir: path.join(projectDir, RULES_DIR),
28
+ snapshotDir: path.join(projectDir, SNAPSHOT_DIR),
29
+ };
30
+ }
31
+ /**
32
+ * Resolves the snapshot root for a block target and migration version.
33
+ *
34
+ * @param projectDir Project directory containing the migration workspace.
35
+ * @param block Block target or resolved block target.
36
+ * @param version Migration version label.
37
+ * @returns The absolute snapshot root directory for the requested block/version pair.
38
+ */
39
+ export function getSnapshotRoot(projectDir, block, version) {
40
+ if ("layout" in block && block.layout === "legacy") {
41
+ return path.join(projectDir, SNAPSHOT_DIR, version);
42
+ }
43
+ return path.join(projectDir, SNAPSHOT_DIR, version, block.key);
44
+ }
45
+ /**
46
+ * Resolves the snapshot `block.json` path for a block target and migration version.
47
+ *
48
+ * @param projectDir Project directory containing the migration workspace.
49
+ * @param block Block target or resolved block target.
50
+ * @param version Migration version label.
51
+ * @returns The absolute snapshot `block.json` path.
52
+ */
53
+ export function getSnapshotBlockJsonPath(projectDir, block, version) {
54
+ return path.join(getSnapshotRoot(projectDir, block, version), ROOT_BLOCK_JSON);
55
+ }
56
+ /**
57
+ * Resolves the snapshot manifest path for a block target and migration version.
58
+ *
59
+ * @param projectDir Project directory containing the migration workspace.
60
+ * @param block Block target or resolved block target.
61
+ * @param version Migration version label.
62
+ * @returns The absolute snapshot manifest path.
63
+ */
64
+ export function getSnapshotManifestPath(projectDir, block, version) {
65
+ return path.join(getSnapshotRoot(projectDir, block, version), ROOT_MANIFEST);
66
+ }
67
+ /**
68
+ * Lists the snapshot versions currently present for a specific block target.
69
+ *
70
+ * Returns the sorted subset of supported migration versions that have a manifest on disk
71
+ * for the provided block, or an empty array when none exist.
72
+ */
73
+ export function getAvailableSnapshotVersionsForBlock(projectDir, supportedMigrationVersions, block) {
74
+ return supportedMigrationVersions
75
+ .filter((version) => fs.existsSync(getSnapshotManifestPath(projectDir, block, version)))
76
+ .sort(compareMigrationVersionLabels);
77
+ }
78
+ /**
79
+ * Formats the standard missing-snapshot guidance for a block target.
80
+ *
81
+ * Returns a user-facing message that either lists the available snapshot
82
+ * versions or explains that no snapshots exist yet for the block.
83
+ */
84
+ export function createMissingBlockSnapshotMessage(blockName, fromVersion, availableSnapshotVersions) {
85
+ return availableSnapshotVersions.length === 0
86
+ ? `Snapshot manifest for ${blockName} @ ${fromVersion} does not exist. ` +
87
+ `No snapshots exist yet for ${blockName}. Run \`wp-typia migrate snapshot --migration-version ${fromVersion}\` first.`
88
+ : `Snapshot manifest for ${blockName} @ ${fromVersion} does not exist. ` +
89
+ `Available snapshot versions for ${blockName}: ${availableSnapshotVersions.join(", ")}. ` +
90
+ `Run \`wp-typia migrate snapshot --migration-version ${fromVersion}\` first if you want to preserve that release.`;
91
+ }
92
+ /**
93
+ * Resolves the snapshot save source path for a block target and migration version.
94
+ *
95
+ * @param projectDir Project directory containing the migration workspace.
96
+ * @param block Block target or resolved block target.
97
+ * @param version Migration version label.
98
+ * @returns The absolute snapshot `save.tsx` path.
99
+ */
100
+ export function getSnapshotSavePath(projectDir, block, version) {
101
+ return path.join(getSnapshotRoot(projectDir, block, version), "save.tsx");
102
+ }
103
+ /**
104
+ * Resolves the generated directory used for a block's migration output.
105
+ *
106
+ * @param paths Resolved migration project paths.
107
+ * @param block Block target or resolved block target.
108
+ * @returns The absolute generated directory for the block.
109
+ */
110
+ export function getGeneratedDirForBlock(paths, block) {
111
+ if ("layout" in block && block.layout === "legacy") {
112
+ return paths.generatedDir;
113
+ }
114
+ return path.join(paths.generatedDir, block.key);
115
+ }
116
+ /**
117
+ * Resolves the migration rule file path for a block version edge.
118
+ *
119
+ * @param paths Resolved migration project paths.
120
+ * @param block Block target or resolved block target.
121
+ * @param fromVersion Source migration version.
122
+ * @param toVersion Destination migration version.
123
+ * @returns The absolute migration rule file path.
124
+ */
125
+ export function getRuleFilePath(paths, block, fromVersion, toVersion) {
126
+ if ("layout" in block && block.layout === "legacy") {
127
+ return path.join(paths.rulesDir, `${fromVersion}-to-${toVersion}.ts`);
128
+ }
129
+ return path.join(paths.rulesDir, block.key, `${fromVersion}-to-${toVersion}.ts`);
130
+ }
131
+ /**
132
+ * Resolves the migration fixture path for a block version edge.
133
+ *
134
+ * @param paths Resolved migration project paths.
135
+ * @param block Block target or resolved block target.
136
+ * @param fromVersion Source migration version.
137
+ * @param toVersion Destination migration version.
138
+ * @returns The absolute migration fixture file path.
139
+ */
140
+ export function getFixtureFilePath(paths, block, fromVersion, toVersion) {
141
+ if ("layout" in block && block.layout === "legacy") {
142
+ return path.join(paths.fixturesDir, `${fromVersion}-to-${toVersion}.json`);
143
+ }
144
+ return path.join(paths.fixturesDir, block.key, `${fromVersion}-to-${toVersion}.json`);
145
+ }
146
+ /**
147
+ * Resolves the validators import path for generated migration artifacts.
148
+ *
149
+ * @param projectDir Project directory containing the migration workspace.
150
+ * @param block Block target or resolved block target.
151
+ * @param fromDir Directory that will import the validators module.
152
+ * @returns A relative module import path without a file extension.
153
+ */
154
+ export function getValidatorsImportPath(projectDir, block, fromDir) {
155
+ const validatorPath = path.join(projectDir, block.typesFile.replace(/types\.ts$/u, "validators.ts"));
156
+ return toImportPath(fromDir, validatorPath, true);
157
+ }
158
+ /**
159
+ * Ensures the migration fixture, generated, rule, and snapshot directories exist.
160
+ *
161
+ * @param projectDir Project directory containing the migration workspace.
162
+ * @param blocks Optional block targets whose scoped rule/fixture directories should be created.
163
+ * @returns Nothing.
164
+ */
165
+ export function ensureMigrationDirectories(projectDir, blocks) {
166
+ const paths = getProjectPaths(projectDir);
167
+ fs.mkdirSync(paths.fixturesDir, { recursive: true });
168
+ fs.mkdirSync(paths.generatedDir, { recursive: true });
169
+ fs.mkdirSync(paths.rulesDir, { recursive: true });
170
+ fs.mkdirSync(paths.snapshotDir, { recursive: true });
171
+ if (!Array.isArray(blocks) || blocks.length === 0) {
172
+ return;
173
+ }
174
+ for (const block of blocks) {
175
+ fs.mkdirSync(path.join(paths.fixturesDir, block.key), { recursive: true });
176
+ fs.mkdirSync(path.join(paths.generatedDir, block.key), { recursive: true });
177
+ fs.mkdirSync(path.join(paths.rulesDir, block.key), { recursive: true });
178
+ }
179
+ }
180
+ /**
181
+ * Discovers the generated migration registry entries for a loaded project state.
182
+ *
183
+ * @param state Loaded migration project state.
184
+ * @returns Sorted migration registry entries for every available block/version edge.
185
+ */
186
+ export function discoverMigrationEntries(state) {
187
+ const entries = [];
188
+ const currentVersion = state.config.currentMigrationVersion;
189
+ for (const block of state.blocks) {
190
+ const generatedDir = getGeneratedDirForBlock(state.paths, block);
191
+ for (const version of state.config.supportedMigrationVersions) {
192
+ if (version === currentVersion) {
193
+ continue;
194
+ }
195
+ const manifestPath = getSnapshotManifestPath(state.projectDir, block, version);
196
+ const blockJsonPath = getSnapshotBlockJsonPath(state.projectDir, block, version);
197
+ const savePath = getSnapshotSavePath(state.projectDir, block, version);
198
+ const rulePath = getRuleFilePath(state.paths, block, version, currentVersion);
199
+ if (!fs.existsSync(manifestPath) ||
200
+ !fs.existsSync(blockJsonPath) ||
201
+ !fs.existsSync(savePath) ||
202
+ !fs.existsSync(rulePath)) {
203
+ continue;
204
+ }
205
+ entries.push({
206
+ block,
207
+ blockJsonImport: toImportPath(generatedDir, blockJsonPath),
208
+ fixtureImport: toImportPath(generatedDir, getFixtureFilePath(state.paths, block, version, currentVersion)),
209
+ fromVersion: version,
210
+ generatedDir,
211
+ manifestImport: toImportPath(generatedDir, manifestPath),
212
+ ruleImport: toImportPath(generatedDir, rulePath, true),
213
+ rulePath,
214
+ saveImport: toImportPath(generatedDir, savePath, true),
215
+ toVersion: currentVersion,
216
+ validatorImport: getValidatorsImportPath(state.projectDir, block, generatedDir),
217
+ });
218
+ }
219
+ }
220
+ return entries.sort((left, right) => {
221
+ const versionDelta = compareMigrationVersionLabels(right.fromVersion, left.fromVersion);
222
+ if (versionDelta !== 0) {
223
+ return versionDelta;
224
+ }
225
+ return left.block.key.localeCompare(right.block.key);
226
+ });
227
+ }
228
+ /**
229
+ * Verifies that a migration rule file exists and no TODO markers remain in it.
230
+ *
231
+ * @param projectDir Project directory containing the migration workspace.
232
+ * @param block Block target or resolved block target.
233
+ * @param fromMigrationVersion Source migration version.
234
+ * @param toMigrationVersion Destination migration version.
235
+ * @returns Nothing.
236
+ * @throws Error When the rule file is missing or still contains TODO markers.
237
+ */
238
+ export function assertRuleHasNoTodos(projectDir, block, fromMigrationVersion, toMigrationVersion) {
239
+ const rulePath = getRuleFilePath(getProjectPaths(projectDir), block, fromMigrationVersion, toMigrationVersion);
240
+ if (!fs.existsSync(rulePath)) {
241
+ throw new Error(`Missing migration rule: ${path.relative(projectDir, rulePath)}`);
242
+ }
243
+ const source = fs.readFileSync(rulePath, "utf8");
244
+ if (source.includes(MIGRATION_TODO_PREFIX)) {
245
+ throw new Error(`Migration rule still contains ${MIGRATION_TODO_PREFIX} markers: ${path.relative(projectDir, rulePath)}`);
246
+ }
247
+ }
248
+ /**
249
+ * Reads lightweight metadata from a migration rule source file.
250
+ *
251
+ * @param rulePath Absolute path to the migration rule source file.
252
+ * @returns Parsed unresolved paths, rename mappings, and transform keys.
253
+ */
254
+ export function readRuleMetadata(rulePath) {
255
+ const source = fs.readFileSync(rulePath, "utf8");
256
+ const unresolvedBlock = source.match(/export const unresolved = \[([\s\S]*?)\] as const;/);
257
+ const renameMapBlock = source.match(/export const renameMap: RenameMap = \{([\s\S]*?)\};/);
258
+ const transformsBlock = source.match(/export const transforms: TransformMap = \{([\s\S]*?)\};/);
259
+ const unresolved = unresolvedBlock
260
+ ? [...unresolvedBlock[1].matchAll(/"([^"]+)"/g)].map((match) => match[1])
261
+ : [];
262
+ const renameMap = renameMapBlock
263
+ ? [...renameMapBlock[1].matchAll(/^\s*"([^"]+)":\s*"([^"]+)"/gm)].map((match) => ({
264
+ currentPath: match[1],
265
+ legacyPath: match[2],
266
+ }))
267
+ : [];
268
+ const transforms = transformsBlock
269
+ ? [...transformsBlock[1].matchAll(/^\s*"([^"]+)":\s*\(/gm)].map((match) => match[1])
270
+ : [];
271
+ return { renameMap, transforms, unresolved };
272
+ }
273
+ /**
274
+ * Normalizes a migration block config against the current working directory.
275
+ *
276
+ * @param block Migration block config with project-relative file paths.
277
+ * @returns A normalized migration block config with ensured relative paths.
278
+ */
279
+ export function createMigrationBlockConfig(block) {
280
+ return {
281
+ blockJsonFile: ensureRelativePath(process.cwd(), block.blockJsonFile),
282
+ blockName: block.blockName,
283
+ key: block.key,
284
+ manifestFile: ensureRelativePath(process.cwd(), block.manifestFile),
285
+ saveFile: ensureRelativePath(process.cwd(), block.saveFile),
286
+ typesFile: ensureRelativePath(process.cwd(), block.typesFile),
287
+ };
288
+ }
@@ -0,0 +1,3 @@
1
+ export type { DiscoveredMigrationLayout } from "./migration-project-layout-discovery.js";
2
+ export { createImplicitLegacyBlock, discoverMigrationInitLayout, ensureAdvancedMigrationProject, readProjectBlockName, resolveMigrationBlocks, } from "./migration-project-layout-discovery.js";
3
+ export { assertRuleHasNoTodos, createMigrationBlockConfig, createMissingBlockSnapshotMessage, discoverMigrationEntries, ensureMigrationDirectories, getAvailableSnapshotVersionsForBlock, getFixtureFilePath, getGeneratedDirForBlock, getProjectPaths, getRuleFilePath, getSnapshotBlockJsonPath, getSnapshotManifestPath, getSnapshotRoot, getSnapshotSavePath, getValidatorsImportPath, readRuleMetadata, } from "./migration-project-layout-paths.js";
@@ -0,0 +1,2 @@
1
+ export { createImplicitLegacyBlock, discoverMigrationInitLayout, ensureAdvancedMigrationProject, readProjectBlockName, resolveMigrationBlocks, } from "./migration-project-layout-discovery.js";
2
+ export { assertRuleHasNoTodos, createMigrationBlockConfig, createMissingBlockSnapshotMessage, discoverMigrationEntries, ensureMigrationDirectories, getAvailableSnapshotVersionsForBlock, getFixtureFilePath, getGeneratedDirForBlock, getProjectPaths, getRuleFilePath, getSnapshotBlockJsonPath, getSnapshotManifestPath, getSnapshotRoot, getSnapshotSavePath, getValidatorsImportPath, readRuleMetadata, } from "./migration-project-layout-paths.js";
@@ -0,0 +1,47 @@
1
+ import type { MigrationBlockConfig, MigrationConfig, MigrationProjectState } from "./migration-types.js";
2
+ /**
3
+ * Writes the initial migration README scaffolds for a project workspace.
4
+ *
5
+ * @param projectDir Project directory containing the migration workspace.
6
+ * @param currentMigrationVersion Current migration version label used in README text.
7
+ * @param blocks Optional block targets whose scoped READMEs should be scaffolded.
8
+ * @returns Nothing.
9
+ */
10
+ export declare function writeInitialMigrationScaffold(projectDir: string, currentMigrationVersion: string, blocks?: MigrationBlockConfig[]): void;
11
+ /**
12
+ * Guards a project directory against legacy semver-based migration workspaces.
13
+ *
14
+ * @param projectDir Absolute or relative project directory containing the migration workspace.
15
+ * @returns Nothing.
16
+ * @throws Error When legacy config keys or semver-named migration artifacts are detected.
17
+ */
18
+ export declare function assertNoLegacySemverMigrationWorkspace(projectDir: string): void;
19
+ /**
20
+ * Loads the migration workspace state for a project directory.
21
+ *
22
+ * By default this loader may run the project's `sync-types` script when the
23
+ * current manifest files are missing, because later migration commands depend
24
+ * on those generated artifacts. Pass `allowSyncTypes: false` to keep the call
25
+ * read-only and fail instead of mutating the workspace.
26
+ *
27
+ * When `allowMissingConfig` is enabled and the migration config file does not
28
+ * exist yet, the loader synthesizes a minimal legacy-root config so bootstrap
29
+ * flows can continue before the first config write.
30
+ *
31
+ * @param projectDir Absolute or relative project directory containing the migration workspace.
32
+ * @param options Loader flags controlling config fallback and `sync-types` side effects.
33
+ * @returns The resolved migration project state, including config, block targets, and helper paths.
34
+ * @throws Error When the project is not migration-capable, required manifests remain missing, or generated files cannot be read.
35
+ */
36
+ export declare function loadMigrationProject(projectDir: string, { allowMissingConfig, allowSyncTypes, }?: {
37
+ allowMissingConfig?: boolean;
38
+ allowSyncTypes?: boolean;
39
+ }): MigrationProjectState;
40
+ /**
41
+ * Writes the migration config source file for a project workspace.
42
+ *
43
+ * @param projectDir Project directory containing the migration workspace.
44
+ * @param config Parsed or synthesized migration config to serialize.
45
+ * @returns Nothing.
46
+ */
47
+ export declare function writeMigrationConfig(projectDir: string, config: MigrationConfig): void;
@@ -0,0 +1,212 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { FIXTURES_DIR, ROOT_BLOCK_JSON, ROOT_MANIFEST, RULES_DIR, SNAPSHOT_DIR, } from "./migration-constants.js";
4
+ import { isLegacySemverMigrationVersion, readJson, runProjectScriptIfPresent, } from "./migration-utils.js";
5
+ import { createLegacyMigrationWorkspaceResetError, hasLegacyConfigKeys, normalizeRelativePath, parseMigrationConfig, } from "./migration-project-config-source.js";
6
+ import { createImplicitLegacyBlock, ensureAdvancedMigrationProject, getProjectPaths, readProjectBlockName, resolveMigrationBlocks, } from "./migration-project-layout.js";
7
+ const LEGACY_VERSIONED_EDGE_FILE_PATTERN = /^(\d+\.\d+\.\d+)-to-(\d+\.\d+\.\d+)\.(?:ts|json)$/;
8
+ function createEmptyMigrationProjectBlockJson() {
9
+ return {};
10
+ }
11
+ function createEmptyMigrationProjectManifest() {
12
+ return {
13
+ attributes: {},
14
+ manifestVersion: 2,
15
+ sourceType: null,
16
+ };
17
+ }
18
+ /**
19
+ * Writes the initial migration README scaffolds for a project workspace.
20
+ *
21
+ * @param projectDir Project directory containing the migration workspace.
22
+ * @param currentMigrationVersion Current migration version label used in README text.
23
+ * @param blocks Optional block targets whose scoped READMEs should be scaffolded.
24
+ * @returns Nothing.
25
+ */
26
+ export function writeInitialMigrationScaffold(projectDir, currentMigrationVersion, blocks) {
27
+ const paths = getProjectPaths(projectDir);
28
+ const readmeFiles = [
29
+ [path.join(paths.snapshotDir, "README.md"), `# Migration Version Snapshots\n\nSnapshots for ${currentMigrationVersion} and future migration versions live here.\n`],
30
+ [path.join(paths.rulesDir, "README.md"), "# Migration Rules\n\nScaffold direct legacy-to-current migration rules in this directory.\n"],
31
+ [path.join(paths.fixturesDir, "README.md"), "# Migration Fixtures\n\nGenerated fixtures are used by verify to assert migrations.\n"],
32
+ ];
33
+ for (const [targetPath, content] of readmeFiles) {
34
+ if (!fs.existsSync(targetPath)) {
35
+ fs.writeFileSync(targetPath, content, "utf8");
36
+ }
37
+ }
38
+ if (!Array.isArray(blocks) || blocks.length === 0) {
39
+ return;
40
+ }
41
+ for (const block of blocks) {
42
+ const scopedReadmes = [
43
+ [path.join(paths.rulesDir, block.key, "README.md"), `# ${block.blockName} Migration Rules\n\nScaffold direct legacy-to-current migration rules for ${block.blockName} in this directory.\n`],
44
+ [path.join(paths.fixturesDir, block.key, "README.md"), `# ${block.blockName} Migration Fixtures\n\nGenerated fixtures for ${block.blockName} are stored in this directory.\n`],
45
+ ];
46
+ for (const [targetPath, content] of scopedReadmes) {
47
+ if (!fs.existsSync(targetPath)) {
48
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
49
+ fs.writeFileSync(targetPath, content, "utf8");
50
+ }
51
+ }
52
+ }
53
+ }
54
+ function findLegacySemverMigrationArtifacts(projectDir) {
55
+ const paths = getProjectPaths(projectDir);
56
+ const matches = [];
57
+ if (fs.existsSync(paths.snapshotDir)) {
58
+ for (const entry of fs.readdirSync(paths.snapshotDir, { withFileTypes: true })) {
59
+ if (entry.isDirectory() && isLegacySemverMigrationVersion(entry.name)) {
60
+ matches.push(normalizeRelativePath(path.join(SNAPSHOT_DIR, entry.name)));
61
+ }
62
+ }
63
+ }
64
+ const scanEdgeDir = (directory, relativeRoot) => {
65
+ if (!fs.existsSync(directory)) {
66
+ return;
67
+ }
68
+ const walk = (currentDir, currentRelativeDir) => {
69
+ for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) {
70
+ if (entry.isDirectory()) {
71
+ walk(path.join(currentDir, entry.name), path.join(currentRelativeDir, entry.name));
72
+ continue;
73
+ }
74
+ if (LEGACY_VERSIONED_EDGE_FILE_PATTERN.test(entry.name)) {
75
+ matches.push(normalizeRelativePath(path.join(currentRelativeDir, entry.name)));
76
+ }
77
+ }
78
+ };
79
+ walk(directory, relativeRoot);
80
+ };
81
+ scanEdgeDir(paths.rulesDir, RULES_DIR);
82
+ scanEdgeDir(paths.fixturesDir, FIXTURES_DIR);
83
+ return matches;
84
+ }
85
+ /**
86
+ * Guards a project directory against legacy semver-based migration workspaces.
87
+ *
88
+ * @param projectDir Absolute or relative project directory containing the migration workspace.
89
+ * @returns Nothing.
90
+ * @throws Error When legacy config keys or semver-named migration artifacts are detected.
91
+ */
92
+ export function assertNoLegacySemverMigrationWorkspace(projectDir) {
93
+ const paths = getProjectPaths(projectDir);
94
+ if (fs.existsSync(paths.configFile)) {
95
+ const source = fs.readFileSync(paths.configFile, "utf8");
96
+ if (hasLegacyConfigKeys(source)) {
97
+ throw createLegacyMigrationWorkspaceResetError("Detected legacy config keys `currentVersion` / `supportedVersions` in `src/migrations/config.ts`.");
98
+ }
99
+ }
100
+ const artifactMatches = findLegacySemverMigrationArtifacts(projectDir);
101
+ if (artifactMatches.length > 0) {
102
+ throw createLegacyMigrationWorkspaceResetError(`Detected legacy semver-named migration artifacts: ${artifactMatches.join(", ")}.`);
103
+ }
104
+ }
105
+ /**
106
+ * Loads the migration workspace state for a project directory.
107
+ *
108
+ * By default this loader may run the project's `sync-types` script when the
109
+ * current manifest files are missing, because later migration commands depend
110
+ * on those generated artifacts. Pass `allowSyncTypes: false` to keep the call
111
+ * read-only and fail instead of mutating the workspace.
112
+ *
113
+ * When `allowMissingConfig` is enabled and the migration config file does not
114
+ * exist yet, the loader synthesizes a minimal legacy-root config so bootstrap
115
+ * flows can continue before the first config write.
116
+ *
117
+ * @param projectDir Absolute or relative project directory containing the migration workspace.
118
+ * @param options Loader flags controlling config fallback and `sync-types` side effects.
119
+ * @returns The resolved migration project state, including config, block targets, and helper paths.
120
+ * @throws Error When the project is not migration-capable, required manifests remain missing, or generated files cannot be read.
121
+ */
122
+ export function loadMigrationProject(projectDir, { allowMissingConfig = false, allowSyncTypes = true, } = {}) {
123
+ assertNoLegacySemverMigrationWorkspace(projectDir);
124
+ ensureAdvancedMigrationProject(projectDir);
125
+ const paths = getProjectPaths(projectDir);
126
+ const config = allowMissingConfig && !fs.existsSync(paths.configFile)
127
+ ? {
128
+ blocks: [createImplicitLegacyBlock(projectDir)],
129
+ currentMigrationVersion: "v1",
130
+ snapshotDir: SNAPSHOT_DIR.replace(/\\/g, "/"),
131
+ supportedMigrationVersions: [],
132
+ }
133
+ : parseMigrationConfig(fs.readFileSync(paths.configFile, "utf8"));
134
+ const configuredBlocks = config.blocks === undefined ? [createImplicitLegacyBlock(projectDir, config.blockName)] : config.blocks;
135
+ const missingManifestFiles = configuredBlocks
136
+ .filter((block) => !fs.existsSync(path.join(projectDir, block.manifestFile)))
137
+ .map((block) => block.manifestFile);
138
+ if (missingManifestFiles.length > 0) {
139
+ if (!allowSyncTypes) {
140
+ throw new Error("Migration planning is read-only and cannot run `sync-types` automatically. " +
141
+ `Missing current manifest file(s): ${missingManifestFiles.join(", ")}. ` +
142
+ "Run your project's `sync-types` script in the project root first, then rerun the planning command.");
143
+ }
144
+ runProjectScriptIfPresent(projectDir, "sync-types");
145
+ const remainingManifestFiles = configuredBlocks
146
+ .filter((block) => !fs.existsSync(path.join(projectDir, block.manifestFile)))
147
+ .map((block) => block.manifestFile);
148
+ if (remainingManifestFiles.length > 0) {
149
+ throw new Error(`Missing current manifest file(s): ${remainingManifestFiles.join(", ")}. ` +
150
+ "Run your project's `sync-types` script in the project root first, then retry.");
151
+ }
152
+ }
153
+ const blocks = resolveMigrationBlocks(projectDir, config);
154
+ return {
155
+ blocks,
156
+ config,
157
+ currentBlockJson: blocks[0]?.currentBlockJson ??
158
+ (config.blocks !== undefined
159
+ ? createEmptyMigrationProjectBlockJson()
160
+ : readJson(path.join(projectDir, ROOT_BLOCK_JSON))),
161
+ currentManifest: blocks[0]?.currentManifest ??
162
+ (config.blocks !== undefined
163
+ ? createEmptyMigrationProjectManifest()
164
+ : readJson(path.join(projectDir, ROOT_MANIFEST))),
165
+ paths,
166
+ projectDir,
167
+ };
168
+ }
169
+ /**
170
+ * Writes the migration config source file for a project workspace.
171
+ *
172
+ * @param projectDir Project directory containing the migration workspace.
173
+ * @param config Parsed or synthesized migration config to serialize.
174
+ * @returns Nothing.
175
+ */
176
+ export function writeMigrationConfig(projectDir, config) {
177
+ const paths = getProjectPaths(projectDir);
178
+ fs.mkdirSync(path.dirname(paths.configFile), { recursive: true });
179
+ if (!Array.isArray(config.blocks)) {
180
+ fs.writeFileSync(paths.configFile, `export const migrationConfig = {
181
+ \tblockName: '${config.blockName ?? readProjectBlockName(projectDir)}',
182
+ \tcurrentMigrationVersion: '${config.currentMigrationVersion}',
183
+ \tsupportedMigrationVersions: [ ${config.supportedMigrationVersions.map((version) => `'${version}'`).join(", ")} ],
184
+ \tsnapshotDir: '${config.snapshotDir}',
185
+ } as const;
186
+
187
+ export default migrationConfig;
188
+ `, "utf8");
189
+ return;
190
+ }
191
+ const blocksSource = config.blocks
192
+ .map((block) => `\t\t{
193
+ \t\t\tkey: '${block.key}',
194
+ \t\t\tblockName: '${block.blockName}',
195
+ \t\t\tblockJsonFile: '${normalizeRelativePath(block.blockJsonFile)}',
196
+ \t\t\tmanifestFile: '${normalizeRelativePath(block.manifestFile)}',
197
+ \t\t\tsaveFile: '${normalizeRelativePath(block.saveFile)}',
198
+ \t\t\ttypesFile: '${normalizeRelativePath(block.typesFile)}',
199
+ \t\t},`)
200
+ .join("\n");
201
+ fs.writeFileSync(paths.configFile, `export const migrationConfig = {
202
+ \tcurrentMigrationVersion: '${config.currentMigrationVersion}',
203
+ \tsupportedMigrationVersions: [ ${config.supportedMigrationVersions.map((version) => `'${version}'`).join(", ")} ],
204
+ \tsnapshotDir: '${config.snapshotDir}',
205
+ \tblocks: [
206
+ ${blocksSource}
207
+ \t],
208
+ } as const;
209
+
210
+ export default migrationConfig;
211
+ `, "utf8");
212
+ }
@@ -1,94 +1,4 @@
1
- import type { MigrationBlockConfig, MigrationConfig, MigrationEntry, MigrationProjectPaths, MigrationProjectState, ResolvedMigrationBlockTarget, RuleMetadata } from "./migration-types.js";
2
- /**
3
- * Describes the migration retrofit layout discovered in a project directory.
4
- *
5
- * Multi-block discovery wins when block targets are discovered under
6
- * `src/blocks/<slug>/block.json`.
7
- * Otherwise the runtime falls back to a supported single-block layout.
8
- */
9
- export type DiscoveredMigrationLayout = {
10
- block: MigrationBlockConfig;
11
- mode: "single";
12
- } | {
13
- blocks: MigrationBlockConfig[];
14
- mode: "multi";
15
- };
16
- export declare function ensureAdvancedMigrationProject(projectDir: string, blocks?: MigrationBlockConfig[]): void;
17
- export declare function getProjectPaths(projectDir: string): MigrationProjectPaths;
18
- /**
19
- * Detects the supported migration retrofit layout for `migrate init`.
20
- *
21
- * Multi-block targets under `src/blocks/<slug>` take precedence over
22
- * single-block layouts.
23
- * Returns the detected layout on success and throws an actionable error when no
24
- * supported first-party layout can be inferred.
25
- */
26
- export declare function discoverMigrationInitLayout(projectDir: string): DiscoveredMigrationLayout;
27
- export declare function resolveMigrationBlocks(projectDir: string, config: MigrationConfig): ResolvedMigrationBlockTarget[];
28
- export declare function getSnapshotRoot(projectDir: string, block: MigrationBlockConfig | ResolvedMigrationBlockTarget, version: string): string;
29
- export declare function getSnapshotBlockJsonPath(projectDir: string, block: MigrationBlockConfig | ResolvedMigrationBlockTarget, version: string): string;
30
- export declare function getSnapshotManifestPath(projectDir: string, block: MigrationBlockConfig | ResolvedMigrationBlockTarget, version: string): string;
31
- /**
32
- * Lists the snapshot versions currently present for a specific block target.
33
- *
34
- * Returns the sorted subset of supported migration versions that have a manifest on disk
35
- * for the provided block, or an empty array when none exist.
36
- */
37
- export declare function getAvailableSnapshotVersionsForBlock(projectDir: string, supportedMigrationVersions: string[], block: MigrationBlockConfig | ResolvedMigrationBlockTarget): string[];
38
- /**
39
- * Formats the standard missing-snapshot guidance for a block target.
40
- *
41
- * Returns a user-facing message that either lists the available snapshot
42
- * versions or explains that no snapshots exist yet for the block.
43
- */
44
- export declare function createMissingBlockSnapshotMessage(blockName: string, fromVersion: string, availableSnapshotVersions: string[]): string;
45
- export declare function getSnapshotSavePath(projectDir: string, block: MigrationBlockConfig | ResolvedMigrationBlockTarget, version: string): string;
46
- export declare function getGeneratedDirForBlock(paths: MigrationProjectPaths, block: MigrationBlockConfig | ResolvedMigrationBlockTarget): string;
47
- export declare function getRuleFilePath(paths: MigrationProjectPaths, block: MigrationBlockConfig | ResolvedMigrationBlockTarget, fromVersion: string, toVersion: string): string;
48
- export declare function getFixtureFilePath(paths: MigrationProjectPaths, block: MigrationBlockConfig | ResolvedMigrationBlockTarget, fromVersion: string, toVersion: string): string;
49
- export declare function getValidatorsImportPath(projectDir: string, block: MigrationBlockConfig | ResolvedMigrationBlockTarget, fromDir: string): string;
50
- export declare function ensureMigrationDirectories(projectDir: string, blocks?: MigrationBlockConfig[]): void;
51
- export declare function writeInitialMigrationScaffold(projectDir: string, currentMigrationVersion: string, blocks?: MigrationBlockConfig[]): void;
52
- /**
53
- * Guards a project directory against legacy semver-based migration workspaces.
54
- *
55
- * @param projectDir Absolute or relative project directory containing the migration workspace.
56
- * @returns Nothing.
57
- * @throws Error When legacy config keys or semver-named migration artifacts are detected.
58
- */
59
- export declare function assertNoLegacySemverMigrationWorkspace(projectDir: string): void;
60
- /**
61
- * Loads the migration workspace state for a project directory.
62
- *
63
- * By default this loader may run the project's `sync-types` script when the
64
- * current manifest files are missing, because later migration commands depend
65
- * on those generated artifacts. Pass `allowSyncTypes: false` to keep the call
66
- * read-only and fail instead of mutating the workspace.
67
- *
68
- * When `allowMissingConfig` is enabled and the migration config file does not
69
- * exist yet, the loader synthesizes a minimal legacy-root config so bootstrap
70
- * flows can continue before the first config write.
71
- *
72
- * @param projectDir Absolute or relative project directory containing the migration workspace.
73
- * @param options Loader flags controlling config fallback and `sync-types` side effects.
74
- * @returns The resolved migration project state, including config, block targets, and helper paths.
75
- * @throws Error When the project is not migration-capable, required manifests remain missing, or generated files cannot be read.
76
- */
77
- export declare function loadMigrationProject(projectDir: string, { allowMissingConfig, allowSyncTypes, }?: {
78
- allowMissingConfig?: boolean;
79
- allowSyncTypes?: boolean;
80
- }): MigrationProjectState;
81
- export declare function discoverMigrationEntries(state: MigrationProjectState): MigrationEntry[];
82
- export declare function parseMigrationConfig(source: string): MigrationConfig;
83
- export declare function writeMigrationConfig(projectDir: string, config: MigrationConfig): void;
84
- /**
85
- * Returns the discovered block name for a supported single-block project.
86
- *
87
- * Uses `discoverSingleBlockTarget(projectDir)` internally and throws when the
88
- * project directory does not resolve to a supported single-block migration
89
- * layout.
90
- */
91
- export declare function readProjectBlockName(projectDir: string): string;
92
- export declare function assertRuleHasNoTodos(projectDir: string, block: MigrationBlockConfig | ResolvedMigrationBlockTarget, fromMigrationVersion: string, toMigrationVersion: string): void;
93
- export declare function readRuleMetadata(rulePath: string): RuleMetadata;
94
- export declare function createMigrationBlockConfig(block: MigrationBlockConfig): MigrationBlockConfig;
1
+ export type { DiscoveredMigrationLayout } from "./migration-project-layout.js";
2
+ export { parseMigrationConfig } from "./migration-project-config-source.js";
3
+ export { assertRuleHasNoTodos, createMigrationBlockConfig, createMissingBlockSnapshotMessage, discoverMigrationEntries, discoverMigrationInitLayout, ensureAdvancedMigrationProject, ensureMigrationDirectories, getAvailableSnapshotVersionsForBlock, getFixtureFilePath, getGeneratedDirForBlock, getProjectPaths, getRuleFilePath, getSnapshotBlockJsonPath, getSnapshotManifestPath, getSnapshotRoot, getSnapshotSavePath, getValidatorsImportPath, readProjectBlockName, readRuleMetadata, resolveMigrationBlocks, } from "./migration-project-layout.js";
4
+ export { assertNoLegacySemverMigrationWorkspace, loadMigrationProject, writeInitialMigrationScaffold, writeMigrationConfig, } from "./migration-project-workspace.js";