@wp-typia/project-tools 0.11.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/runtime/cli-add.d.ts +100 -4
- package/dist/runtime/cli-add.js +705 -79
- package/dist/runtime/cli-core.d.ts +7 -2
- package/dist/runtime/cli-core.js +6 -2
- package/dist/runtime/cli-doctor.js +279 -0
- package/dist/runtime/cli-help.js +10 -3
- package/dist/runtime/hooked-blocks.d.ts +17 -0
- package/dist/runtime/hooked-blocks.js +13 -0
- package/dist/runtime/index.d.ts +14 -2
- package/dist/runtime/index.js +13 -1
- package/dist/runtime/migrations.d.ts +5 -5
- package/dist/runtime/migrations.js +45 -0
- package/dist/runtime/package-managers.js +1 -1
- package/dist/runtime/workspace-inventory.d.ts +93 -0
- package/dist/runtime/workspace-inventory.js +306 -0
- package/dist/runtime/workspace-project.d.ts +66 -0
- package/dist/runtime/workspace-project.js +152 -0
- package/package.json +2 -2
- package/templates/_shared/base/package.json.mustache +1 -1
- package/templates/_shared/compound/core/package.json.mustache +1 -1
- package/templates/_shared/compound/persistence/package.json.mustache +1 -1
- package/templates/_shared/persistence/core/package.json.mustache +1 -1
- package/templates/interactivity/package.json.mustache +1 -1
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* `packages/wp-typia`.
|
|
7
7
|
*
|
|
8
8
|
* Import `formatAddHelpText`, `runAddBlockCommand`,
|
|
9
|
-
* `
|
|
9
|
+
* `runAddVariationCommand`, `runAddPatternCommand`,
|
|
10
|
+
* `runAddBindingSourceCommand`, `runAddHookedBlockCommand`,
|
|
11
|
+
* and `HOOKED_BLOCK_POSITION_IDS`,
|
|
12
|
+
* `getWorkspaceBlockSelectOptions`, and `seedWorkspaceMigrationProject` for
|
|
10
13
|
* explicit `wp-typia add` flows,
|
|
11
14
|
* `getDoctorChecks`, `runDoctor`, and `DoctorCheck` for diagnostics,
|
|
12
15
|
* `formatHelpText` for top-level CLI usage output, scaffold helpers such as
|
|
@@ -18,7 +21,9 @@
|
|
|
18
21
|
* template inspection flows.
|
|
19
22
|
*/
|
|
20
23
|
export { getDoctorChecks, runDoctor, type DoctorCheck } from "./cli-doctor.js";
|
|
21
|
-
export {
|
|
24
|
+
export { formatAddHelpText, getWorkspaceBlockSelectOptions, runAddBindingSourceCommand, runAddBlockCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddVariationCommand, seedWorkspaceMigrationProject, } from "./cli-add.js";
|
|
25
|
+
export { HOOKED_BLOCK_POSITION_IDS } from "./hooked-blocks.js";
|
|
26
|
+
export type { HookedBlockPositionId } from "./hooked-blocks.js";
|
|
22
27
|
export { formatHelpText } from "./cli-help.js";
|
|
23
28
|
export { getNextSteps, getOptionalOnboarding, runScaffoldFlow, } from "./cli-scaffold.js";
|
|
24
29
|
export { createReadlinePrompt, type ReadlinePrompt } from "./cli-prompt.js";
|
package/dist/runtime/cli-core.js
CHANGED
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* `packages/wp-typia`.
|
|
7
7
|
*
|
|
8
8
|
* Import `formatAddHelpText`, `runAddBlockCommand`,
|
|
9
|
-
* `
|
|
9
|
+
* `runAddVariationCommand`, `runAddPatternCommand`,
|
|
10
|
+
* `runAddBindingSourceCommand`, `runAddHookedBlockCommand`,
|
|
11
|
+
* and `HOOKED_BLOCK_POSITION_IDS`,
|
|
12
|
+
* `getWorkspaceBlockSelectOptions`, and `seedWorkspaceMigrationProject` for
|
|
10
13
|
* explicit `wp-typia add` flows,
|
|
11
14
|
* `getDoctorChecks`, `runDoctor`, and `DoctorCheck` for diagnostics,
|
|
12
15
|
* `formatHelpText` for top-level CLI usage output, scaffold helpers such as
|
|
@@ -18,7 +21,8 @@
|
|
|
18
21
|
* template inspection flows.
|
|
19
22
|
*/
|
|
20
23
|
export { getDoctorChecks, runDoctor } from "./cli-doctor.js";
|
|
21
|
-
export {
|
|
24
|
+
export { formatAddHelpText, getWorkspaceBlockSelectOptions, runAddBindingSourceCommand, runAddBlockCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddVariationCommand, seedWorkspaceMigrationProject, } from "./cli-add.js";
|
|
25
|
+
export { HOOKED_BLOCK_POSITION_IDS } from "./hooked-blocks.js";
|
|
22
26
|
export { formatHelpText } from "./cli-help.js";
|
|
23
27
|
export { getNextSteps, getOptionalOnboarding, runScaffoldFlow, } from "./cli-scaffold.js";
|
|
24
28
|
export { createReadlinePrompt } from "./cli-prompt.js";
|
|
@@ -4,7 +4,22 @@ import path from "node:path";
|
|
|
4
4
|
import { execFileSync } from "node:child_process";
|
|
5
5
|
import { access, constants as fsConstants, rm, writeFile } from "node:fs/promises";
|
|
6
6
|
import { getBuiltInTemplateLayerDirs } from "./template-builtins.js";
|
|
7
|
+
import { HOOKED_BLOCK_ANCHOR_PATTERN, HOOKED_BLOCK_POSITION_SET, } from "./hooked-blocks.js";
|
|
7
8
|
import { listTemplates } from "./template-registry.js";
|
|
9
|
+
import { readWorkspaceInventory } from "./workspace-inventory.js";
|
|
10
|
+
import { getInvalidWorkspaceProjectReason, parseWorkspacePackageJson, WORKSPACE_TEMPLATE_PACKAGE, tryResolveWorkspaceProject, } from "./workspace-project.js";
|
|
11
|
+
const WORKSPACE_COLLECTION_IMPORT_LINE = "import '../../collection';";
|
|
12
|
+
const WORKSPACE_COLLECTION_IMPORT_PATTERN = /^\s*import\s+["']\.\.\/\.\.\/collection["']\s*;?\s*$/m;
|
|
13
|
+
const WORKSPACE_BINDING_SERVER_GLOB = "/src/bindings/*/server.php";
|
|
14
|
+
const WORKSPACE_BINDING_EDITOR_SCRIPT = "build/bindings/index.js";
|
|
15
|
+
const WORKSPACE_BINDING_EDITOR_ASSET = "build/bindings/index.asset.php";
|
|
16
|
+
const WORKSPACE_GENERATED_BLOCK_ARTIFACTS = [
|
|
17
|
+
"block.json",
|
|
18
|
+
"typia.manifest.json",
|
|
19
|
+
"typia.schema.json",
|
|
20
|
+
"typia-validator.php",
|
|
21
|
+
"typia.openapi.json",
|
|
22
|
+
];
|
|
8
23
|
function readCommandVersion(command, args = ["--version"]) {
|
|
9
24
|
try {
|
|
10
25
|
return execFileSync(command, args, {
|
|
@@ -40,6 +55,194 @@ async function checkTempDirectory() {
|
|
|
40
55
|
return false;
|
|
41
56
|
}
|
|
42
57
|
}
|
|
58
|
+
function createDoctorCheck(label, status, detail) {
|
|
59
|
+
return { detail, label, status };
|
|
60
|
+
}
|
|
61
|
+
function getWorkspaceBootstrapRelativePath(packageName) {
|
|
62
|
+
const packageBaseName = packageName.split("/").pop() ?? packageName;
|
|
63
|
+
return `${packageBaseName}.php`;
|
|
64
|
+
}
|
|
65
|
+
function checkExistingFiles(projectDir, label, filePaths) {
|
|
66
|
+
const missing = filePaths
|
|
67
|
+
.filter((filePath) => typeof filePath === "string")
|
|
68
|
+
.filter((filePath) => !fs.existsSync(path.join(projectDir, filePath)));
|
|
69
|
+
return createDoctorCheck(label, missing.length === 0 ? "pass" : "fail", missing.length === 0 ? "All referenced files exist" : `Missing: ${missing.join(", ")}`);
|
|
70
|
+
}
|
|
71
|
+
function checkWorkspacePackageMetadata(workspace, packageJson) {
|
|
72
|
+
const issues = [];
|
|
73
|
+
const packageName = packageJson.name;
|
|
74
|
+
const bootstrapRelativePath = getWorkspaceBootstrapRelativePath(typeof packageName === "string" && packageName.length > 0 ? packageName : workspace.packageName);
|
|
75
|
+
const wpTypia = packageJson.wpTypia;
|
|
76
|
+
if (typeof packageName !== "string" || packageName.length === 0) {
|
|
77
|
+
issues.push("package.json must define a string name for workspace bootstrap resolution");
|
|
78
|
+
}
|
|
79
|
+
if (wpTypia?.projectType !== "workspace") {
|
|
80
|
+
issues.push('wpTypia.projectType must be "workspace"');
|
|
81
|
+
}
|
|
82
|
+
if (wpTypia?.templatePackage !== WORKSPACE_TEMPLATE_PACKAGE) {
|
|
83
|
+
issues.push(`wpTypia.templatePackage must be "${WORKSPACE_TEMPLATE_PACKAGE}"`);
|
|
84
|
+
}
|
|
85
|
+
if (wpTypia?.namespace !== workspace.workspace.namespace) {
|
|
86
|
+
issues.push(`wpTypia.namespace must equal "${workspace.workspace.namespace}"`);
|
|
87
|
+
}
|
|
88
|
+
if (wpTypia?.textDomain !== workspace.workspace.textDomain) {
|
|
89
|
+
issues.push(`wpTypia.textDomain must equal "${workspace.workspace.textDomain}"`);
|
|
90
|
+
}
|
|
91
|
+
if (wpTypia?.phpPrefix !== workspace.workspace.phpPrefix) {
|
|
92
|
+
issues.push(`wpTypia.phpPrefix must equal "${workspace.workspace.phpPrefix}"`);
|
|
93
|
+
}
|
|
94
|
+
if (!fs.existsSync(path.join(workspace.projectDir, bootstrapRelativePath))) {
|
|
95
|
+
issues.push(`Missing bootstrap file ${bootstrapRelativePath}`);
|
|
96
|
+
}
|
|
97
|
+
return createDoctorCheck("Workspace package metadata", issues.length === 0 ? "pass" : "fail", issues.length === 0
|
|
98
|
+
? `package.json metadata aligns with ${workspace.packageName} and ${bootstrapRelativePath}`
|
|
99
|
+
: issues.join("; "));
|
|
100
|
+
}
|
|
101
|
+
function getWorkspaceBlockRequiredFiles(block) {
|
|
102
|
+
const blockDir = path.join("src", "blocks", block.slug);
|
|
103
|
+
return Array.from(new Set([
|
|
104
|
+
block.typesFile,
|
|
105
|
+
block.apiTypesFile,
|
|
106
|
+
block.openApiFile,
|
|
107
|
+
path.join(blockDir, "index.tsx"),
|
|
108
|
+
...WORKSPACE_GENERATED_BLOCK_ARTIFACTS.map((fileName) => path.join(blockDir, fileName)),
|
|
109
|
+
].filter((filePath) => typeof filePath === "string")));
|
|
110
|
+
}
|
|
111
|
+
function checkWorkspaceBlockMetadata(projectDir, workspace, block) {
|
|
112
|
+
const blockJsonRelativePath = path.join("src", "blocks", block.slug, "block.json");
|
|
113
|
+
const blockJsonPath = path.join(projectDir, blockJsonRelativePath);
|
|
114
|
+
if (!fs.existsSync(blockJsonPath)) {
|
|
115
|
+
return createDoctorCheck(`Block metadata ${block.slug}`, "fail", `Missing ${blockJsonRelativePath}`);
|
|
116
|
+
}
|
|
117
|
+
let blockJson;
|
|
118
|
+
try {
|
|
119
|
+
blockJson = JSON.parse(fs.readFileSync(blockJsonPath, "utf8"));
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
return createDoctorCheck(`Block metadata ${block.slug}`, "fail", error instanceof Error ? error.message : String(error));
|
|
123
|
+
}
|
|
124
|
+
const expectedName = `${workspace.workspace.namespace}/${block.slug}`;
|
|
125
|
+
const issues = [];
|
|
126
|
+
if (blockJson.name !== expectedName) {
|
|
127
|
+
issues.push(`block.json name must equal "${expectedName}"`);
|
|
128
|
+
}
|
|
129
|
+
if (blockJson.textdomain !== workspace.workspace.textDomain) {
|
|
130
|
+
issues.push(`block.json textdomain must equal "${workspace.workspace.textDomain}"`);
|
|
131
|
+
}
|
|
132
|
+
return createDoctorCheck(`Block metadata ${block.slug}`, issues.length === 0 ? "pass" : "fail", issues.length === 0
|
|
133
|
+
? `block.json matches ${expectedName} and ${workspace.workspace.textDomain}`
|
|
134
|
+
: issues.join("; "));
|
|
135
|
+
}
|
|
136
|
+
function checkWorkspaceBlockHooks(projectDir, blockSlug) {
|
|
137
|
+
const blockJsonRelativePath = path.join("src", "blocks", blockSlug, "block.json");
|
|
138
|
+
const blockJsonPath = path.join(projectDir, blockJsonRelativePath);
|
|
139
|
+
if (!fs.existsSync(blockJsonPath)) {
|
|
140
|
+
return createDoctorCheck(`Block hooks ${blockSlug}`, "fail", `Missing ${blockJsonRelativePath}`);
|
|
141
|
+
}
|
|
142
|
+
let blockJson;
|
|
143
|
+
try {
|
|
144
|
+
blockJson = JSON.parse(fs.readFileSync(blockJsonPath, "utf8"));
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
return createDoctorCheck(`Block hooks ${blockSlug}`, "fail", error instanceof Error ? error.message : String(error));
|
|
148
|
+
}
|
|
149
|
+
const blockHooks = blockJson.blockHooks;
|
|
150
|
+
if (blockHooks === undefined) {
|
|
151
|
+
return createDoctorCheck(`Block hooks ${blockSlug}`, "pass", "No blockHooks metadata configured");
|
|
152
|
+
}
|
|
153
|
+
if (!blockHooks || typeof blockHooks !== "object" || Array.isArray(blockHooks)) {
|
|
154
|
+
return createDoctorCheck(`Block hooks ${blockSlug}`, "fail", `${blockJsonRelativePath} must define blockHooks as an object when present.`);
|
|
155
|
+
}
|
|
156
|
+
const blockName = typeof blockJson.name === "string" && blockJson.name.trim().length > 0
|
|
157
|
+
? blockJson.name.trim()
|
|
158
|
+
: null;
|
|
159
|
+
const invalidEntries = Object.entries(blockHooks).filter(([anchor, position]) => (blockName !== null && anchor.trim() === blockName) ||
|
|
160
|
+
anchor.trim().length === 0 ||
|
|
161
|
+
anchor !== anchor.trim() ||
|
|
162
|
+
!HOOKED_BLOCK_ANCHOR_PATTERN.test(anchor) ||
|
|
163
|
+
typeof position !== "string" ||
|
|
164
|
+
!HOOKED_BLOCK_POSITION_SET.has(position));
|
|
165
|
+
return createDoctorCheck(`Block hooks ${blockSlug}`, invalidEntries.length === 0 ? "pass" : "fail", invalidEntries.length === 0
|
|
166
|
+
? `blockHooks metadata is valid${Object.keys(blockHooks).length > 0 ? ` (${Object.keys(blockHooks).join(", ")})` : ""}`
|
|
167
|
+
: `Invalid blockHooks entries: ${invalidEntries
|
|
168
|
+
.map(([anchor, position]) => `${anchor || "<empty>"} => ${String(position)}`)
|
|
169
|
+
.join(", ")}`);
|
|
170
|
+
}
|
|
171
|
+
function checkWorkspaceBlockCollectionImport(projectDir, blockSlug) {
|
|
172
|
+
const entryRelativePath = path.join("src", "blocks", blockSlug, "index.tsx");
|
|
173
|
+
const entryPath = path.join(projectDir, entryRelativePath);
|
|
174
|
+
if (!fs.existsSync(entryPath)) {
|
|
175
|
+
return createDoctorCheck(`Block collection ${blockSlug}`, "fail", `Missing ${entryRelativePath}`);
|
|
176
|
+
}
|
|
177
|
+
const source = fs.readFileSync(entryPath, "utf8");
|
|
178
|
+
const hasCollectionImport = WORKSPACE_COLLECTION_IMPORT_PATTERN.test(source);
|
|
179
|
+
return createDoctorCheck(`Block collection ${blockSlug}`, hasCollectionImport ? "pass" : "fail", hasCollectionImport
|
|
180
|
+
? "Shared block collection import is present"
|
|
181
|
+
: `Missing a shared collection import like ${WORKSPACE_COLLECTION_IMPORT_LINE}`);
|
|
182
|
+
}
|
|
183
|
+
function checkWorkspacePatternBootstrap(projectDir, packageName) {
|
|
184
|
+
const packageBaseName = packageName.split("/").pop() ?? packageName;
|
|
185
|
+
const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
|
|
186
|
+
if (!fs.existsSync(bootstrapPath)) {
|
|
187
|
+
return createDoctorCheck("Pattern bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
|
|
188
|
+
}
|
|
189
|
+
const source = fs.readFileSync(bootstrapPath, "utf8");
|
|
190
|
+
const hasCategoryAnchor = source.includes("register_block_pattern_category");
|
|
191
|
+
const hasPatternGlob = source.includes("/src/patterns/*.php");
|
|
192
|
+
return createDoctorCheck("Pattern bootstrap", hasCategoryAnchor && hasPatternGlob ? "pass" : "fail", hasCategoryAnchor && hasPatternGlob
|
|
193
|
+
? "Pattern category and loader hooks are present"
|
|
194
|
+
: "Missing pattern category registration or src/patterns loader hook");
|
|
195
|
+
}
|
|
196
|
+
function checkWorkspaceBindingBootstrap(projectDir, packageName) {
|
|
197
|
+
const packageBaseName = packageName.split("/").pop() ?? packageName;
|
|
198
|
+
const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
|
|
199
|
+
if (!fs.existsSync(bootstrapPath)) {
|
|
200
|
+
return createDoctorCheck("Binding bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
|
|
201
|
+
}
|
|
202
|
+
const source = fs.readFileSync(bootstrapPath, "utf8");
|
|
203
|
+
const hasServerGlob = source.includes(WORKSPACE_BINDING_SERVER_GLOB);
|
|
204
|
+
const hasEditorEnqueueHook = source.includes("enqueue_block_editor_assets");
|
|
205
|
+
const hasEditorScript = source.includes(WORKSPACE_BINDING_EDITOR_SCRIPT);
|
|
206
|
+
const hasEditorAsset = source.includes(WORKSPACE_BINDING_EDITOR_ASSET);
|
|
207
|
+
return createDoctorCheck("Binding bootstrap", hasServerGlob && hasEditorEnqueueHook && hasEditorScript && hasEditorAsset ? "pass" : "fail", hasServerGlob && hasEditorEnqueueHook && hasEditorScript && hasEditorAsset
|
|
208
|
+
? "Binding source PHP and editor bootstrap hooks are present"
|
|
209
|
+
: "Missing binding source PHP require glob or editor enqueue hook");
|
|
210
|
+
}
|
|
211
|
+
function checkWorkspaceBindingSourcesIndex(projectDir, bindingSources) {
|
|
212
|
+
const indexRelativePath = [path.join("src", "bindings", "index.ts"), path.join("src", "bindings", "index.js")].find((relativePath) => fs.existsSync(path.join(projectDir, relativePath)));
|
|
213
|
+
if (!indexRelativePath) {
|
|
214
|
+
return createDoctorCheck("Binding sources index", "fail", "Missing src/bindings/index.ts or src/bindings/index.js");
|
|
215
|
+
}
|
|
216
|
+
const indexPath = path.join(projectDir, indexRelativePath);
|
|
217
|
+
const source = fs.readFileSync(indexPath, "utf8");
|
|
218
|
+
const missingImports = bindingSources.filter((bindingSource) => !source.includes(`./${bindingSource.slug}/editor`));
|
|
219
|
+
return createDoctorCheck("Binding sources index", missingImports.length === 0 ? "pass" : "fail", missingImports.length === 0
|
|
220
|
+
? "Binding source editor registrations are aggregated"
|
|
221
|
+
: `Missing editor imports for: ${missingImports.map((entry) => entry.slug).join(", ")}`);
|
|
222
|
+
}
|
|
223
|
+
function checkVariationEntrypoint(projectDir, blockSlug) {
|
|
224
|
+
const entryPath = path.join(projectDir, "src", "blocks", blockSlug, "index.tsx");
|
|
225
|
+
if (!fs.existsSync(entryPath)) {
|
|
226
|
+
return createDoctorCheck(`Variation entrypoint ${blockSlug}`, "fail", `Missing ${path.relative(projectDir, entryPath)}`);
|
|
227
|
+
}
|
|
228
|
+
const source = fs.readFileSync(entryPath, "utf8");
|
|
229
|
+
const hasImport = source.includes("./variations");
|
|
230
|
+
const hasCall = source.includes("registerWorkspaceVariations()");
|
|
231
|
+
return createDoctorCheck(`Variation entrypoint ${blockSlug}`, hasImport && hasCall ? "pass" : "fail", hasImport && hasCall
|
|
232
|
+
? "Variations registration hook is present"
|
|
233
|
+
: "Missing ./variations import or registerWorkspaceVariations() call");
|
|
234
|
+
}
|
|
235
|
+
function checkMigrationWorkspaceHint(workspace, packageJson) {
|
|
236
|
+
const hasMigrationScript = typeof packageJson.scripts?.["migration:doctor"] === "string";
|
|
237
|
+
const migrationConfigRelativePath = path.join("src", "migrations", "config.ts");
|
|
238
|
+
const hasMigrationConfig = fs.existsSync(path.join(workspace.projectDir, migrationConfigRelativePath));
|
|
239
|
+
if (!hasMigrationScript && !hasMigrationConfig) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
return createDoctorCheck("Migration workspace", hasMigrationConfig ? "pass" : "fail", hasMigrationConfig
|
|
243
|
+
? "Run `wp-typia migrate doctor --all` for migration target, snapshot, fixture, and generated artifact checks"
|
|
244
|
+
: `Missing ${migrationConfigRelativePath} for the configured migration workspace`);
|
|
245
|
+
}
|
|
43
246
|
/**
|
|
44
247
|
* Collect all runtime doctor checks for the current environment.
|
|
45
248
|
*
|
|
@@ -109,6 +312,82 @@ export async function getDoctorChecks(cwd) {
|
|
|
109
312
|
detail: hasAssets ? layerDirs.join(" + ") : "Missing core template assets",
|
|
110
313
|
});
|
|
111
314
|
}
|
|
315
|
+
let workspace = null;
|
|
316
|
+
let invalidWorkspaceReason = null;
|
|
317
|
+
try {
|
|
318
|
+
invalidWorkspaceReason = getInvalidWorkspaceProjectReason(cwd);
|
|
319
|
+
workspace = tryResolveWorkspaceProject(cwd);
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
checks.push(createDoctorCheck("Workspace package metadata", "fail", error instanceof Error ? error.message : String(error)));
|
|
323
|
+
return checks;
|
|
324
|
+
}
|
|
325
|
+
if (!workspace) {
|
|
326
|
+
if (invalidWorkspaceReason) {
|
|
327
|
+
checks.push(createDoctorCheck("Workspace package metadata", "fail", invalidWorkspaceReason));
|
|
328
|
+
}
|
|
329
|
+
return checks;
|
|
330
|
+
}
|
|
331
|
+
checks.push(createDoctorCheck("Workspace marker", "pass", `Official workspace detected for ${workspace.workspace.namespace}`));
|
|
332
|
+
let workspacePackageJson;
|
|
333
|
+
try {
|
|
334
|
+
workspacePackageJson = parseWorkspacePackageJson(workspace.projectDir);
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
checks.push(createDoctorCheck("Workspace package metadata", "fail", error instanceof Error ? error.message : String(error)));
|
|
338
|
+
return checks;
|
|
339
|
+
}
|
|
340
|
+
checks.push(checkWorkspacePackageMetadata(workspace, workspacePackageJson));
|
|
341
|
+
try {
|
|
342
|
+
const inventory = readWorkspaceInventory(workspace.projectDir);
|
|
343
|
+
checks.push(createDoctorCheck("Workspace inventory", "pass", `${inventory.blocks.length} block(s), ${inventory.variations.length} variation(s), ${inventory.patterns.length} pattern(s), ${inventory.bindingSources.length} binding source(s)`));
|
|
344
|
+
for (const block of inventory.blocks) {
|
|
345
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Block ${block.slug}`, [
|
|
346
|
+
...getWorkspaceBlockRequiredFiles(block),
|
|
347
|
+
]));
|
|
348
|
+
checks.push(checkWorkspaceBlockMetadata(workspace.projectDir, workspace, block));
|
|
349
|
+
checks.push(checkWorkspaceBlockHooks(workspace.projectDir, block.slug));
|
|
350
|
+
checks.push(checkWorkspaceBlockCollectionImport(workspace.projectDir, block.slug));
|
|
351
|
+
}
|
|
352
|
+
const registeredBlockSlugs = new Set(inventory.blocks.map((block) => block.slug));
|
|
353
|
+
const variationTargetBlocks = new Set();
|
|
354
|
+
for (const variation of inventory.variations) {
|
|
355
|
+
if (!registeredBlockSlugs.has(variation.block)) {
|
|
356
|
+
checks.push(createDoctorCheck(`Variation ${variation.block}/${variation.slug}`, "fail", `Variation references unknown block "${variation.block}"`));
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
variationTargetBlocks.add(variation.block);
|
|
360
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Variation ${variation.block}/${variation.slug}`, [variation.file]));
|
|
361
|
+
}
|
|
362
|
+
for (const blockSlug of variationTargetBlocks) {
|
|
363
|
+
checks.push(checkVariationEntrypoint(workspace.projectDir, blockSlug));
|
|
364
|
+
}
|
|
365
|
+
const shouldCheckPatternBootstrap = inventory.patterns.length > 0 ||
|
|
366
|
+
fs.existsSync(path.join(workspace.projectDir, "src", "patterns"));
|
|
367
|
+
if (shouldCheckPatternBootstrap) {
|
|
368
|
+
checks.push(checkWorkspacePatternBootstrap(workspace.projectDir, workspace.packageName));
|
|
369
|
+
}
|
|
370
|
+
for (const pattern of inventory.patterns) {
|
|
371
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Pattern ${pattern.slug}`, [pattern.file]));
|
|
372
|
+
}
|
|
373
|
+
if (inventory.bindingSources.length > 0) {
|
|
374
|
+
checks.push(checkWorkspaceBindingBootstrap(workspace.projectDir, workspace.packageName));
|
|
375
|
+
checks.push(checkWorkspaceBindingSourcesIndex(workspace.projectDir, inventory.bindingSources));
|
|
376
|
+
}
|
|
377
|
+
for (const bindingSource of inventory.bindingSources) {
|
|
378
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Binding source ${bindingSource.slug}`, [
|
|
379
|
+
bindingSource.serverFile,
|
|
380
|
+
bindingSource.editorFile,
|
|
381
|
+
]));
|
|
382
|
+
}
|
|
383
|
+
const migrationWorkspaceCheck = checkMigrationWorkspaceHint(workspace, workspacePackageJson);
|
|
384
|
+
if (migrationWorkspaceCheck) {
|
|
385
|
+
checks.push(migrationWorkspaceCheck);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
checks.push(createDoctorCheck("Workspace inventory", "fail", error instanceof Error ? error.message : String(error)));
|
|
390
|
+
}
|
|
112
391
|
return checks;
|
|
113
392
|
}
|
|
114
393
|
/**
|
package/dist/runtime/cli-help.js
CHANGED
|
@@ -17,8 +17,10 @@ export function formatHelpText() {
|
|
|
17
17
|
wp-typia create <project-dir> [--template compound] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--no-install] [--package-manager <id>]
|
|
18
18
|
wp-typia <project-dir> [create flags...]
|
|
19
19
|
wp-typia add block <name> --template <basic|interactivity|persistence|compound> [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>]
|
|
20
|
-
wp-typia add variation
|
|
21
|
-
wp-typia add pattern
|
|
20
|
+
wp-typia add variation <name> --block <block-slug>
|
|
21
|
+
wp-typia add pattern <name>
|
|
22
|
+
wp-typia add binding-source <name>
|
|
23
|
+
wp-typia add hooked-block <block-slug> --anchor <anchor-block-name> --position <before|after|firstChild|lastChild>
|
|
22
24
|
wp-typia migrate <init|snapshot|diff|scaffold|verify|doctor|fixtures|fuzz> [...]
|
|
23
25
|
wp-typia templates list
|
|
24
26
|
wp-typia templates inspect <id>
|
|
@@ -32,6 +34,11 @@ Package managers: ${PACKAGE_MANAGER_IDS.join(", ")}
|
|
|
32
34
|
Notes:
|
|
33
35
|
\`wp-typia create\` is the canonical scaffold command.
|
|
34
36
|
\`wp-typia <project-dir>\` remains a backward-compatible alias to \`create\`.
|
|
35
|
-
\`add variation\`
|
|
37
|
+
\`add variation\` uses an existing workspace block from \`scripts/block-config.ts\`.
|
|
38
|
+
\`add pattern\` scaffolds a namespaced PHP pattern shell under \`src/patterns/\`.
|
|
39
|
+
\`add binding-source\` scaffolds shared PHP and editor registration under \`src/bindings/\`.
|
|
40
|
+
\`add hooked-block\` patches an existing workspace block's \`block.json\` \`blockHooks\` metadata.
|
|
41
|
+
\`wp-typia doctor\` checks environment readiness plus workspace inventory and source-tree drift.
|
|
42
|
+
\`wp-typia migrate doctor --all\` checks migration target alignment, snapshots, fixtures, and generated migration artifacts.
|
|
36
43
|
\`migrate\` is the canonical migration command; \`migrations\` is no longer supported.`;
|
|
37
44
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared hooked-block metadata primitives used by add flows, doctor checks,
|
|
3
|
+
* and interactive prompts.
|
|
4
|
+
*/
|
|
5
|
+
export declare const HOOKED_BLOCK_POSITION_IDS: readonly ["before", "after", "firstChild", "lastChild"];
|
|
6
|
+
/**
|
|
7
|
+
* Union of valid `blockHooks` positions accepted by wp-typia workspace flows.
|
|
8
|
+
*/
|
|
9
|
+
export type HookedBlockPositionId = (typeof HOOKED_BLOCK_POSITION_IDS)[number];
|
|
10
|
+
/**
|
|
11
|
+
* Fast lookup set for validating hooked-block positions across runtime surfaces.
|
|
12
|
+
*/
|
|
13
|
+
export declare const HOOKED_BLOCK_POSITION_SET: Set<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Canonical `namespace/slug` block name format required for hooked-block anchors.
|
|
16
|
+
*/
|
|
17
|
+
export declare const HOOKED_BLOCK_ANCHOR_PATTERN: RegExp;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared hooked-block metadata primitives used by add flows, doctor checks,
|
|
3
|
+
* and interactive prompts.
|
|
4
|
+
*/
|
|
5
|
+
export const HOOKED_BLOCK_POSITION_IDS = ["before", "after", "firstChild", "lastChild"];
|
|
6
|
+
/**
|
|
7
|
+
* Fast lookup set for validating hooked-block positions across runtime surfaces.
|
|
8
|
+
*/
|
|
9
|
+
export const HOOKED_BLOCK_POSITION_SET = new Set(HOOKED_BLOCK_POSITION_IDS);
|
|
10
|
+
/**
|
|
11
|
+
* Canonical `namespace/slug` block name format required for hooked-block anchors.
|
|
12
|
+
*/
|
|
13
|
+
export const HOOKED_BLOCK_ANCHOR_PATTERN = /^[a-z0-9-]+\/[a-z0-9-]+$/;
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public runtime surface for wp-typia project tools.
|
|
3
|
+
*
|
|
4
|
+
* This barrel exposes the stable orchestration APIs that power the `wp-typia`
|
|
5
|
+
* CLI while keeping reusable project logic out of the CLI package itself.
|
|
6
|
+
* Consumers should prefer these exports for scaffold, add, migrate, doctor,
|
|
7
|
+
* and workspace-aware helpers such as `getWorkspaceBlockSelectOptions`,
|
|
8
|
+
* `runAddBlockCommand`, `runAddVariationCommand`, `runAddPatternCommand`,
|
|
9
|
+
* `runAddBindingSourceCommand`, `runAddHookedBlockCommand`,
|
|
10
|
+
* `HOOKED_BLOCK_POSITION_IDS`, and `runDoctor`.
|
|
11
|
+
*/
|
|
1
12
|
export { scaffoldProject, collectScaffoldAnswers, getDefaultAnswers, getTemplateVariables, resolvePackageManagerId, resolveTemplateId, } from "./scaffold.js";
|
|
2
13
|
export { formatMigrationHelpText, parseMigrationArgs, runMigrationCommand, } from "./migrations.js";
|
|
14
|
+
export { parseWorkspacePackageManagerId, resolveWorkspaceProject, tryResolveWorkspaceProject, } from "./workspace-project.js";
|
|
3
15
|
export { manifestAttributeToJsonSchema, projectJsonSchemaDocument, manifestToJsonSchema, manifestToOpenApi, normalizeEndpointAuthDefinition, } from "./schema-core.js";
|
|
4
16
|
export { buildCompoundChildStarterManifestDocument, getStarterManifestFiles, stringifyStarterManifest, } from "./starter-manifests.js";
|
|
5
17
|
export type { EndpointAuthIntent, EndpointOpenApiAuthMode, EndpointOpenApiContractDocument, EndpointOpenApiDocumentOptions, EndpointOpenApiEndpointDefinition, EndpointOpenApiMethod, EndpointWordPressAuthDefinition, EndpointWordPressAuthMechanism, JsonSchemaDocument, JsonSchemaProjectionProfile, JsonSchemaObject, NormalizedEndpointAuthDefinition, OpenApiDocument, OpenApiInfo, OpenApiOperation, OpenApiParameter, OpenApiPathItem, OpenApiSecurityScheme, } from "./schema-core.js";
|
|
6
18
|
export { PACKAGE_MANAGER_IDS, PACKAGE_MANAGERS, formatPackageExecCommand, formatInstallCommand, formatRunScript, getPackageManager, getPackageManagerSelectOptions, transformPackageManagerText, } from "./package-managers.js";
|
|
7
19
|
export { TEMPLATE_IDS, TEMPLATE_REGISTRY, getTemplateById, getTemplateSelectOptions, listTemplates, } from "./template-registry.js";
|
|
8
|
-
export {
|
|
9
|
-
export type { DoctorCheck, ReadlinePrompt } from "./cli-core.js";
|
|
20
|
+
export { createReadlinePrompt, formatAddHelpText, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, HOOKED_BLOCK_POSITION_IDS, runAddBindingSourceCommand, runAddBlockCommand, runAddHookedBlockCommand, runAddPatternCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
|
|
21
|
+
export type { DoctorCheck, HookedBlockPositionId, ReadlinePrompt } from "./cli-core.js";
|
package/dist/runtime/index.js
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public runtime surface for wp-typia project tools.
|
|
3
|
+
*
|
|
4
|
+
* This barrel exposes the stable orchestration APIs that power the `wp-typia`
|
|
5
|
+
* CLI while keeping reusable project logic out of the CLI package itself.
|
|
6
|
+
* Consumers should prefer these exports for scaffold, add, migrate, doctor,
|
|
7
|
+
* and workspace-aware helpers such as `getWorkspaceBlockSelectOptions`,
|
|
8
|
+
* `runAddBlockCommand`, `runAddVariationCommand`, `runAddPatternCommand`,
|
|
9
|
+
* `runAddBindingSourceCommand`, `runAddHookedBlockCommand`,
|
|
10
|
+
* `HOOKED_BLOCK_POSITION_IDS`, and `runDoctor`.
|
|
11
|
+
*/
|
|
1
12
|
export { scaffoldProject, collectScaffoldAnswers, getDefaultAnswers, getTemplateVariables, resolvePackageManagerId, resolveTemplateId, } from "./scaffold.js";
|
|
2
13
|
export { formatMigrationHelpText, parseMigrationArgs, runMigrationCommand, } from "./migrations.js";
|
|
14
|
+
export { parseWorkspacePackageManagerId, resolveWorkspaceProject, tryResolveWorkspaceProject, } from "./workspace-project.js";
|
|
3
15
|
export { manifestAttributeToJsonSchema, projectJsonSchemaDocument, manifestToJsonSchema, manifestToOpenApi, normalizeEndpointAuthDefinition, } from "./schema-core.js";
|
|
4
16
|
export { buildCompoundChildStarterManifestDocument, getStarterManifestFiles, stringifyStarterManifest, } from "./starter-manifests.js";
|
|
5
17
|
export { PACKAGE_MANAGER_IDS, PACKAGE_MANAGERS, formatPackageExecCommand, formatInstallCommand, formatRunScript, getPackageManager, getPackageManagerSelectOptions, transformPackageManagerText, } from "./package-managers.js";
|
|
6
18
|
export { TEMPLATE_IDS, TEMPLATE_REGISTRY, getTemplateById, getTemplateSelectOptions, listTemplates, } from "./template-registry.js";
|
|
7
|
-
export {
|
|
19
|
+
export { createReadlinePrompt, formatAddHelpText, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, HOOKED_BLOCK_POSITION_IDS, runAddBindingSourceCommand, runAddBlockCommand, runAddHookedBlockCommand, runAddPatternCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createMigrationDiff } from "./migration-diff.js";
|
|
2
2
|
import { formatDiffReport } from "./migration-render.js";
|
|
3
3
|
import { createMigrationRiskSummary } from "./migration-risk.js";
|
|
4
|
-
import type { MigrationBlockConfig, ParsedMigrationArgs, RenderLine } from "./migration-types.js";
|
|
4
|
+
import type { MigrationBlockConfig, MigrationProjectState, ParsedMigrationArgs, RenderLine } from "./migration-types.js";
|
|
5
5
|
import type { ReadlinePrompt } from "./cli-prompt.js";
|
|
6
6
|
type CommandRenderOptions = {
|
|
7
7
|
prompt?: ReadlinePrompt;
|
|
@@ -78,7 +78,7 @@ export { formatDiffReport };
|
|
|
78
78
|
* @param options Optional prompt/render hooks for testable and interactive execution.
|
|
79
79
|
* @returns The command result, or a promise when the selected command is interactive.
|
|
80
80
|
*/
|
|
81
|
-
export declare function runMigrationCommand(command: ParsedMigrationArgs, cwd: string, { prompt, renderLine }?: CommandRenderOptions):
|
|
81
|
+
export declare function runMigrationCommand(command: ParsedMigrationArgs, cwd: string, { prompt, renderLine }?: CommandRenderOptions): MigrationProjectState | import("./migration-types.js").MigrationDiff | MigrationPlanSummary | Promise<MigrationPlanSummary | {
|
|
82
82
|
cancelled: true;
|
|
83
83
|
}> | {
|
|
84
84
|
block: import("./migration-types.js").ResolvedMigrationBlockTarget;
|
|
@@ -139,7 +139,7 @@ export declare function wizardProjectMigrations(projectDir: string, { isInteract
|
|
|
139
139
|
* @returns The loaded migration project state after the config, snapshots, and generated files are written.
|
|
140
140
|
* @throws Error When the project layout is unsupported or the migration version label is invalid.
|
|
141
141
|
*/
|
|
142
|
-
export declare function initProjectMigrations(projectDir: string, currentMigrationVersion: string, { renderLine }?: CommandRenderOptions):
|
|
142
|
+
export declare function initProjectMigrations(projectDir: string, currentMigrationVersion: string, { renderLine }?: CommandRenderOptions): MigrationProjectState;
|
|
143
143
|
/**
|
|
144
144
|
* Captures the current project state as a named migration snapshot and refreshes generated artifacts.
|
|
145
145
|
*
|
|
@@ -152,7 +152,7 @@ export declare function initProjectMigrations(projectDir: string, currentMigrati
|
|
|
152
152
|
export declare function snapshotProjectVersion(projectDir: string, migrationVersion: string, { renderLine, skipConfigUpdate, skipSyncTypes, }?: CommandRenderOptions & {
|
|
153
153
|
skipConfigUpdate?: boolean;
|
|
154
154
|
skipSyncTypes?: boolean;
|
|
155
|
-
}):
|
|
155
|
+
}): MigrationProjectState;
|
|
156
156
|
/**
|
|
157
157
|
* Computes and renders migration diffs for a selected legacy-to-target edge.
|
|
158
158
|
*
|
|
@@ -246,4 +246,4 @@ export declare function fuzzProjectMigrations(projectDir: string, { all, fromMig
|
|
|
246
246
|
* @param options Console rendering options for initialization output.
|
|
247
247
|
* @returns The loaded migration project state after initialization completes.
|
|
248
248
|
*/
|
|
249
|
-
export declare function seedProjectMigrations(projectDir: string, currentMigrationVersion: string, blocks: MigrationBlockConfig[], { renderLine }?: CommandRenderOptions):
|
|
249
|
+
export declare function seedProjectMigrations(projectDir: string, currentMigrationVersion: string, blocks: MigrationBlockConfig[], { renderLine }?: CommandRenderOptions): MigrationProjectState;
|
|
@@ -11,6 +11,8 @@ import { assertRuleHasNoTodos, assertNoLegacySemverMigrationWorkspace, discoverM
|
|
|
11
11
|
import { formatDiffReport, renderFuzzFile, renderGeneratedDeprecatedFile, renderGeneratedMigrationIndexFile, renderMigrationRegistryFile, renderMigrationRuleFile, renderPhpMigrationRegistryFile, renderVerifyFile, } from "./migration-render.js";
|
|
12
12
|
import { createMigrationRiskSummary, formatMigrationRiskSummary } from "./migration-risk.js";
|
|
13
13
|
import { assertMigrationVersionLabel, compareMigrationVersionLabels, copyFile, detectPackageManagerId, formatLegacyMigrationWorkspaceResetGuidance, getLocalTsxBinary, isInteractiveTerminal, readJson, resolveTargetMigrationVersion, runProjectScriptIfPresent, sanitizeSaveSnapshotSource, sanitizeSnapshotBlockJson, } from "./migration-utils.js";
|
|
14
|
+
import { readWorkspaceInventory } from "./workspace-inventory.js";
|
|
15
|
+
import { getInvalidWorkspaceProjectReason, tryResolveWorkspaceProject } from "./workspace-project.js";
|
|
14
16
|
/**
|
|
15
17
|
* Returns the formatted help text for migration CLI commands and flags.
|
|
16
18
|
*
|
|
@@ -561,6 +563,48 @@ export function verifyProjectMigrations(projectDir, { all = false, fromMigration
|
|
|
561
563
|
}
|
|
562
564
|
return { verifiedVersions: targetVersions };
|
|
563
565
|
}
|
|
566
|
+
function recordWorkspaceMigrationTargetAlignment(projectDir, state, recordCheck) {
|
|
567
|
+
let invalidWorkspaceReason = null;
|
|
568
|
+
let workspace;
|
|
569
|
+
try {
|
|
570
|
+
invalidWorkspaceReason = getInvalidWorkspaceProjectReason(projectDir);
|
|
571
|
+
workspace = tryResolveWorkspaceProject(projectDir);
|
|
572
|
+
}
|
|
573
|
+
catch (error) {
|
|
574
|
+
recordCheck("fail", "Workspace migration targets", error instanceof Error ? error.message : String(error));
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
if (!workspace) {
|
|
578
|
+
if (invalidWorkspaceReason) {
|
|
579
|
+
recordCheck("fail", "Workspace migration targets", invalidWorkspaceReason);
|
|
580
|
+
}
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
try {
|
|
584
|
+
const inventory = readWorkspaceInventory(workspace.projectDir);
|
|
585
|
+
const expectedTargets = inventory.blocks.map((block) => `${workspace.workspace.namespace}/${block.slug}`);
|
|
586
|
+
const configuredTargets = state.blocks.map((block) => block.blockName);
|
|
587
|
+
const expectedTargetSet = new Set(expectedTargets);
|
|
588
|
+
const configuredTargetSet = new Set(configuredTargets);
|
|
589
|
+
const missingTargets = expectedTargets.filter((target) => !configuredTargetSet.has(target));
|
|
590
|
+
const staleTargets = configuredTargets.filter((target) => !expectedTargetSet.has(target));
|
|
591
|
+
recordCheck(missingTargets.length === 0 && staleTargets.length === 0 ? "pass" : "fail", "Workspace migration targets", missingTargets.length === 0 && staleTargets.length === 0
|
|
592
|
+
? `${expectedTargets.length} workspace block target(s) align with migration config`
|
|
593
|
+
: [
|
|
594
|
+
missingTargets.length > 0
|
|
595
|
+
? `Missing from migration config: ${missingTargets.join(", ")}`
|
|
596
|
+
: null,
|
|
597
|
+
staleTargets.length > 0
|
|
598
|
+
? `Not present in scripts/block-config.ts: ${staleTargets.join(", ")}`
|
|
599
|
+
: null,
|
|
600
|
+
]
|
|
601
|
+
.filter((detail) => typeof detail === "string")
|
|
602
|
+
.join("; "));
|
|
603
|
+
}
|
|
604
|
+
catch (error) {
|
|
605
|
+
recordCheck("fail", "Workspace migration targets", error instanceof Error ? error.message : String(error));
|
|
606
|
+
}
|
|
607
|
+
}
|
|
564
608
|
/**
|
|
565
609
|
* Validate the migration workspace without mutating files.
|
|
566
610
|
*
|
|
@@ -591,6 +635,7 @@ export function doctorProjectMigrations(projectDir, { all = false, fromMigration
|
|
|
591
635
|
const snapshotVersions = new Set(targetVersions.length > 0
|
|
592
636
|
? [state.config.currentMigrationVersion, ...targetVersions]
|
|
593
637
|
: state.config.supportedMigrationVersions);
|
|
638
|
+
recordWorkspaceMigrationTargetAlignment(projectDir, state, recordCheck);
|
|
594
639
|
for (const version of snapshotVersions) {
|
|
595
640
|
for (const block of state.blocks) {
|
|
596
641
|
const snapshotRoot = getSnapshotRoot(projectDir, block, version);
|