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