@wp-typia/project-tools 0.16.11 → 0.16.12

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 (101) hide show
  1. package/README.md +9 -3
  2. package/dist/runtime/built-in-block-artifact-documents.d.ts +3 -0
  3. package/dist/runtime/built-in-block-artifact-documents.js +2 -0
  4. package/dist/runtime/built-in-block-artifact-types.d.ts +51 -0
  5. package/dist/runtime/built-in-block-artifact-types.js +304 -0
  6. package/dist/runtime/built-in-block-artifacts.js +4 -803
  7. package/dist/runtime/built-in-block-attribute-emitters.d.ts +71 -0
  8. package/dist/runtime/built-in-block-attribute-emitters.js +176 -0
  9. package/dist/runtime/built-in-block-attribute-specs.d.ts +38 -0
  10. package/dist/runtime/built-in-block-attribute-specs.js +358 -0
  11. package/dist/runtime/built-in-block-code-templates/basic.d.ts +4 -0
  12. package/dist/runtime/built-in-block-code-templates/basic.js +249 -0
  13. package/dist/runtime/built-in-block-code-templates/compound-child.d.ts +4 -0
  14. package/dist/runtime/built-in-block-code-templates/compound-child.js +138 -0
  15. package/dist/runtime/built-in-block-code-templates/compound-parent.d.ts +6 -0
  16. package/dist/runtime/built-in-block-code-templates/compound-parent.js +227 -0
  17. package/dist/runtime/built-in-block-code-templates/compound-persistence.d.ts +4 -0
  18. package/dist/runtime/built-in-block-code-templates/compound-persistence.js +478 -0
  19. package/dist/runtime/built-in-block-code-templates/compound.d.ts +3 -0
  20. package/dist/runtime/built-in-block-code-templates/compound.js +3 -0
  21. package/dist/runtime/built-in-block-code-templates/interactivity.d.ts +5 -0
  22. package/dist/runtime/built-in-block-code-templates/interactivity.js +547 -0
  23. package/dist/runtime/built-in-block-code-templates/persistence.d.ts +5 -0
  24. package/dist/runtime/built-in-block-code-templates/persistence.js +550 -0
  25. package/dist/runtime/built-in-block-code-templates/shared.d.ts +16 -0
  26. package/dist/runtime/built-in-block-code-templates/shared.js +53 -0
  27. package/dist/runtime/built-in-block-code-templates.d.ts +5 -32
  28. package/dist/runtime/built-in-block-code-templates.js +5 -2230
  29. package/dist/runtime/cli-add-block-config.d.ts +6 -0
  30. package/dist/runtime/cli-add-block-config.js +143 -0
  31. package/dist/runtime/cli-add-block-legacy-validator.d.ts +4 -0
  32. package/dist/runtime/cli-add-block-legacy-validator.js +168 -0
  33. package/dist/runtime/cli-add-block.js +3 -301
  34. package/dist/runtime/cli-add-workspace-assets.d.ts +38 -0
  35. package/dist/runtime/cli-add-workspace-assets.js +399 -0
  36. package/dist/runtime/cli-add-workspace.d.ts +2 -38
  37. package/dist/runtime/cli-add-workspace.js +5 -396
  38. package/dist/runtime/cli-doctor-environment.d.ts +12 -0
  39. package/dist/runtime/cli-doctor-environment.js +123 -0
  40. package/dist/runtime/cli-doctor-workspace.d.ts +14 -0
  41. package/dist/runtime/cli-doctor-workspace.js +296 -0
  42. package/dist/runtime/cli-doctor.d.ts +4 -2
  43. package/dist/runtime/cli-doctor.js +10 -405
  44. package/dist/runtime/migration-command-surface.d.ts +67 -0
  45. package/dist/runtime/migration-command-surface.js +189 -0
  46. package/dist/runtime/migration-diff-rename.d.ts +13 -0
  47. package/dist/runtime/migration-diff-rename.js +192 -0
  48. package/dist/runtime/migration-diff-transform.d.ts +14 -0
  49. package/dist/runtime/migration-diff-transform.js +105 -0
  50. package/dist/runtime/migration-diff.js +12 -297
  51. package/dist/runtime/migration-generated-artifacts.d.ts +3 -0
  52. package/dist/runtime/migration-generated-artifacts.js +41 -0
  53. package/dist/runtime/migration-maintenance.d.ts +51 -0
  54. package/dist/runtime/migration-maintenance.js +380 -0
  55. package/dist/runtime/migration-planning.d.ts +23 -0
  56. package/dist/runtime/migration-planning.js +131 -0
  57. package/dist/runtime/migration-project-config-source.d.ts +6 -0
  58. package/dist/runtime/migration-project-config-source.js +424 -0
  59. package/dist/runtime/migration-project-layout-discovery.d.ts +61 -0
  60. package/dist/runtime/migration-project-layout-discovery.js +337 -0
  61. package/dist/runtime/migration-project-layout-paths.d.ts +135 -0
  62. package/dist/runtime/migration-project-layout-paths.js +288 -0
  63. package/dist/runtime/migration-project-layout.d.ts +3 -0
  64. package/dist/runtime/migration-project-layout.js +2 -0
  65. package/dist/runtime/migration-project-workspace.d.ts +47 -0
  66. package/dist/runtime/migration-project-workspace.js +212 -0
  67. package/dist/runtime/migration-project.d.ts +4 -94
  68. package/dist/runtime/migration-project.js +3 -1101
  69. package/dist/runtime/migration-render-diff-rule.d.ts +5 -0
  70. package/dist/runtime/migration-render-diff-rule.js +120 -0
  71. package/dist/runtime/migration-render-execution.d.ts +3 -0
  72. package/dist/runtime/migration-render-execution.js +428 -0
  73. package/dist/runtime/migration-render-generated.d.ts +27 -0
  74. package/dist/runtime/migration-render-generated.js +230 -0
  75. package/dist/runtime/migration-render-support.d.ts +3 -0
  76. package/dist/runtime/migration-render-support.js +16 -0
  77. package/dist/runtime/migration-render.d.ts +3 -33
  78. package/dist/runtime/migration-render.js +3 -789
  79. package/dist/runtime/migrations.d.ts +24 -118
  80. package/dist/runtime/migrations.js +12 -700
  81. package/dist/runtime/scaffold-bootstrap.d.ts +45 -0
  82. package/dist/runtime/scaffold-bootstrap.js +185 -0
  83. package/dist/runtime/scaffold-package-manager-files.d.ts +35 -0
  84. package/dist/runtime/scaffold-package-manager-files.js +79 -0
  85. package/dist/runtime/scaffold.d.ts +1 -12
  86. package/dist/runtime/scaffold.js +10 -393
  87. package/dist/runtime/template-source-contracts.d.ts +81 -0
  88. package/dist/runtime/template-source-contracts.js +1 -0
  89. package/dist/runtime/template-source-external.d.ts +21 -0
  90. package/dist/runtime/template-source-external.js +184 -0
  91. package/dist/runtime/template-source-locators.d.ts +4 -0
  92. package/dist/runtime/template-source-locators.js +72 -0
  93. package/dist/runtime/template-source-normalization.d.ts +7 -0
  94. package/dist/runtime/template-source-normalization.js +53 -0
  95. package/dist/runtime/template-source-remote.d.ts +23 -0
  96. package/dist/runtime/template-source-remote.js +336 -0
  97. package/dist/runtime/template-source-seeds.d.ts +12 -0
  98. package/dist/runtime/template-source-seeds.js +243 -0
  99. package/dist/runtime/template-source.d.ts +4 -86
  100. package/dist/runtime/template-source.js +9 -828
  101. package/package.json +4 -4
