@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,18 @@
1
+ import type { DoctorCheck } from "./cli-doctor.js";
2
+ /**
3
+ * Collect workspace-scoped doctor checks for the given working directory.
4
+ *
5
+ * When the directory is not an official workspace, the function returns a
6
+ * "Doctor scope" row explaining that only environment checks ran, plus a
7
+ * failing workspace metadata row when a nearby candidate workspace is invalid.
8
+ * When workspace resolution or metadata parsing throws, the corresponding
9
+ * failing rows are returned early and the remaining checks are skipped.
10
+ * When an official workspace is detected, a passing "Doctor scope" row is
11
+ * emitted first so the remaining package metadata, inventory, source-tree
12
+ * drift, and optional migration hint rows are clearly framed as workspace
13
+ * diagnostics for that run.
14
+ *
15
+ * @param cwd Working directory expected to host an official workspace.
16
+ * @returns Ordered workspace check rows ready for CLI rendering.
17
+ */
18
+ export declare function getWorkspaceDoctorChecks(cwd: string): DoctorCheck[];
@@ -0,0 +1,308 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { parseScaffoldBlockMetadata } from "@wp-typia/block-runtime/blocks";
4
+ import { HOOKED_BLOCK_ANCHOR_PATTERN, HOOKED_BLOCK_POSITION_SET, } from "./hooked-blocks.js";
5
+ import { readWorkspaceInventory } from "./workspace-inventory.js";
6
+ import { getInvalidWorkspaceProjectReason, parseWorkspacePackageJson, WORKSPACE_TEMPLATE_PACKAGE, tryResolveWorkspaceProject, } from "./workspace-project.js";
7
+ const WORKSPACE_COLLECTION_IMPORT_LINE = "import '../../collection';";
8
+ const WORKSPACE_COLLECTION_IMPORT_PATTERN = /^\s*import\s+["']\.\.\/\.\.\/collection["']\s*;?\s*$/m;
9
+ const WORKSPACE_BINDING_SERVER_GLOB = "/src/bindings/*/server.php";
10
+ const WORKSPACE_BINDING_EDITOR_SCRIPT = "build/bindings/index.js";
11
+ const WORKSPACE_BINDING_EDITOR_ASSET = "build/bindings/index.asset.php";
12
+ const WORKSPACE_GENERATED_BLOCK_ARTIFACTS = [
13
+ "block.json",
14
+ "typia.manifest.json",
15
+ "typia.schema.json",
16
+ "typia-validator.php",
17
+ "typia.openapi.json",
18
+ ];
19
+ function createDoctorCheck(label, status, detail) {
20
+ return { detail, label, status };
21
+ }
22
+ function createDoctorScopeCheck(status, detail) {
23
+ return createDoctorCheck("Doctor scope", status, detail);
24
+ }
25
+ function getWorkspaceBootstrapRelativePath(packageName) {
26
+ const packageBaseName = packageName.split("/").pop() ?? packageName;
27
+ return `${packageBaseName}.php`;
28
+ }
29
+ function checkExistingFiles(projectDir, label, filePaths) {
30
+ const missing = filePaths
31
+ .filter((filePath) => typeof filePath === "string")
32
+ .filter((filePath) => !fs.existsSync(path.join(projectDir, filePath)));
33
+ return createDoctorCheck(label, missing.length === 0 ? "pass" : "fail", missing.length === 0 ? "All referenced files exist" : `Missing: ${missing.join(", ")}`);
34
+ }
35
+ function checkWorkspacePackageMetadata(workspace, packageJson) {
36
+ const issues = [];
37
+ const packageName = packageJson.name;
38
+ const bootstrapRelativePath = getWorkspaceBootstrapRelativePath(typeof packageName === "string" && packageName.length > 0 ? packageName : workspace.packageName);
39
+ const wpTypia = packageJson.wpTypia;
40
+ if (typeof packageName !== "string" || packageName.length === 0) {
41
+ issues.push("package.json must define a string name for workspace bootstrap resolution");
42
+ }
43
+ if (wpTypia?.projectType !== "workspace") {
44
+ issues.push('wpTypia.projectType must be "workspace"');
45
+ }
46
+ if (wpTypia?.templatePackage !== WORKSPACE_TEMPLATE_PACKAGE) {
47
+ issues.push(`wpTypia.templatePackage must be "${WORKSPACE_TEMPLATE_PACKAGE}"`);
48
+ }
49
+ if (wpTypia?.namespace !== workspace.workspace.namespace) {
50
+ issues.push(`wpTypia.namespace must equal "${workspace.workspace.namespace}"`);
51
+ }
52
+ if (wpTypia?.textDomain !== workspace.workspace.textDomain) {
53
+ issues.push(`wpTypia.textDomain must equal "${workspace.workspace.textDomain}"`);
54
+ }
55
+ if (wpTypia?.phpPrefix !== workspace.workspace.phpPrefix) {
56
+ issues.push(`wpTypia.phpPrefix must equal "${workspace.workspace.phpPrefix}"`);
57
+ }
58
+ if (!fs.existsSync(path.join(workspace.projectDir, bootstrapRelativePath))) {
59
+ issues.push(`Missing bootstrap file ${bootstrapRelativePath}`);
60
+ }
61
+ return createDoctorCheck("Workspace package metadata", issues.length === 0 ? "pass" : "fail", issues.length === 0
62
+ ? `package.json metadata aligns with ${workspace.packageName} and ${bootstrapRelativePath}`
63
+ : issues.join("; "));
64
+ }
65
+ function getWorkspaceBlockRequiredFiles(block) {
66
+ const blockDir = path.join("src", "blocks", block.slug);
67
+ return Array.from(new Set([
68
+ block.typesFile,
69
+ block.apiTypesFile,
70
+ block.openApiFile,
71
+ path.join(blockDir, "index.tsx"),
72
+ ...WORKSPACE_GENERATED_BLOCK_ARTIFACTS.map((fileName) => path.join(blockDir, fileName)),
73
+ ].filter((filePath) => typeof filePath === "string")));
74
+ }
75
+ function checkWorkspaceBlockMetadata(projectDir, workspace, block) {
76
+ const blockJsonRelativePath = path.join("src", "blocks", block.slug, "block.json");
77
+ const blockJsonPath = path.join(projectDir, blockJsonRelativePath);
78
+ if (!fs.existsSync(blockJsonPath)) {
79
+ return createDoctorCheck(`Block metadata ${block.slug}`, "fail", `Missing ${blockJsonRelativePath}`);
80
+ }
81
+ let blockJson;
82
+ try {
83
+ blockJson = parseScaffoldBlockMetadata(JSON.parse(fs.readFileSync(blockJsonPath, "utf8")));
84
+ }
85
+ catch (error) {
86
+ return createDoctorCheck(`Block metadata ${block.slug}`, "fail", error instanceof Error ? error.message : String(error));
87
+ }
88
+ const expectedName = `${workspace.workspace.namespace}/${block.slug}`;
89
+ const issues = [];
90
+ if (blockJson.name !== expectedName) {
91
+ issues.push(`block.json name must equal "${expectedName}"`);
92
+ }
93
+ if (blockJson.textdomain !== workspace.workspace.textDomain) {
94
+ issues.push(`block.json textdomain must equal "${workspace.workspace.textDomain}"`);
95
+ }
96
+ return createDoctorCheck(`Block metadata ${block.slug}`, issues.length === 0 ? "pass" : "fail", issues.length === 0
97
+ ? `block.json matches ${expectedName} and ${workspace.workspace.textDomain}`
98
+ : issues.join("; "));
99
+ }
100
+ function checkWorkspaceBlockHooks(projectDir, blockSlug) {
101
+ const blockJsonRelativePath = path.join("src", "blocks", blockSlug, "block.json");
102
+ const blockJsonPath = path.join(projectDir, blockJsonRelativePath);
103
+ if (!fs.existsSync(blockJsonPath)) {
104
+ return createDoctorCheck(`Block hooks ${blockSlug}`, "fail", `Missing ${blockJsonRelativePath}`);
105
+ }
106
+ let blockJson;
107
+ try {
108
+ blockJson = parseScaffoldBlockMetadata(JSON.parse(fs.readFileSync(blockJsonPath, "utf8")));
109
+ }
110
+ catch (error) {
111
+ return createDoctorCheck(`Block hooks ${blockSlug}`, "fail", error instanceof Error ? error.message : String(error));
112
+ }
113
+ const blockHooks = blockJson.blockHooks;
114
+ if (blockHooks === undefined) {
115
+ return createDoctorCheck(`Block hooks ${blockSlug}`, "pass", "No blockHooks metadata configured");
116
+ }
117
+ if (!blockHooks || typeof blockHooks !== "object" || Array.isArray(blockHooks)) {
118
+ return createDoctorCheck(`Block hooks ${blockSlug}`, "fail", `${blockJsonRelativePath} must define blockHooks as an object when present.`);
119
+ }
120
+ const blockName = typeof blockJson.name === "string" && blockJson.name.trim().length > 0
121
+ ? blockJson.name.trim()
122
+ : null;
123
+ const invalidEntries = Object.entries(blockHooks).filter(([anchor, position]) => (blockName !== null && anchor.trim() === blockName) ||
124
+ anchor.trim().length === 0 ||
125
+ anchor !== anchor.trim() ||
126
+ !HOOKED_BLOCK_ANCHOR_PATTERN.test(anchor) ||
127
+ typeof position !== "string" ||
128
+ !HOOKED_BLOCK_POSITION_SET.has(position));
129
+ return createDoctorCheck(`Block hooks ${blockSlug}`, invalidEntries.length === 0 ? "pass" : "fail", invalidEntries.length === 0
130
+ ? `blockHooks metadata is valid${Object.keys(blockHooks).length > 0 ? ` (${Object.keys(blockHooks).join(", ")})` : ""}`
131
+ : `Invalid blockHooks entries: ${invalidEntries
132
+ .map(([anchor, position]) => `${anchor || "<empty>"} => ${String(position)}`)
133
+ .join(", ")}`);
134
+ }
135
+ function checkWorkspaceBlockCollectionImport(projectDir, blockSlug) {
136
+ const entryRelativePath = path.join("src", "blocks", blockSlug, "index.tsx");
137
+ const entryPath = path.join(projectDir, entryRelativePath);
138
+ if (!fs.existsSync(entryPath)) {
139
+ return createDoctorCheck(`Block collection ${blockSlug}`, "fail", `Missing ${entryRelativePath}`);
140
+ }
141
+ const source = fs.readFileSync(entryPath, "utf8");
142
+ const hasCollectionImport = WORKSPACE_COLLECTION_IMPORT_PATTERN.test(source);
143
+ return createDoctorCheck(`Block collection ${blockSlug}`, hasCollectionImport ? "pass" : "fail", hasCollectionImport
144
+ ? "Shared block collection import is present"
145
+ : `Missing a shared collection import like ${WORKSPACE_COLLECTION_IMPORT_LINE}`);
146
+ }
147
+ function checkWorkspacePatternBootstrap(projectDir, packageName) {
148
+ const packageBaseName = packageName.split("/").pop() ?? packageName;
149
+ const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
150
+ if (!fs.existsSync(bootstrapPath)) {
151
+ return createDoctorCheck("Pattern bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
152
+ }
153
+ const source = fs.readFileSync(bootstrapPath, "utf8");
154
+ const hasCategoryAnchor = source.includes("register_block_pattern_category");
155
+ const hasPatternGlob = source.includes("/src/patterns/*.php");
156
+ return createDoctorCheck("Pattern bootstrap", hasCategoryAnchor && hasPatternGlob ? "pass" : "fail", hasCategoryAnchor && hasPatternGlob
157
+ ? "Pattern category and loader hooks are present"
158
+ : "Missing pattern category registration or src/patterns loader hook");
159
+ }
160
+ function checkWorkspaceBindingBootstrap(projectDir, packageName) {
161
+ const packageBaseName = packageName.split("/").pop() ?? packageName;
162
+ const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
163
+ if (!fs.existsSync(bootstrapPath)) {
164
+ return createDoctorCheck("Binding bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
165
+ }
166
+ const source = fs.readFileSync(bootstrapPath, "utf8");
167
+ const hasServerGlob = source.includes(WORKSPACE_BINDING_SERVER_GLOB);
168
+ const hasEditorEnqueueHook = source.includes("enqueue_block_editor_assets");
169
+ const hasEditorScript = source.includes(WORKSPACE_BINDING_EDITOR_SCRIPT);
170
+ const hasEditorAsset = source.includes(WORKSPACE_BINDING_EDITOR_ASSET);
171
+ return createDoctorCheck("Binding bootstrap", hasServerGlob && hasEditorEnqueueHook && hasEditorScript && hasEditorAsset ? "pass" : "fail", hasServerGlob && hasEditorEnqueueHook && hasEditorScript && hasEditorAsset
172
+ ? "Binding source PHP and editor bootstrap hooks are present"
173
+ : "Missing binding source PHP require glob or editor enqueue hook");
174
+ }
175
+ function checkWorkspaceBindingSourcesIndex(projectDir, bindingSources) {
176
+ const indexRelativePath = [path.join("src", "bindings", "index.ts"), path.join("src", "bindings", "index.js")].find((relativePath) => fs.existsSync(path.join(projectDir, relativePath)));
177
+ if (!indexRelativePath) {
178
+ return createDoctorCheck("Binding sources index", "fail", "Missing src/bindings/index.ts or src/bindings/index.js");
179
+ }
180
+ const indexPath = path.join(projectDir, indexRelativePath);
181
+ const source = fs.readFileSync(indexPath, "utf8");
182
+ const missingImports = bindingSources.filter((bindingSource) => !source.includes(`./${bindingSource.slug}/editor`));
183
+ return createDoctorCheck("Binding sources index", missingImports.length === 0 ? "pass" : "fail", missingImports.length === 0
184
+ ? "Binding source editor registrations are aggregated"
185
+ : `Missing editor imports for: ${missingImports.map((entry) => entry.slug).join(", ")}`);
186
+ }
187
+ function checkVariationEntrypoint(projectDir, blockSlug) {
188
+ const entryPath = path.join(projectDir, "src", "blocks", blockSlug, "index.tsx");
189
+ if (!fs.existsSync(entryPath)) {
190
+ return createDoctorCheck(`Variation entrypoint ${blockSlug}`, "fail", `Missing ${path.relative(projectDir, entryPath)}`);
191
+ }
192
+ const source = fs.readFileSync(entryPath, "utf8");
193
+ const hasImport = source.includes("./variations");
194
+ const hasCall = source.includes("registerWorkspaceVariations()");
195
+ return createDoctorCheck(`Variation entrypoint ${blockSlug}`, hasImport && hasCall ? "pass" : "fail", hasImport && hasCall
196
+ ? "Variations registration hook is present"
197
+ : "Missing ./variations import or registerWorkspaceVariations() call");
198
+ }
199
+ function checkMigrationWorkspaceHint(workspace, packageJson) {
200
+ const hasMigrationScript = typeof packageJson.scripts?.["migration:doctor"] === "string";
201
+ const migrationConfigRelativePath = path.join("src", "migrations", "config.ts");
202
+ const hasMigrationConfig = fs.existsSync(path.join(workspace.projectDir, migrationConfigRelativePath));
203
+ if (!hasMigrationScript && !hasMigrationConfig) {
204
+ return null;
205
+ }
206
+ return createDoctorCheck("Migration workspace", hasMigrationConfig ? "pass" : "fail", hasMigrationConfig
207
+ ? "Run `wp-typia migrate doctor --all` for migration target, snapshot, fixture, and generated artifact checks"
208
+ : `Missing ${migrationConfigRelativePath} for the configured migration workspace`);
209
+ }
210
+ /**
211
+ * Collect workspace-scoped doctor checks for the given working directory.
212
+ *
213
+ * When the directory is not an official workspace, the function returns a
214
+ * "Doctor scope" row explaining that only environment checks ran, plus a
215
+ * failing workspace metadata row when a nearby candidate workspace is invalid.
216
+ * When workspace resolution or metadata parsing throws, the corresponding
217
+ * failing rows are returned early and the remaining checks are skipped.
218
+ * When an official workspace is detected, a passing "Doctor scope" row is
219
+ * emitted first so the remaining package metadata, inventory, source-tree
220
+ * drift, and optional migration hint rows are clearly framed as workspace
221
+ * diagnostics for that run.
222
+ *
223
+ * @param cwd Working directory expected to host an official workspace.
224
+ * @returns Ordered workspace check rows ready for CLI rendering.
225
+ */
226
+ export function getWorkspaceDoctorChecks(cwd) {
227
+ const checks = [];
228
+ let workspace = null;
229
+ let invalidWorkspaceReason = null;
230
+ try {
231
+ invalidWorkspaceReason = getInvalidWorkspaceProjectReason(cwd);
232
+ workspace = tryResolveWorkspaceProject(cwd);
233
+ }
234
+ catch (error) {
235
+ checks.push(createDoctorScopeCheck("fail", "Environment checks ran, but workspace discovery could not continue. Fix the nearby workspace package metadata and rerun `wp-typia doctor`."));
236
+ checks.push(createDoctorCheck("Workspace package metadata", "fail", error instanceof Error ? error.message : String(error)));
237
+ return checks;
238
+ }
239
+ if (!workspace) {
240
+ if (invalidWorkspaceReason) {
241
+ checks.push(createDoctorScopeCheck("fail", "Environment checks ran, but workspace diagnostics could not continue because a nearby wp-typia workspace candidate is invalid. Fix the workspace package metadata and rerun `wp-typia doctor`."));
242
+ checks.push(createDoctorCheck("Workspace package metadata", "fail", invalidWorkspaceReason));
243
+ }
244
+ else {
245
+ checks.push(createDoctorScopeCheck("pass", "No official wp-typia workspace root was detected, so this run only covered environment readiness. Re-run `wp-typia doctor` from a workspace root if you expected package metadata, inventory, or generated artifact checks."));
246
+ }
247
+ return checks;
248
+ }
249
+ checks.push(createDoctorScopeCheck("pass", `Official workspace detected for ${workspace.workspace.namespace}; environment readiness checks ran and workspace-scoped diagnostics are enabled for the package metadata, inventory, source-tree drift, and any configured migration hint rows below.`));
250
+ let workspacePackageJson;
251
+ try {
252
+ workspacePackageJson = parseWorkspacePackageJson(workspace.projectDir);
253
+ }
254
+ catch (error) {
255
+ checks.push(createDoctorCheck("Workspace package metadata", "fail", error instanceof Error ? error.message : String(error)));
256
+ return checks;
257
+ }
258
+ checks.push(checkWorkspacePackageMetadata(workspace, workspacePackageJson));
259
+ try {
260
+ const inventory = readWorkspaceInventory(workspace.projectDir);
261
+ 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)`));
262
+ for (const block of inventory.blocks) {
263
+ checks.push(checkExistingFiles(workspace.projectDir, `Block ${block.slug}`, getWorkspaceBlockRequiredFiles(block)));
264
+ checks.push(checkWorkspaceBlockMetadata(workspace.projectDir, workspace, block));
265
+ checks.push(checkWorkspaceBlockHooks(workspace.projectDir, block.slug));
266
+ checks.push(checkWorkspaceBlockCollectionImport(workspace.projectDir, block.slug));
267
+ }
268
+ const registeredBlockSlugs = new Set(inventory.blocks.map((block) => block.slug));
269
+ const variationTargetBlocks = new Set();
270
+ for (const variation of inventory.variations) {
271
+ if (!registeredBlockSlugs.has(variation.block)) {
272
+ checks.push(createDoctorCheck(`Variation ${variation.block}/${variation.slug}`, "fail", `Variation references unknown block "${variation.block}"`));
273
+ continue;
274
+ }
275
+ variationTargetBlocks.add(variation.block);
276
+ checks.push(checkExistingFiles(workspace.projectDir, `Variation ${variation.block}/${variation.slug}`, [variation.file]));
277
+ }
278
+ for (const blockSlug of variationTargetBlocks) {
279
+ checks.push(checkVariationEntrypoint(workspace.projectDir, blockSlug));
280
+ }
281
+ const shouldCheckPatternBootstrap = inventory.patterns.length > 0 ||
282
+ fs.existsSync(path.join(workspace.projectDir, "src", "patterns"));
283
+ if (shouldCheckPatternBootstrap) {
284
+ checks.push(checkWorkspacePatternBootstrap(workspace.projectDir, workspace.packageName));
285
+ }
286
+ for (const pattern of inventory.patterns) {
287
+ checks.push(checkExistingFiles(workspace.projectDir, `Pattern ${pattern.slug}`, [pattern.file]));
288
+ }
289
+ if (inventory.bindingSources.length > 0) {
290
+ checks.push(checkWorkspaceBindingBootstrap(workspace.projectDir, workspace.packageName));
291
+ checks.push(checkWorkspaceBindingSourcesIndex(workspace.projectDir, inventory.bindingSources));
292
+ }
293
+ for (const bindingSource of inventory.bindingSources) {
294
+ checks.push(checkExistingFiles(workspace.projectDir, `Binding source ${bindingSource.slug}`, [
295
+ bindingSource.serverFile,
296
+ bindingSource.editorFile,
297
+ ]));
298
+ }
299
+ const migrationWorkspaceCheck = checkMigrationWorkspaceHint(workspace, workspacePackageJson);
300
+ if (migrationWorkspaceCheck) {
301
+ checks.push(migrationWorkspaceCheck);
302
+ }
303
+ }
304
+ catch (error) {
305
+ checks.push(createDoctorCheck("Workspace inventory", "fail", error instanceof Error ? error.message : String(error)));
306
+ }
307
+ return checks;
308
+ }
@@ -16,8 +16,10 @@ interface RunDoctorOptions {
16
16
  /**
17
17
  * Collect all runtime doctor checks for the current environment.
18
18
  *
19
- * The returned array includes command availability checks, directory
20
- * writability checks, and built-in template asset checks in display order.
19
+ * The returned array concatenates environment checks (command availability,
20
+ * directory writability, and built-in template assets) followed by
21
+ * workspace checks (package metadata, inventory, blocks, variations,
22
+ * patterns, bindings, and optional migration hints) in display order.
21
23
  *
22
24
  * @param cwd Working directory to validate for writability.
23
25
  * @returns Ordered doctor check rows ready for CLI rendering.