@@ -1,417 +1,22 @@
1
- import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { execFileSync } from "node:child_process";
5
- import { access, constants as fsConstants, rm, writeFile } from "node:fs/promises";
6
- import { parseScaffoldBlockMetadata } from "@wp-typia/block-runtime/blocks";
7
- import { getBuiltInTemplateLayerDirs, isOmittableBuiltInTemplateLayerDir, } from "./template-builtins.js";
8
- import { HOOKED_BLOCK_ANCHOR_PATTERN, HOOKED_BLOCK_POSITION_SET, } from "./hooked-blocks.js";
9
- import { isBuiltInTemplateId, listTemplates } from "./template-registry.js";
10
- import { readWorkspaceInventory } from "./workspace-inventory.js";
11
- import { getInvalidWorkspaceProjectReason, parseWorkspacePackageJson, WORKSPACE_TEMPLATE_PACKAGE, tryResolveWorkspaceProject, } from "./workspace-project.js";
12
1
  import { createCliCommandError, formatDoctorCheckLine, formatDoctorSummaryLine, getDoctorFailureDetailLines, } from "./cli-diagnostics.js";
13
- const WORKSPACE_COLLECTION_IMPORT_LINE = "import '../../collection';";
14
- const WORKSPACE_COLLECTION_IMPORT_PATTERN = /^\s*import\s+["']\.\.\/\.\.\/collection["']\s*;?\s*$/m;
15
- const WORKSPACE_BINDING_SERVER_GLOB = "/src/bindings/*/server.php";
16
- const WORKSPACE_BINDING_EDITOR_SCRIPT = "build/bindings/index.js";
17
- const WORKSPACE_BINDING_EDITOR_ASSET = "build/bindings/index.asset.php";
18
- const WORKSPACE_GENERATED_BLOCK_ARTIFACTS = [
19
- "block.json",
20
- "typia.manifest.json",
21
- "typia.schema.json",
22
- "typia-validator.php",
23
- "typia.openapi.json",
24
- ];
25
- function readCommandVersion(command, args = ["--version"]) {
26
- try {
27
- return execFileSync(command, args, {
28
- encoding: "utf8",
29
- stdio: ["ignore", "pipe", "ignore"],
30
- }).trim();
31
- }
32
- catch {
33
- return null;
34
- }
35
- }
36
- function compareMajorVersion(actualVersion, minimumMajor) {
37
- const parsed = Number.parseInt(actualVersion.replace(/^v/, "").split(".")[0] ?? "", 10);
38
- return Number.isFinite(parsed) && parsed >= minimumMajor;
39
- }
40
- async function checkWritableDirectory(directory) {
41
- try {
42
- await access(directory, fsConstants.W_OK);
43
- return true;
44
- }
45
- catch {
46
- return false;
47
- }
48
- }
49
- async function checkTempDirectory() {
50
- const tempFile = path.join(os.tmpdir(), `wp-typia-${Date.now()}.tmp`);
51
- try {
52
- await writeFile(tempFile, "ok", "utf8");
53
- await rm(tempFile, { force: true });
54
- return true;
55
- }
56
- catch {
57
- return false;
58
- }
59
- }
60
- function createDoctorCheck(label, status, detail) {
61
- return { detail, label, status };
62
- }
63
- function getWorkspaceBootstrapRelativePath(packageName) {
64
- const packageBaseName = packageName.split("/").pop() ?? packageName;
65
- return `${packageBaseName}.php`;
66
- }
67
- function checkExistingFiles(projectDir, label, filePaths) {
68
- const missing = filePaths
69
- .filter((filePath) => typeof filePath === "string")
70
- .filter((filePath) => !fs.existsSync(path.join(projectDir, filePath)));
71
- return createDoctorCheck(label, missing.length === 0 ? "pass" : "fail", missing.length === 0 ? "All referenced files exist" : `Missing: ${missing.join(", ")}`);
72
- }
73
- function checkWorkspacePackageMetadata(workspace, packageJson) {
74
- const issues = [];
75
- const packageName = packageJson.name;
76
- const bootstrapRelativePath = getWorkspaceBootstrapRelativePath(typeof packageName === "string" && packageName.length > 0 ? packageName : workspace.packageName);
77
- const wpTypia = packageJson.wpTypia;
78
- if (typeof packageName !== "string" || packageName.length === 0) {
79
- issues.push("package.json must define a string name for workspace bootstrap resolution");
80
- }
81
- if (wpTypia?.projectType !== "workspace") {
82
- issues.push('wpTypia.projectType must be "workspace"');
83
- }
84
- if (wpTypia?.templatePackage !== WORKSPACE_TEMPLATE_PACKAGE) {
85
- issues.push(`wpTypia.templatePackage must be "${WORKSPACE_TEMPLATE_PACKAGE}"`);
86
- }
87
- if (wpTypia?.namespace !== workspace.workspace.namespace) {
88
- issues.push(`wpTypia.namespace must equal "${workspace.workspace.namespace}"`);
89
- }
90
- if (wpTypia?.textDomain !== workspace.workspace.textDomain) {
91
- issues.push(`wpTypia.textDomain must equal "${workspace.workspace.textDomain}"`);
92
- }
93
- if (wpTypia?.phpPrefix !== workspace.workspace.phpPrefix) {
94
- issues.push(`wpTypia.phpPrefix must equal "${workspace.workspace.phpPrefix}"`);
95
- }
96
- if (!fs.existsSync(path.join(workspace.projectDir, bootstrapRelativePath))) {
97
- issues.push(`Missing bootstrap file ${bootstrapRelativePath}`);
98
- }
99
- return createDoctorCheck("Workspace package metadata", issues.length === 0 ? "pass" : "fail", issues.length === 0
100
- ? `package.json metadata aligns with ${workspace.packageName} and ${bootstrapRelativePath}`
101
- : issues.join("; "));
102
- }
103
- function getWorkspaceBlockRequiredFiles(block) {
104
- const blockDir = path.join("src", "blocks", block.slug);
105
- return Array.from(new Set([
106
- block.typesFile,
107
- block.apiTypesFile,
108
- block.openApiFile,
109
- path.join(blockDir, "index.tsx"),
110
- ...WORKSPACE_GENERATED_BLOCK_ARTIFACTS.map((fileName) => path.join(blockDir, fileName)),
111
- ].filter((filePath) => typeof filePath === "string")));
112
- }
113
- function checkWorkspaceBlockMetadata(projectDir, workspace, block) {
114
- const blockJsonRelativePath = path.join("src", "blocks", block.slug, "block.json");
115
- const blockJsonPath = path.join(projectDir, blockJsonRelativePath);
116
- if (!fs.existsSync(blockJsonPath)) {
117
- return createDoctorCheck(`Block metadata ${block.slug}`, "fail", `Missing ${blockJsonRelativePath}`);
118
- }
119
- let blockJson;
120
- try {
121
- blockJson = parseScaffoldBlockMetadata(JSON.parse(fs.readFileSync(blockJsonPath, "utf8")));
122
- }
123
- catch (error) {
124
- return createDoctorCheck(`Block metadata ${block.slug}`, "fail", error instanceof Error ? error.message : String(error));
125
- }
126
- const expectedName = `${workspace.workspace.namespace}/${block.slug}`;
127
- const issues = [];
128
- if (blockJson.name !== expectedName) {
129
- issues.push(`block.json name must equal "${expectedName}"`);
130
- }
131
- if (blockJson.textdomain !== workspace.workspace.textDomain) {
132
- issues.push(`block.json textdomain must equal "${workspace.workspace.textDomain}"`);
133
- }
134
- return createDoctorCheck(`Block metadata ${block.slug}`, issues.length === 0 ? "pass" : "fail", issues.length === 0
135
- ? `block.json matches ${expectedName} and ${workspace.workspace.textDomain}`
136
- : issues.join("; "));
137
- }
138
- function checkWorkspaceBlockHooks(projectDir, blockSlug) {
139
- const blockJsonRelativePath = path.join("src", "blocks", blockSlug, "block.json");
140
- const blockJsonPath = path.join(projectDir, blockJsonRelativePath);
141
- if (!fs.existsSync(blockJsonPath)) {
142
- return createDoctorCheck(`Block hooks ${blockSlug}`, "fail", `Missing ${blockJsonRelativePath}`);
143
- }
144
- let blockJson;
145
- try {
146
- blockJson = parseScaffoldBlockMetadata(JSON.parse(fs.readFileSync(blockJsonPath, "utf8")));
147
- }
148
- catch (error) {
149
- return createDoctorCheck(`Block hooks ${blockSlug}`, "fail", error instanceof Error ? error.message : String(error));
150
- }
151
- const blockHooks = blockJson.blockHooks;
152
- if (blockHooks === undefined) {
153
- return createDoctorCheck(`Block hooks ${blockSlug}`, "pass", "No blockHooks metadata configured");
154
- }
155
- if (!blockHooks || typeof blockHooks !== "object" || Array.isArray(blockHooks)) {
156
- return createDoctorCheck(`Block hooks ${blockSlug}`, "fail", `${blockJsonRelativePath} must define blockHooks as an object when present.`);
157
- }
158
- const blockName = typeof blockJson.name === "string" && blockJson.name.trim().length > 0
159
- ? blockJson.name.trim()
160
- : null;
161
- const invalidEntries = Object.entries(blockHooks).filter(([anchor, position]) => (blockName !== null && anchor.trim() === blockName) ||
162
- anchor.trim().length === 0 ||
163
- anchor !== anchor.trim() ||
164
- !HOOKED_BLOCK_ANCHOR_PATTERN.test(anchor) ||
165
- typeof position !== "string" ||
166
- !HOOKED_BLOCK_POSITION_SET.has(position));
167
- return createDoctorCheck(`Block hooks ${blockSlug}`, invalidEntries.length === 0 ? "pass" : "fail", invalidEntries.length === 0
168
- ? `blockHooks metadata is valid${Object.keys(blockHooks).length > 0 ? ` (${Object.keys(blockHooks).join(", ")})` : ""}`
169
- : `Invalid blockHooks entries: ${invalidEntries
170
- .map(([anchor, position]) => `${anchor || "<empty>"} => ${String(position)}`)
171
- .join(", ")}`);
172
- }
173
- function checkWorkspaceBlockCollectionImport(projectDir, blockSlug) {
174
- const entryRelativePath = path.join("src", "blocks", blockSlug, "index.tsx");
175
- const entryPath = path.join(projectDir, entryRelativePath);
176
- if (!fs.existsSync(entryPath)) {
177
- return createDoctorCheck(`Block collection ${blockSlug}`, "fail", `Missing ${entryRelativePath}`);
178
- }
179
- const source = fs.readFileSync(entryPath, "utf8");
180
- const hasCollectionImport = WORKSPACE_COLLECTION_IMPORT_PATTERN.test(source);
181
- return createDoctorCheck(`Block collection ${blockSlug}`, hasCollectionImport ? "pass" : "fail", hasCollectionImport
182
- ? "Shared block collection import is present"
183
- : `Missing a shared collection import like ${WORKSPACE_COLLECTION_IMPORT_LINE}`);
184
- }
185
- function checkWorkspacePatternBootstrap(projectDir, packageName) {
186
- const packageBaseName = packageName.split("/").pop() ?? packageName;
187
- const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
188
- if (!fs.existsSync(bootstrapPath)) {
189
- return createDoctorCheck("Pattern bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
190
- }
191
- const source = fs.readFileSync(bootstrapPath, "utf8");
192
- const hasCategoryAnchor = source.includes("register_block_pattern_category");
193
- const hasPatternGlob = source.includes("/src/patterns/*.php");
194
- return createDoctorCheck("Pattern bootstrap", hasCategoryAnchor && hasPatternGlob ? "pass" : "fail", hasCategoryAnchor && hasPatternGlob
195
- ? "Pattern category and loader hooks are present"
196
- : "Missing pattern category registration or src/patterns loader hook");
197
- }
198
- function checkWorkspaceBindingBootstrap(projectDir, packageName) {
199
- const packageBaseName = packageName.split("/").pop() ?? packageName;
200
- const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
201
- if (!fs.existsSync(bootstrapPath)) {
202
- return createDoctorCheck("Binding bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
203
- }
204
- const source = fs.readFileSync(bootstrapPath, "utf8");
205
- const hasServerGlob = source.includes(WORKSPACE_BINDING_SERVER_GLOB);
206
- const hasEditorEnqueueHook = source.includes("enqueue_block_editor_assets");
207
- const hasEditorScript = source.includes(WORKSPACE_BINDING_EDITOR_SCRIPT);
208
- const hasEditorAsset = source.includes(WORKSPACE_BINDING_EDITOR_ASSET);
209
- return createDoctorCheck("Binding bootstrap", hasServerGlob && hasEditorEnqueueHook && hasEditorScript && hasEditorAsset ? "pass" : "fail", hasServerGlob && hasEditorEnqueueHook && hasEditorScript && hasEditorAsset
210
- ? "Binding source PHP and editor bootstrap hooks are present"
211
- : "Missing binding source PHP require glob or editor enqueue hook");
212
- }
213
- function checkWorkspaceBindingSourcesIndex(projectDir, bindingSources) {
214
- const indexRelativePath = [path.join("src", "bindings", "index.ts"), path.join("src", "bindings", "index.js")].find((relativePath) => fs.existsSync(path.join(projectDir, relativePath)));
215
- if (!indexRelativePath) {
216
- return createDoctorCheck("Binding sources index", "fail", "Missing src/bindings/index.ts or src/bindings/index.js");
217
- }
218
- const indexPath = path.join(projectDir, indexRelativePath);
219
- const source = fs.readFileSync(indexPath, "utf8");
220
- const missingImports = bindingSources.filter((bindingSource) => !source.includes(`./${bindingSource.slug}/editor`));
221
- return createDoctorCheck("Binding sources index", missingImports.length === 0 ? "pass" : "fail", missingImports.length === 0
222
- ? "Binding source editor registrations are aggregated"
223
- : `Missing editor imports for: ${missingImports.map((entry) => entry.slug).join(", ")}`);
224
- }
225
- function checkVariationEntrypoint(projectDir, blockSlug) {
226
- const entryPath = path.join(projectDir, "src", "blocks", blockSlug, "index.tsx");
227
- if (!fs.existsSync(entryPath)) {
228
- return createDoctorCheck(`Variation entrypoint ${blockSlug}`, "fail", `Missing ${path.relative(projectDir, entryPath)}`);
229
- }
230
- const source = fs.readFileSync(entryPath, "utf8");
231
- const hasImport = source.includes("./variations");
232
- const hasCall = source.includes("registerWorkspaceVariations()");
233
- return createDoctorCheck(`Variation entrypoint ${blockSlug}`, hasImport && hasCall ? "pass" : "fail", hasImport && hasCall
234
- ? "Variations registration hook is present"
235
- : "Missing ./variations import or registerWorkspaceVariations() call");
236
- }
237
- function checkMigrationWorkspaceHint(workspace, packageJson) {
238
- const hasMigrationScript = typeof packageJson.scripts?.["migration:doctor"] === "string";
239
- const migrationConfigRelativePath = path.join("src", "migrations", "config.ts");
240
- const hasMigrationConfig = fs.existsSync(path.join(workspace.projectDir, migrationConfigRelativePath));
241
- if (!hasMigrationScript && !hasMigrationConfig) {
242
- return null;
243
- }
244
- return createDoctorCheck("Migration workspace", hasMigrationConfig ? "pass" : "fail", hasMigrationConfig
245
- ? "Run `wp-typia migrate doctor --all` for migration target, snapshot, fixture, and generated artifact checks"
246
- : `Missing ${migrationConfigRelativePath} for the configured migration workspace`);
247
- }
2
+ import { getEnvironmentDoctorChecks } from "./cli-doctor-environment.js";
3
+ import { getWorkspaceDoctorChecks } from "./cli-doctor-workspace.js";
248
4
  /**
249
5
  * Collect all runtime doctor checks for the current environment.
250
6
  *
251
- * The returned array includes command availability checks, directory
252
- * writability checks, and built-in template asset checks in display order.
7
+ * The returned array concatenates environment checks (command availability,
8
+ * directory writability, and built-in template assets) followed by
9
+ * workspace checks (package metadata, inventory, blocks, variations,
10
+ * patterns, bindings, and optional migration hints) in display order.
253
11
  *
254
12
  * @param cwd Working directory to validate for writability.
255
13
  * @returns Ordered doctor check rows ready for CLI rendering.
256
14
  */
257
15
  export async function getDoctorChecks(cwd) {
258
- const checks = [];
259
- const bunVersion = readCommandVersion("bun");
260
- const nodeVersion = readCommandVersion("node");
261
- const gitVersion = readCommandVersion("git");
262
- const cwdWritable = await checkWritableDirectory(cwd);
263
- const tempWritable = await checkTempDirectory();
264
- checks.push({
265
- status: bunVersion && compareMajorVersion(bunVersion, 1) ? "pass" : "fail",
266
- label: "Bun",
267
- detail: bunVersion ? `Detected ${bunVersion}` : "Not available",
268
- });
269
- checks.push({
270
- status: nodeVersion && compareMajorVersion(nodeVersion, 20) ? "pass" : "fail",
271
- label: "Node",
272
- detail: nodeVersion ? `Detected ${nodeVersion}` : "Not available",
273
- });
274
- checks.push({
275
- status: gitVersion ? "pass" : "fail",
276
- label: "git",
277
- detail: gitVersion ?? "Not available",
278
- });
279
- checks.push({
280
- status: cwdWritable ? "pass" : "fail",
281
- label: "Current directory",
282
- detail: cwdWritable ? "Writable" : "Not writable",
283
- });
284
- checks.push({
285
- status: tempWritable ? "pass" : "fail",
286
- label: "Temp directory",
287
- detail: tempWritable ? "Writable" : "Not writable",
288
- });
289
- for (const template of listTemplates()) {
290
- if (!isBuiltInTemplateId(template.id)) {
291
- const templateDirExists = fs.existsSync(template.templateDir);
292
- const hasAssets = templateDirExists &&
293
- fs.existsSync(path.join(template.templateDir, "package.json.mustache"));
294
- checks.push({
295
- status: !templateDirExists || hasAssets ? "pass" : "fail",
296
- label: `Template ${template.id}`,
297
- detail: !templateDirExists
298
- ? "External template metadata only; local overlay package is not installed."
299
- : hasAssets
300
- ? template.templateDir
301
- : "Missing core template assets",
302
- });
303
- continue;
304
- }
305
- const builtInTemplateId = template.id;
306
- const layerDirs = builtInTemplateId === "persistence"
307
- ? Array.from(new Set([
308
- ...getBuiltInTemplateLayerDirs(builtInTemplateId, { persistencePolicy: "authenticated" }),
309
- ...getBuiltInTemplateLayerDirs(builtInTemplateId, { persistencePolicy: "public" }),
310
- ]))
311
- : builtInTemplateId === "compound"
312
- ? Array.from(new Set([
313
- ...getBuiltInTemplateLayerDirs(builtInTemplateId),
314
- ...getBuiltInTemplateLayerDirs(builtInTemplateId, {
315
- persistenceEnabled: true,
316
- persistencePolicy: "authenticated",
317
- }),
318
- ...getBuiltInTemplateLayerDirs(builtInTemplateId, {
319
- persistenceEnabled: true,
320
- persistencePolicy: "public",
321
- }),
322
- ]))
323
- : getBuiltInTemplateLayerDirs(builtInTemplateId);
324
- const missingRequiredLayer = layerDirs.some((layerDir) => !fs.existsSync(layerDir) &&
325
- !isOmittableBuiltInTemplateLayerDir(builtInTemplateId, layerDir));
326
- const existingLayerDirs = layerDirs.filter((layerDir) => fs.existsSync(layerDir));
327
- const hasAssets = !missingRequiredLayer &&
328
- existingLayerDirs.some((layerDir) => fs.existsSync(path.join(layerDir, "package.json.mustache"))) &&
329
- existingLayerDirs.some((layerDir) => fs.existsSync(path.join(layerDir, "src")));
330
- checks.push({
331
- status: hasAssets ? "pass" : "fail",
332
- label: `Template ${template.id}`,
333
- detail: hasAssets
334
- ? existingLayerDirs.join(" + ")
335
- : "Missing core template assets",
336
- });
337
- }
338
- let workspace = null;
339
- let invalidWorkspaceReason = null;
340
- try {
341
- invalidWorkspaceReason = getInvalidWorkspaceProjectReason(cwd);
342
- workspace = tryResolveWorkspaceProject(cwd);
343
- }
344
- catch (error) {
345
- checks.push(createDoctorCheck("Workspace package metadata", "fail", error instanceof Error ? error.message : String(error)));
346
- return checks;
347
- }
348
- if (!workspace) {
349
- if (invalidWorkspaceReason) {
350
- checks.push(createDoctorCheck("Workspace package metadata", "fail", invalidWorkspaceReason));
351
- }
352
- return checks;
353
- }
354
- checks.push(createDoctorCheck("Workspace marker", "pass", `Official workspace detected for ${workspace.workspace.namespace}`));
355
- let workspacePackageJson;
356
- try {
357
- workspacePackageJson = parseWorkspacePackageJson(workspace.projectDir);
358
- }
359
- catch (error) {
360
- checks.push(createDoctorCheck("Workspace package metadata", "fail", error instanceof Error ? error.message : String(error)));
361
- return checks;
362
- }
363
- checks.push(checkWorkspacePackageMetadata(workspace, workspacePackageJson));
364
- try {
365
- const inventory = readWorkspaceInventory(workspace.projectDir);
366
- 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)`));
367
- for (const block of inventory.blocks) {
368
- checks.push(checkExistingFiles(workspace.projectDir, `Block ${block.slug}`, [
369
- ...getWorkspaceBlockRequiredFiles(block),
370
- ]));
371
- checks.push(checkWorkspaceBlockMetadata(workspace.projectDir, workspace, block));
372
- checks.push(checkWorkspaceBlockHooks(workspace.projectDir, block.slug));
373
- checks.push(checkWorkspaceBlockCollectionImport(workspace.projectDir, block.slug));
374
- }
375
- const registeredBlockSlugs = new Set(inventory.blocks.map((block) => block.slug));
376
- const variationTargetBlocks = new Set();
377
- for (const variation of inventory.variations) {
378
- if (!registeredBlockSlugs.has(variation.block)) {
379
- checks.push(createDoctorCheck(`Variation ${variation.block}/${variation.slug}`, "fail", `Variation references unknown block "${variation.block}"`));
380
- continue;
381
- }
382
- variationTargetBlocks.add(variation.block);
383
- checks.push(checkExistingFiles(workspace.projectDir, `Variation ${variation.block}/${variation.slug}`, [variation.file]));
384
- }
385
- for (const blockSlug of variationTargetBlocks) {
386
- checks.push(checkVariationEntrypoint(workspace.projectDir, blockSlug));
387
- }
388
- const shouldCheckPatternBootstrap = inventory.patterns.length > 0 ||
389
- fs.existsSync(path.join(workspace.projectDir, "src", "patterns"));
390
- if (shouldCheckPatternBootstrap) {
391
- checks.push(checkWorkspacePatternBootstrap(workspace.projectDir, workspace.packageName));
392
- }
393
- for (const pattern of inventory.patterns) {
394
- checks.push(checkExistingFiles(workspace.projectDir, `Pattern ${pattern.slug}`, [pattern.file]));
395
- }
396
- if (inventory.bindingSources.length > 0) {
397
- checks.push(checkWorkspaceBindingBootstrap(workspace.projectDir, workspace.packageName));
398
- checks.push(checkWorkspaceBindingSourcesIndex(workspace.projectDir, inventory.bindingSources));
399
- }
400
- for (const bindingSource of inventory.bindingSources) {
401
- checks.push(checkExistingFiles(workspace.projectDir, `Binding source ${bindingSource.slug}`, [
402
- bindingSource.serverFile,
403
- bindingSource.editorFile,
404
- ]));
405
- }
406
- const migrationWorkspaceCheck = checkMigrationWorkspaceHint(workspace, workspacePackageJson);
407
- if (migrationWorkspaceCheck) {
408
- checks.push(migrationWorkspaceCheck);
409
- }
410
- }
411
- catch (error) {
412
- checks.push(createDoctorCheck("Workspace inventory", "fail", error instanceof Error ? error.message : String(error)));
413
- }
414
- return checks;
16
+ return [
17
+ ...(await getEnvironmentDoctorChecks(cwd)),
18
+ ...getWorkspaceDoctorChecks(cwd),
19
+ ];
415
20
  }
416
21
  /**
417
22
  * Run doctor checks, render each line, and fail when any check does not pass.
@@ -0,0 +1,67 @@
1
+ import type { ReadlinePrompt } from './cli-prompt.js';
2
+ import type { ParsedMigrationArgs, RenderLine } from './migration-types.js';
3
+ export type CommandRenderOptions = {
4
+ prompt?: ReadlinePrompt;
5
+ renderLine?: RenderLine;
6
+ };
7
+ export type DiffLikeOptions = {
8
+ fromMigrationVersion?: string;
9
+ renderLine?: RenderLine;
10
+ toMigrationVersion?: string;
11
+ };
12
+ export type VerifyOptions = {
13
+ all?: boolean;
14
+ fromMigrationVersion?: string;
15
+ renderLine?: RenderLine;
16
+ };
17
+ export type FixturesOptions = {
18
+ all?: boolean;
19
+ confirmOverwrite?: ((message: string) => boolean) | undefined;
20
+ force?: boolean;
21
+ fromMigrationVersion?: string;
22
+ isInteractive?: boolean;
23
+ renderLine?: RenderLine;
24
+ toMigrationVersion?: string;
25
+ };
26
+ export type FuzzOptions = {
27
+ all?: boolean;
28
+ fromMigrationVersion?: string;
29
+ iterations?: number;
30
+ renderLine?: RenderLine;
31
+ seed?: number;
32
+ };
33
+ export type WizardOptions = CommandRenderOptions & {
34
+ isInteractive?: boolean;
35
+ };
36
+ /**
37
+ * Returns the formatted help text for migration CLI commands and flags.
38
+ *
39
+ * @returns Multi-line usage text for the `wp-typia migrate` command surface.
40
+ */
41
+ export declare function formatMigrationHelpText(): string;
42
+ /**
43
+ * Parses migration CLI arguments into a structured command payload.
44
+ *
45
+ * @param argv Command-line arguments that follow the `migrate` subcommand.
46
+ * @returns Parsed migration command and normalized flags for runtime dispatch.
47
+ * @throws Error When no arguments are provided, an unknown flag is encountered, or legacy semver flags are used.
48
+ */
49
+ export declare function parseMigrationArgs(argv: string[]): ParsedMigrationArgs;
50
+ /**
51
+ * Parse an optional positive integer flag value.
52
+ *
53
+ * @param value Raw CLI flag value, or `undefined` when the flag was omitted.
54
+ * @param label Human-readable flag label used in validation error messages.
55
+ * @returns The parsed integer when provided, otherwise `undefined`.
56
+ * @throws Error When the value is not a base-10 integer greater than zero.
57
+ */
58
+ export declare function parsePositiveInteger(value: string | undefined, label: string): number | undefined;
59
+ /**
60
+ * Parse an optional non-negative integer flag value.
61
+ *
62
+ * @param value Raw CLI flag value, or `undefined` when the flag was omitted.
63
+ * @param label Human-readable flag label used in validation error messages.
64
+ * @returns The parsed integer when provided, otherwise `undefined`.
65
+ * @throws Error When the value is not a base-10 integer greater than or equal to zero.
66
+ */
67
+ export declare function parseNonNegativeInteger(value: string | undefined, label: string): number | undefined;