wp-typia 0.23.0 → 0.24.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/README.md +3 -1
- package/bin/routing-metadata.generated.js +11 -0
- package/dist-bunli/.bunli/commands.gen.js +11884 -9017
- package/dist-bunli/{cli-9npd9was.js → cli-0v407aag.js} +12 -10
- package/dist-bunli/{cli-hhp1d348.js → cli-1170yyve.js} +8 -7
- package/dist-bunli/{cli-1meywwsy.js → cli-74y6z3yx.js} +1455 -819
- package/dist-bunli/{cli-qse6myha.js → cli-8hxf9qw6.js} +11 -3
- package/dist-bunli/{cli-8reep89s.js → cli-9fx0qgb7.js} +2 -2
- package/dist-bunli/{cli-add-21bvpfgw.js → cli-add-nmdraf20.js} +8542 -7667
- package/dist-bunli/{cli-52ke0ptp.js → cli-am5x7tb4.js} +8 -2
- package/dist-bunli/{cli-43mx1vfb.js → cli-bajwv85z.js} +2 -1
- package/dist-bunli/cli-ccax7s0s.js +34 -0
- package/dist-bunli/{cli-z5qkx2pn.js → cli-cwjdzq6n.js} +79 -13
- package/dist-bunli/{cli-diagnostics-5dvztm7q.js → cli-diagnostics-10drxh34.js} +1 -1
- package/dist-bunli/{cli-doctor-wy2yjsge.js → cli-doctor-pcss6ecx.js} +688 -459
- package/dist-bunli/{cli-2rqf6t0b.js → cli-e4bwd81c.js} +8 -11
- package/dist-bunli/{cli-init-xnsbxncv.js → cli-init-he7vm7kc.js} +15 -11
- package/dist-bunli/{cli-prompt-614tq57c.js → cli-prompt-ncyg68rn.js} +1 -1
- package/dist-bunli/{cli-bq2v559b.js → cli-rdcga1bd.js} +31 -13
- package/dist-bunli/{cli-scaffold-zhp2ym8z.js → cli-scaffold-an2k0fnm.js} +28 -16
- package/dist-bunli/{cli-c2acv5dv.js → cli-sw06c521.js} +2 -2
- package/dist-bunli/{cli-templates-hc71dfc2.js → cli-templates-g8t4fm11.js} +3 -2
- package/dist-bunli/{cli-p95wr1q8.js → cli-tq730sqt.js} +6 -3
- package/dist-bunli/{cli-ts9thts5.js → cli-v0nnagb3.js} +1513 -1053
- package/dist-bunli/{cli-agywa5n6.js → cli-y0a8nztv.js} +15 -6
- package/dist-bunli/cli-z48frc8t.js +229 -0
- package/dist-bunli/cli.js +5 -5
- package/dist-bunli/{command-list-aqrkx021.js → command-list-xaw5agks.js} +241 -64
- package/dist-bunli/{create-template-validation-rtec5sng.js → create-template-validation-4fr851vg.js} +5 -4
- package/dist-bunli/{migrations-bx0yvc2v.js → migrations-z7f4kxba.js} +10 -9
- package/dist-bunli/node-cli.js +661 -389
- package/dist-bunli/{workspace-project-csnnggz6.js → workspace-project-gmv2a71z.js} +4 -3
- package/package.json +2 -2
- package/dist-bunli/cli-j8et6jvr.js +0 -123
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
|
+
assertPostMetaBindingPath,
|
|
3
4
|
hasAdminViewManualSettingsRouteParameters,
|
|
4
5
|
hasExecutablePattern,
|
|
5
6
|
hasUncommentedPattern,
|
|
6
7
|
isAdminViewManualSettingsRestResource,
|
|
8
|
+
loadPostMetaBindingFieldsSync,
|
|
7
9
|
maskTypeScriptCommentsAndLiterals
|
|
8
|
-
} from "./cli-
|
|
10
|
+
} from "./cli-z48frc8t.js";
|
|
9
11
|
import {
|
|
10
12
|
getBuiltInTemplateLayerDirs,
|
|
11
13
|
isOmittableBuiltInTemplateLayerDir
|
|
12
|
-
} from "./cli-
|
|
14
|
+
} from "./cli-sw06c521.js";
|
|
13
15
|
import {
|
|
14
16
|
isBuiltInTemplateId,
|
|
15
17
|
listTemplates
|
|
16
|
-
} from "./cli-
|
|
18
|
+
} from "./cli-8hxf9qw6.js";
|
|
17
19
|
import {
|
|
18
20
|
EDITOR_PLUGIN_SLOT_IDS,
|
|
19
21
|
HOOKED_BLOCK_ANCHOR_PATTERN,
|
|
@@ -24,28 +26,33 @@ import {
|
|
|
24
26
|
REST_RESOURCE_NAMESPACE_PATTERN,
|
|
25
27
|
assertValidPostMetaPostType,
|
|
26
28
|
escapeRegex,
|
|
29
|
+
formatPatternCatalogDiagnostics,
|
|
27
30
|
isGeneratedRestResourceRoutePatternCompatible,
|
|
28
31
|
pathExists,
|
|
29
32
|
readWorkspaceInventoryAsync,
|
|
30
|
-
resolveEditorPluginSlotAlias
|
|
31
|
-
|
|
33
|
+
resolveEditorPluginSlotAlias,
|
|
34
|
+
resolvePatternCatalogContentFile,
|
|
35
|
+
validatePatternCatalog
|
|
36
|
+
} from "./cli-v0nnagb3.js";
|
|
32
37
|
import"./cli-cvxvcw7c.js";
|
|
33
38
|
import"./cli-t73q5aqz.js";
|
|
34
|
-
import"./cli-
|
|
39
|
+
import"./cli-bajwv85z.js";
|
|
35
40
|
import {
|
|
36
41
|
CLI_DIAGNOSTIC_CODES,
|
|
37
42
|
createCliCommandError,
|
|
38
43
|
formatDoctorCheckLine,
|
|
39
|
-
formatDoctorSummaryLine
|
|
40
|
-
|
|
41
|
-
} from "./cli-p95wr1q8.js";
|
|
44
|
+
formatDoctorSummaryLine
|
|
45
|
+
} from "./cli-tq730sqt.js";
|
|
42
46
|
import {
|
|
43
47
|
WORKSPACE_TEMPLATE_PACKAGE,
|
|
44
48
|
getInvalidWorkspaceProjectReason,
|
|
45
49
|
parseWorkspacePackageJson,
|
|
46
50
|
tryResolveWorkspaceProject
|
|
47
|
-
} from "./cli-
|
|
48
|
-
import"./cli-
|
|
51
|
+
} from "./cli-1170yyve.js";
|
|
52
|
+
import"./cli-am5x7tb4.js";
|
|
53
|
+
import {
|
|
54
|
+
readJsonFileSync
|
|
55
|
+
} from "./cli-ccax7s0s.js";
|
|
49
56
|
import"./cli-xnn9xjcy.js";
|
|
50
57
|
|
|
51
58
|
// ../wp-typia-project-tools/src/runtime/cli-doctor-environment.ts
|
|
@@ -229,7 +236,9 @@ function checkWorkspaceBindingTarget(projectDir, workspace, registeredBlockSlugs
|
|
|
229
236
|
const blockJsonPath = path3.join(projectDir, blockJsonRelativePath);
|
|
230
237
|
const issues = [];
|
|
231
238
|
try {
|
|
232
|
-
const blockJson = parseScaffoldBlockMetadata(
|
|
239
|
+
const blockJson = parseScaffoldBlockMetadata(readJsonFileSync(blockJsonPath, {
|
|
240
|
+
context: "workspace block metadata"
|
|
241
|
+
}));
|
|
233
242
|
const attributes = blockJson.attributes;
|
|
234
243
|
if (!attributes || typeof attributes !== "object" || Array.isArray(attributes)) {
|
|
235
244
|
issues.push(`${blockJsonRelativePath} must define an attributes object`);
|
|
@@ -278,6 +287,55 @@ function checkWorkspaceBindingTarget(projectDir, workspace, registeredBlockSlugs
|
|
|
278
287
|
}
|
|
279
288
|
return createDoctorCheck(`Binding target ${bindingSource.slug}`, issues.length === 0 ? "pass" : "fail", issues.length === 0 ? `${bindingSource.block}.${bindingSource.attribute} is declared and supported` : issues.join("; "));
|
|
280
289
|
}
|
|
290
|
+
function checkWorkspaceBindingPostMeta(projectDir, inventory, bindingSource) {
|
|
291
|
+
if (!bindingSource.postMeta) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const postMeta = inventory.postMeta.find((entry) => entry.slug === bindingSource.postMeta);
|
|
295
|
+
if (!postMeta) {
|
|
296
|
+
return createDoctorCheck(`Binding post meta ${bindingSource.slug}`, "fail", `Binding source references unknown post meta contract "${bindingSource.postMeta}".`);
|
|
297
|
+
}
|
|
298
|
+
const issues = [];
|
|
299
|
+
try {
|
|
300
|
+
const fields = loadPostMetaBindingFieldsSync(projectDir, postMeta);
|
|
301
|
+
if (bindingSource.metaPath) {
|
|
302
|
+
assertPostMetaBindingPath(fields, postMeta.slug, bindingSource.metaPath);
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
issues.push(error instanceof Error ? error.message : String(error));
|
|
306
|
+
}
|
|
307
|
+
const serverPath = path3.join(projectDir, bindingSource.serverFile);
|
|
308
|
+
if (fs3.existsSync(serverPath)) {
|
|
309
|
+
const serverSource = fs3.readFileSync(serverPath, "utf8");
|
|
310
|
+
if (!serverSource.includes("get_post_meta")) {
|
|
311
|
+
issues.push(`${bindingSource.serverFile} must read post meta values`);
|
|
312
|
+
}
|
|
313
|
+
if (!serverSource.includes(postMeta.metaKey)) {
|
|
314
|
+
issues.push(`${bindingSource.serverFile} must reference ${postMeta.metaKey}`);
|
|
315
|
+
}
|
|
316
|
+
if (!serverSource.includes(postMeta.schemaFile)) {
|
|
317
|
+
issues.push(`${bindingSource.serverFile} must reference ${postMeta.schemaFile}`);
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
issues.push(`Missing ${bindingSource.serverFile}`);
|
|
321
|
+
}
|
|
322
|
+
const editorPath = path3.join(projectDir, bindingSource.editorFile);
|
|
323
|
+
if (fs3.existsSync(editorPath)) {
|
|
324
|
+
const editorSource = fs3.readFileSync(editorPath, "utf8");
|
|
325
|
+
if (!editorSource.includes("POST_META_BINDING_FIELDS")) {
|
|
326
|
+
issues.push(`${bindingSource.editorFile} must define post meta binding fields`);
|
|
327
|
+
}
|
|
328
|
+
if (!editorSource.includes(postMeta.schemaFile)) {
|
|
329
|
+
issues.push(`${bindingSource.editorFile} must reference ${postMeta.schemaFile}`);
|
|
330
|
+
}
|
|
331
|
+
if (bindingSource.metaPath && !editorSource.includes(bindingSource.metaPath)) {
|
|
332
|
+
issues.push(`${bindingSource.editorFile} must reference default meta path "${bindingSource.metaPath}"`);
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
issues.push(`Missing ${bindingSource.editorFile}`);
|
|
336
|
+
}
|
|
337
|
+
return createDoctorCheck(`Binding post meta ${bindingSource.slug}`, issues.length === 0 ? "pass" : "fail", issues.length === 0 ? `${bindingSource.slug} reads ${postMeta.slug} via ${postMeta.schemaFile}` : issues.join("; "));
|
|
338
|
+
}
|
|
281
339
|
function getWorkspaceBindingDoctorChecks(workspace, inventory) {
|
|
282
340
|
const checks = [];
|
|
283
341
|
if (inventory.bindingSources.length > 0) {
|
|
@@ -294,22 +352,151 @@ function getWorkspaceBindingDoctorChecks(workspace, inventory) {
|
|
|
294
352
|
if (bindingTargetCheck) {
|
|
295
353
|
checks.push(bindingTargetCheck);
|
|
296
354
|
}
|
|
355
|
+
const bindingPostMetaCheck = checkWorkspaceBindingPostMeta(workspace.projectDir, inventory, bindingSource);
|
|
356
|
+
if (bindingPostMetaCheck) {
|
|
357
|
+
checks.push(bindingPostMetaCheck);
|
|
358
|
+
}
|
|
297
359
|
}
|
|
298
360
|
return checks;
|
|
299
361
|
}
|
|
300
362
|
|
|
301
|
-
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-
|
|
363
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-block-addons.ts
|
|
302
364
|
import fs4 from "fs";
|
|
303
365
|
import path4 from "path";
|
|
304
|
-
import { parseScaffoldBlockMetadata as parseScaffoldBlockMetadata2 } from "@wp-typia/block-runtime/blocks";
|
|
305
|
-
var WORKSPACE_COLLECTION_IMPORT_LINE = "import '../../collection';";
|
|
306
|
-
var WORKSPACE_COLLECTION_IMPORT_PATTERN = /^\s*import\s+["']\.\.\/\.\.\/collection["']\s*;?\s*$/m;
|
|
307
366
|
var WORKSPACE_VARIATIONS_IMPORT_PATTERN = /^\s*import\s*\{\s*registerWorkspaceVariations\s*\}\s*from\s*["']\.\/variations["']\s*;?\s*$/mu;
|
|
308
367
|
var WORKSPACE_VARIATIONS_CALL_PATTERN = /registerWorkspaceVariations\s*\(\s*\)\s*;?/u;
|
|
309
368
|
var WORKSPACE_BLOCK_STYLES_IMPORT_PATTERN = /^\s*import\s*\{\s*registerWorkspaceBlockStyles\s*\}\s*from\s*["']\.\/styles["']\s*;?\s*$/mu;
|
|
310
369
|
var WORKSPACE_BLOCK_STYLES_CALL_PATTERN = /registerWorkspaceBlockStyles\s*\(\s*\)\s*;?/u;
|
|
311
370
|
var WORKSPACE_BLOCK_TRANSFORMS_IMPORT_PATTERN = /^\s*import\s*\{\s*applyWorkspaceBlockTransforms\s*\}\s*from\s*["']\.\/transforms["']\s*;?\s*$/mu;
|
|
312
371
|
var WORKSPACE_BLOCK_TRANSFORMS_CALL_PATTERN = /applyWorkspaceBlockTransforms\s*\(\s*registration\s*\.\s*settings\s*\)\s*;?/u;
|
|
372
|
+
function isNestedPatternContentFile(patternFile) {
|
|
373
|
+
if (!patternFile) {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
const normalizedPath = patternFile.replace(/\\/gu, "/");
|
|
377
|
+
return normalizedPath.startsWith("src/patterns/") && normalizedPath.slice("src/patterns/".length).includes("/");
|
|
378
|
+
}
|
|
379
|
+
function checkWorkspacePatternBootstrap(projectDir, packageName, requiresNestedPatternGlob) {
|
|
380
|
+
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
381
|
+
if (!fs4.existsSync(bootstrapPath)) {
|
|
382
|
+
return createDoctorCheck("Pattern bootstrap", "fail", `Missing ${path4.basename(bootstrapPath)}`);
|
|
383
|
+
}
|
|
384
|
+
const source = fs4.readFileSync(bootstrapPath, "utf8");
|
|
385
|
+
const hasCategoryAnchor = source.includes("register_block_pattern_category");
|
|
386
|
+
const hasPatternGlob = source.includes("/src/patterns/*.php");
|
|
387
|
+
const hasNestedPatternGlob = source.includes("/src/patterns/*/*.php");
|
|
388
|
+
const hasRequiredPatternGlobs = hasPatternGlob && (!requiresNestedPatternGlob || hasNestedPatternGlob);
|
|
389
|
+
return createDoctorCheck("Pattern bootstrap", hasCategoryAnchor && hasRequiredPatternGlobs ? "pass" : "fail", hasCategoryAnchor && hasRequiredPatternGlobs ? "Pattern category and loader hooks are present" : requiresNestedPatternGlob ? "Missing pattern category registration or nested src/patterns loader hook" : "Missing pattern category registration or src/patterns loader hook");
|
|
390
|
+
}
|
|
391
|
+
function checkVariationEntrypoint(projectDir, blockSlug) {
|
|
392
|
+
const entryPath = path4.join(projectDir, "src", "blocks", blockSlug, "index.tsx");
|
|
393
|
+
if (!fs4.existsSync(entryPath)) {
|
|
394
|
+
return createDoctorCheck(`Variation entrypoint ${blockSlug}`, "fail", `Missing ${path4.relative(projectDir, entryPath)}`);
|
|
395
|
+
}
|
|
396
|
+
const source = fs4.readFileSync(entryPath, "utf8");
|
|
397
|
+
const hasImport = hasUncommentedPattern(source, WORKSPACE_VARIATIONS_IMPORT_PATTERN);
|
|
398
|
+
const hasCall = hasExecutablePattern(source, WORKSPACE_VARIATIONS_CALL_PATTERN);
|
|
399
|
+
return createDoctorCheck(`Variation entrypoint ${blockSlug}`, hasImport && hasCall ? "pass" : "fail", hasImport && hasCall ? "Variations registration hook is present" : "Missing ./variations import or registerWorkspaceVariations() call");
|
|
400
|
+
}
|
|
401
|
+
function checkBlockStyleEntrypoint(projectDir, blockSlug) {
|
|
402
|
+
const entryPath = path4.join(projectDir, "src", "blocks", blockSlug, "index.tsx");
|
|
403
|
+
if (!fs4.existsSync(entryPath)) {
|
|
404
|
+
return createDoctorCheck(`Block style entrypoint ${blockSlug}`, "fail", `Missing ${path4.relative(projectDir, entryPath)}`);
|
|
405
|
+
}
|
|
406
|
+
const source = fs4.readFileSync(entryPath, "utf8");
|
|
407
|
+
const hasImport = hasUncommentedPattern(source, WORKSPACE_BLOCK_STYLES_IMPORT_PATTERN);
|
|
408
|
+
const hasCall = hasExecutablePattern(source, WORKSPACE_BLOCK_STYLES_CALL_PATTERN);
|
|
409
|
+
return createDoctorCheck(`Block style entrypoint ${blockSlug}`, hasImport && hasCall ? "pass" : "fail", hasImport && hasCall ? "Block style registration hook is present" : "Missing ./styles import or registerWorkspaceBlockStyles() call");
|
|
410
|
+
}
|
|
411
|
+
function checkBlockTransformEntrypoint(projectDir, blockSlug) {
|
|
412
|
+
const entryPath = path4.join(projectDir, "src", "blocks", blockSlug, "index.tsx");
|
|
413
|
+
if (!fs4.existsSync(entryPath)) {
|
|
414
|
+
return createDoctorCheck(`Block transform entrypoint ${blockSlug}`, "fail", `Missing ${path4.relative(projectDir, entryPath)}`);
|
|
415
|
+
}
|
|
416
|
+
const source = fs4.readFileSync(entryPath, "utf8");
|
|
417
|
+
const hasImport = hasUncommentedPattern(source, WORKSPACE_BLOCK_TRANSFORMS_IMPORT_PATTERN);
|
|
418
|
+
const hasCall = hasExecutablePattern(source, WORKSPACE_BLOCK_TRANSFORMS_CALL_PATTERN);
|
|
419
|
+
return createDoctorCheck(`Block transform entrypoint ${blockSlug}`, hasImport && hasCall ? "pass" : "fail", hasImport && hasCall ? "Block transform registration hook is present" : "Missing ./transforms import or applyWorkspaceBlockTransforms(registration.settings) call");
|
|
420
|
+
}
|
|
421
|
+
function checkBlockTransformConfig(workspace, transform) {
|
|
422
|
+
const expectedTo = `${workspace.workspace.namespace}/${transform.block}`;
|
|
423
|
+
const issues = [];
|
|
424
|
+
if (!WORKSPACE_FULL_BLOCK_NAME_PATTERN.test(transform.from)) {
|
|
425
|
+
issues.push("from must use full namespace/block format");
|
|
426
|
+
}
|
|
427
|
+
if (transform.to !== expectedTo) {
|
|
428
|
+
issues.push(`to must equal "${expectedTo}" for workspace block "${transform.block}"`);
|
|
429
|
+
}
|
|
430
|
+
return createDoctorCheck(`Block transform config ${transform.block}/${transform.slug}`, issues.length === 0 ? "pass" : "fail", issues.length === 0 ? `${transform.from} transforms into ${transform.to}` : issues.join("; "));
|
|
431
|
+
}
|
|
432
|
+
function getWorkspaceBlockAddonDoctorChecks(workspace, inventory, registeredBlockSlugs) {
|
|
433
|
+
const checks = [];
|
|
434
|
+
const variationTargetBlocks = new Set;
|
|
435
|
+
for (const variation of inventory.variations) {
|
|
436
|
+
if (!registeredBlockSlugs.has(variation.block)) {
|
|
437
|
+
checks.push(createDoctorCheck(`Variation ${variation.block}/${variation.slug}`, "fail", `Variation references unknown block "${variation.block}"`));
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
variationTargetBlocks.add(variation.block);
|
|
441
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Variation ${variation.block}/${variation.slug}`, [variation.file]));
|
|
442
|
+
}
|
|
443
|
+
for (const blockSlug of variationTargetBlocks) {
|
|
444
|
+
checks.push(checkVariationEntrypoint(workspace.projectDir, blockSlug));
|
|
445
|
+
}
|
|
446
|
+
const blockStyleTargetBlocks = new Set;
|
|
447
|
+
for (const blockStyle of inventory.blockStyles) {
|
|
448
|
+
if (!registeredBlockSlugs.has(blockStyle.block)) {
|
|
449
|
+
checks.push(createDoctorCheck(`Block style ${blockStyle.block}/${blockStyle.slug}`, "fail", `Block style references unknown block "${blockStyle.block}"`));
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
blockStyleTargetBlocks.add(blockStyle.block);
|
|
453
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Block style ${blockStyle.block}/${blockStyle.slug}`, [blockStyle.file]));
|
|
454
|
+
}
|
|
455
|
+
for (const blockSlug of blockStyleTargetBlocks) {
|
|
456
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Block style registry ${blockSlug}`, [
|
|
457
|
+
path4.join("src", "blocks", blockSlug, "styles", "index.ts")
|
|
458
|
+
]));
|
|
459
|
+
checks.push(checkBlockStyleEntrypoint(workspace.projectDir, blockSlug));
|
|
460
|
+
}
|
|
461
|
+
const blockTransformTargetBlocks = new Set;
|
|
462
|
+
for (const blockTransform of inventory.blockTransforms) {
|
|
463
|
+
if (!registeredBlockSlugs.has(blockTransform.block)) {
|
|
464
|
+
checks.push(createDoctorCheck(`Block transform ${blockTransform.block}/${blockTransform.slug}`, "fail", `Block transform references unknown block "${blockTransform.block}"`));
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
blockTransformTargetBlocks.add(blockTransform.block);
|
|
468
|
+
checks.push(checkBlockTransformConfig(workspace, blockTransform));
|
|
469
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Block transform ${blockTransform.block}/${blockTransform.slug}`, [blockTransform.file]));
|
|
470
|
+
}
|
|
471
|
+
for (const blockSlug of blockTransformTargetBlocks) {
|
|
472
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Block transform registry ${blockSlug}`, [
|
|
473
|
+
path4.join("src", "blocks", blockSlug, "transforms", "index.ts")
|
|
474
|
+
]));
|
|
475
|
+
checks.push(checkBlockTransformEntrypoint(workspace.projectDir, blockSlug));
|
|
476
|
+
}
|
|
477
|
+
const shouldCheckPatternBootstrap = inventory.patterns.length > 0 || fs4.existsSync(path4.join(workspace.projectDir, "src", "patterns"));
|
|
478
|
+
if (shouldCheckPatternBootstrap) {
|
|
479
|
+
const requiresNestedPatternGlob = inventory.patterns.some((pattern) => isNestedPatternContentFile(resolvePatternCatalogContentFile(pattern)));
|
|
480
|
+
checks.push(checkWorkspacePatternBootstrap(workspace.projectDir, workspace.packageName, requiresNestedPatternGlob));
|
|
481
|
+
}
|
|
482
|
+
if (inventory.patterns.length > 0) {
|
|
483
|
+
const catalogValidation = validatePatternCatalog(inventory.patterns, {
|
|
484
|
+
projectDir: workspace.projectDir
|
|
485
|
+
});
|
|
486
|
+
checks.push(createDoctorCheck("Pattern catalog", catalogValidation.errors.length > 0 ? "fail" : catalogValidation.warnings.length > 0 ? "warn" : "pass", catalogValidation.diagnostics.length > 0 ? formatPatternCatalogDiagnostics(catalogValidation.diagnostics) : "Pattern catalog metadata is valid"));
|
|
487
|
+
}
|
|
488
|
+
for (const pattern of inventory.patterns) {
|
|
489
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Pattern ${pattern.slug}`, [
|
|
490
|
+
resolvePatternCatalogContentFile(pattern)
|
|
491
|
+
]));
|
|
492
|
+
}
|
|
493
|
+
return checks;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-block-iframe.ts
|
|
497
|
+
import fs5 from "fs";
|
|
498
|
+
import path5 from "path";
|
|
499
|
+
import { parseScaffoldBlockMetadata as parseScaffoldBlockMetadata2 } from "@wp-typia/block-runtime/blocks";
|
|
313
500
|
var WORKSPACE_BLOCK_IFRAME_COMPATIBILITY_DOC_URL = "https://developer.wordpress.org/block-editor/reference-guides/block-api/block-api-versions/block-migration-for-iframe-editor-compatibility/";
|
|
314
501
|
var WORKSPACE_BLOCK_IFRAME_DIAGNOSTIC_CODES = {
|
|
315
502
|
API_VERSION: "wp-typia.workspace.block.iframe.api-version",
|
|
@@ -340,7 +527,7 @@ var WORKSPACE_BLOCK_LOCAL_STYLE_FILES = [
|
|
|
340
527
|
var WORKSPACE_BLOCK_IFRAME_GLOBAL_DOM_PATTERN = /\b(?:document|window)\b|\b(?:parent|top)\b(?!\s*:)/gu;
|
|
341
528
|
var WORKSPACE_BLOCK_PROPS_PATTERN = /\buse(?:Block|InnerBlocks)Props(?:\.save)?\s*\(/u;
|
|
342
529
|
function normalizePathSeparators(relativePath) {
|
|
343
|
-
return relativePath.split(
|
|
530
|
+
return relativePath.split(path5.sep).join("/");
|
|
344
531
|
}
|
|
345
532
|
function hasRegisteredBlockAsset(value) {
|
|
346
533
|
if (typeof value === "string") {
|
|
@@ -352,16 +539,18 @@ function hasRegisteredBlockAsset(value) {
|
|
|
352
539
|
return false;
|
|
353
540
|
}
|
|
354
541
|
function readWorkspaceBlockIframeMetadata(projectDir, blockSlug) {
|
|
355
|
-
const blockJsonRelativePath =
|
|
356
|
-
const blockJsonPath =
|
|
357
|
-
if (!
|
|
542
|
+
const blockJsonRelativePath = path5.join("src", "blocks", blockSlug, "block.json");
|
|
543
|
+
const blockJsonPath = path5.join(projectDir, blockJsonRelativePath);
|
|
544
|
+
if (!fs5.existsSync(blockJsonPath)) {
|
|
358
545
|
return {
|
|
359
546
|
blockJsonRelativePath,
|
|
360
547
|
error: `Missing ${blockJsonRelativePath}`
|
|
361
548
|
};
|
|
362
549
|
}
|
|
363
550
|
try {
|
|
364
|
-
const document = parseScaffoldBlockMetadata2(
|
|
551
|
+
const document = parseScaffoldBlockMetadata2(readJsonFileSync(blockJsonPath, {
|
|
552
|
+
context: "workspace block metadata"
|
|
553
|
+
}));
|
|
365
554
|
return {
|
|
366
555
|
blockJsonRelativePath,
|
|
367
556
|
document
|
|
@@ -378,8 +567,8 @@ function isWorkspaceBlockEditorSource(relativePath) {
|
|
|
378
567
|
return false;
|
|
379
568
|
}
|
|
380
569
|
const normalizedRelativePath = normalizePathSeparators(relativePath);
|
|
381
|
-
const normalizedDirName =
|
|
382
|
-
const normalizedBaseName =
|
|
570
|
+
const normalizedDirName = path5.posix.dirname(normalizedRelativePath);
|
|
571
|
+
const normalizedBaseName = path5.posix.basename(normalizedRelativePath, path5.posix.extname(normalizedRelativePath));
|
|
383
572
|
if (WORKSPACE_BLOCK_EDITOR_SOURCE_BASENAMES.has(normalizedBaseName)) {
|
|
384
573
|
return true;
|
|
385
574
|
}
|
|
@@ -387,12 +576,12 @@ function isWorkspaceBlockEditorSource(relativePath) {
|
|
|
387
576
|
return pathSegments.some((segment) => WORKSPACE_BLOCK_EDITOR_SOURCE_DIRECTORIES.has(segment));
|
|
388
577
|
}
|
|
389
578
|
function isWorkspaceBlockSaveSource(relativePath) {
|
|
390
|
-
const normalizedBaseName =
|
|
579
|
+
const normalizedBaseName = path5.basename(relativePath, path5.extname(relativePath));
|
|
391
580
|
return normalizedBaseName === "save";
|
|
392
581
|
}
|
|
393
582
|
function collectWorkspaceBlockEditorSources(projectDir, blockSlug) {
|
|
394
|
-
const blockDir =
|
|
395
|
-
if (!
|
|
583
|
+
const blockDir = path5.join(projectDir, "src", "blocks", blockSlug);
|
|
584
|
+
if (!fs5.existsSync(blockDir)) {
|
|
396
585
|
return [];
|
|
397
586
|
}
|
|
398
587
|
const collected = [];
|
|
@@ -402,8 +591,8 @@ function collectWorkspaceBlockEditorSources(projectDir, blockSlug) {
|
|
|
402
591
|
if (!currentDir) {
|
|
403
592
|
continue;
|
|
404
593
|
}
|
|
405
|
-
for (const entry of
|
|
406
|
-
const absolutePath =
|
|
594
|
+
for (const entry of fs5.readdirSync(currentDir, { withFileTypes: true })) {
|
|
595
|
+
const absolutePath = path5.join(currentDir, entry.name);
|
|
407
596
|
if (entry.isDirectory()) {
|
|
408
597
|
queue.push(absolutePath);
|
|
409
598
|
continue;
|
|
@@ -411,13 +600,13 @@ function collectWorkspaceBlockEditorSources(projectDir, blockSlug) {
|
|
|
411
600
|
if (!entry.isFile()) {
|
|
412
601
|
continue;
|
|
413
602
|
}
|
|
414
|
-
const relativePath =
|
|
603
|
+
const relativePath = path5.relative(projectDir, absolutePath);
|
|
415
604
|
if (!isWorkspaceBlockEditorSource(relativePath)) {
|
|
416
605
|
continue;
|
|
417
606
|
}
|
|
418
607
|
collected.push({
|
|
419
608
|
relativePath: normalizePathSeparators(relativePath),
|
|
420
|
-
source:
|
|
609
|
+
source: fs5.readFileSync(absolutePath, "utf8")
|
|
421
610
|
});
|
|
422
611
|
}
|
|
423
612
|
}
|
|
@@ -475,25 +664,60 @@ function findWorkspaceBlockGlobalDomAccesses(editorSources) {
|
|
|
475
664
|
return findings;
|
|
476
665
|
});
|
|
477
666
|
}
|
|
667
|
+
function getWorkspaceBlockIframeCompatibilityChecks(projectDir, blockSlug) {
|
|
668
|
+
const metadataResult = readWorkspaceBlockIframeMetadata(projectDir, blockSlug);
|
|
669
|
+
if (!metadataResult.document) {
|
|
670
|
+
return [
|
|
671
|
+
createDoctorCheck(`Block iframe/API v3 ${blockSlug}`, "warn", metadataResult.error ?? `Unable to inspect ${metadataResult.blockJsonRelativePath}`, WORKSPACE_BLOCK_IFRAME_DIAGNOSTIC_CODES.API_VERSION)
|
|
672
|
+
];
|
|
673
|
+
}
|
|
674
|
+
const blockJson = metadataResult.document;
|
|
675
|
+
const apiVersion = typeof blockJson.apiVersion === "number" && Number.isFinite(blockJson.apiVersion) ? blockJson.apiVersion : null;
|
|
676
|
+
const blockDir = path5.join(projectDir, "src", "blocks", blockSlug);
|
|
677
|
+
const localStyleFiles = WORKSPACE_BLOCK_LOCAL_STYLE_FILES.filter((fileName) => fs5.existsSync(path5.join(blockDir, fileName))).map((fileName) => normalizePathSeparators(path5.join("src", "blocks", blockSlug, fileName)));
|
|
678
|
+
const hasRegisteredEditorStyles = hasRegisteredBlockAsset(blockJson.style) || hasRegisteredBlockAsset(blockJson.editorStyle);
|
|
679
|
+
const editorSources = collectWorkspaceBlockEditorSources(projectDir, blockSlug);
|
|
680
|
+
const editorWrapperSources = editorSources.filter((source) => !isWorkspaceBlockSaveSource(source.relativePath));
|
|
681
|
+
const globalDomAccesses = findWorkspaceBlockGlobalDomAccesses(editorSources);
|
|
682
|
+
const hasBlockPropsUsage = editorSources.some(({ source }) => hasExecutablePattern(source, WORKSPACE_BLOCK_PROPS_PATTERN));
|
|
683
|
+
const hasEditorBlockPropsUsage = editorWrapperSources.some(({ source }) => hasExecutablePattern(source, WORKSPACE_BLOCK_PROPS_PATTERN));
|
|
684
|
+
const blockWrapperStatus = editorWrapperSources.length === 0 || hasEditorBlockPropsUsage ? "pass" : "warn";
|
|
685
|
+
const blockWrapperDetail = editorSources.length === 0 ? "No editor-facing block source files found; general file checks will report missing entrypoints" : editorWrapperSources.length === 0 ? "No editor wrapper source files found; general file checks will report missing entrypoints" : hasEditorBlockPropsUsage ? "Editor-facing sources use block wrapper props" : hasBlockPropsUsage ? "Only save-facing useBlockProps.save() usage was detected. Confirm the editor wrapper also receives useBlockProps() or useInnerBlocksProps() before relying on iframe editor rendering." : "No useBlockProps(), useBlockProps.save(), or useInnerBlocksProps() usage was detected in editor-facing sources. Confirm the block wrapper receives WordPress block editor props before relying on iframe editor rendering.";
|
|
686
|
+
return [
|
|
687
|
+
createDoctorCheck(`Block iframe API version ${blockSlug}`, apiVersion !== null && apiVersion >= 3 ? "pass" : "warn", apiVersion !== null && apiVersion >= 3 ? "block.json declares apiVersion 3 for iframe editor readiness" : `Set ${metadataResult.blockJsonRelativePath} apiVersion to 3 after testing the block in iframe-enabled Post Editor and Site Editor contexts. WordPress recommends API v3 for iframe editor compatibility. See ${WORKSPACE_BLOCK_IFRAME_COMPATIBILITY_DOC_URL}`, WORKSPACE_BLOCK_IFRAME_DIAGNOSTIC_CODES.API_VERSION),
|
|
688
|
+
createDoctorCheck(`Block iframe styles ${blockSlug}`, localStyleFiles.length === 0 || hasRegisteredEditorStyles ? "pass" : "warn", localStyleFiles.length === 0 ? "No local block stylesheet source files found to register" : hasRegisteredEditorStyles ? "block.json registers block styles for iframe editor loading" : `Found stylesheet source files (${localStyleFiles.join(", ")}) but block.json does not declare style or editorStyle. Register block content styles so iframe editors do not depend on parent admin styles.`, WORKSPACE_BLOCK_IFRAME_DIAGNOSTIC_CODES.EDITOR_STYLES),
|
|
689
|
+
createDoctorCheck(`Block iframe globals ${blockSlug}`, globalDomAccesses.length === 0 ? "pass" : "warn", globalDomAccesses.length === 0 ? "No direct window/document/parent DOM access detected in editor-facing block sources" : `Direct global DOM access detected at ${globalDomAccesses.join(", ")}. Prefer element.ownerDocument/defaultView via refs or useRefEffect for iframe editor content.`, WORKSPACE_BLOCK_IFRAME_DIAGNOSTIC_CODES.EDITOR_GLOBALS),
|
|
690
|
+
createDoctorCheck(`Block iframe wrapper ${blockSlug}`, blockWrapperStatus, blockWrapperDetail, WORKSPACE_BLOCK_IFRAME_DIAGNOSTIC_CODES.BLOCK_PROPS)
|
|
691
|
+
];
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-block-metadata.ts
|
|
695
|
+
import fs6 from "fs";
|
|
696
|
+
import path6 from "path";
|
|
697
|
+
import { parseScaffoldBlockMetadata as parseScaffoldBlockMetadata3 } from "@wp-typia/block-runtime/blocks";
|
|
698
|
+
var WORKSPACE_COLLECTION_IMPORT_LINE = "import '../../collection';";
|
|
699
|
+
var WORKSPACE_COLLECTION_IMPORT_PATTERN = /^\s*import\s+["']\.\.\/\.\.\/collection["']\s*;?\s*$/m;
|
|
478
700
|
function getWorkspaceBlockRequiredFiles(block) {
|
|
479
|
-
const blockDir =
|
|
701
|
+
const blockDir = path6.join("src", "blocks", block.slug);
|
|
480
702
|
return Array.from(new Set([
|
|
481
703
|
block.typesFile,
|
|
482
704
|
block.apiTypesFile,
|
|
483
705
|
block.openApiFile,
|
|
484
|
-
|
|
485
|
-
...WORKSPACE_GENERATED_BLOCK_ARTIFACTS.map((fileName) =>
|
|
706
|
+
path6.join(blockDir, "index.tsx"),
|
|
707
|
+
...WORKSPACE_GENERATED_BLOCK_ARTIFACTS.map((fileName) => path6.join(blockDir, fileName))
|
|
486
708
|
].filter((filePath) => typeof filePath === "string")));
|
|
487
709
|
}
|
|
488
710
|
function checkWorkspaceBlockMetadata(projectDir, workspace, block) {
|
|
489
|
-
const blockJsonRelativePath =
|
|
490
|
-
const blockJsonPath =
|
|
491
|
-
if (!
|
|
711
|
+
const blockJsonRelativePath = path6.join("src", "blocks", block.slug, "block.json");
|
|
712
|
+
const blockJsonPath = path6.join(projectDir, blockJsonRelativePath);
|
|
713
|
+
if (!fs6.existsSync(blockJsonPath)) {
|
|
492
714
|
return createDoctorCheck(`Block metadata ${block.slug}`, "fail", `Missing ${blockJsonRelativePath}`);
|
|
493
715
|
}
|
|
494
716
|
let blockJson;
|
|
495
717
|
try {
|
|
496
|
-
blockJson =
|
|
718
|
+
blockJson = parseScaffoldBlockMetadata3(readJsonFileSync(blockJsonPath, {
|
|
719
|
+
context: "workspace block metadata"
|
|
720
|
+
}));
|
|
497
721
|
} catch (error) {
|
|
498
722
|
return createDoctorCheck(`Block metadata ${block.slug}`, "fail", error instanceof Error ? error.message : String(error));
|
|
499
723
|
}
|
|
@@ -508,14 +732,16 @@ function checkWorkspaceBlockMetadata(projectDir, workspace, block) {
|
|
|
508
732
|
return createDoctorCheck(`Block metadata ${block.slug}`, issues.length === 0 ? "pass" : "fail", issues.length === 0 ? `block.json matches ${expectedName} and ${workspace.workspace.textDomain}` : issues.join("; "));
|
|
509
733
|
}
|
|
510
734
|
function checkWorkspaceBlockHooks(projectDir, blockSlug) {
|
|
511
|
-
const blockJsonRelativePath =
|
|
512
|
-
const blockJsonPath =
|
|
513
|
-
if (!
|
|
735
|
+
const blockJsonRelativePath = path6.join("src", "blocks", blockSlug, "block.json");
|
|
736
|
+
const blockJsonPath = path6.join(projectDir, blockJsonRelativePath);
|
|
737
|
+
if (!fs6.existsSync(blockJsonPath)) {
|
|
514
738
|
return createDoctorCheck(`Block hooks ${blockSlug}`, "fail", `Missing ${blockJsonRelativePath}`);
|
|
515
739
|
}
|
|
516
740
|
let blockJson;
|
|
517
741
|
try {
|
|
518
|
-
blockJson =
|
|
742
|
+
blockJson = parseScaffoldBlockMetadata3(readJsonFileSync(blockJsonPath, {
|
|
743
|
+
context: "workspace block metadata"
|
|
744
|
+
}));
|
|
519
745
|
} catch (error) {
|
|
520
746
|
return createDoctorCheck(`Block hooks ${blockSlug}`, "fail", error instanceof Error ? error.message : String(error));
|
|
521
747
|
}
|
|
@@ -531,311 +757,74 @@ function checkWorkspaceBlockHooks(projectDir, blockSlug) {
|
|
|
531
757
|
return createDoctorCheck(`Block hooks ${blockSlug}`, invalidEntries.length === 0 ? "pass" : "fail", invalidEntries.length === 0 ? `blockHooks metadata is valid${Object.keys(blockHooks).length > 0 ? ` (${Object.keys(blockHooks).join(", ")})` : ""}` : `Invalid blockHooks entries: ${invalidEntries.map(([anchor, position]) => `${anchor || "<empty>"} => ${String(position)}`).join(", ")}`);
|
|
532
758
|
}
|
|
533
759
|
function checkWorkspaceBlockCollectionImport(projectDir, blockSlug) {
|
|
534
|
-
const entryRelativePath =
|
|
535
|
-
const entryPath =
|
|
536
|
-
if (!
|
|
760
|
+
const entryRelativePath = path6.join("src", "blocks", blockSlug, "index.tsx");
|
|
761
|
+
const entryPath = path6.join(projectDir, entryRelativePath);
|
|
762
|
+
if (!fs6.existsSync(entryPath)) {
|
|
537
763
|
return createDoctorCheck(`Block collection ${blockSlug}`, "fail", `Missing ${entryRelativePath}`);
|
|
538
764
|
}
|
|
539
|
-
const source =
|
|
765
|
+
const source = fs6.readFileSync(entryPath, "utf8");
|
|
540
766
|
const hasCollectionImport = WORKSPACE_COLLECTION_IMPORT_PATTERN.test(source);
|
|
541
767
|
return createDoctorCheck(`Block collection ${blockSlug}`, hasCollectionImport ? "pass" : "fail", hasCollectionImport ? "Shared block collection import is present" : `Missing a shared collection import like ${WORKSPACE_COLLECTION_IMPORT_LINE}`);
|
|
542
768
|
}
|
|
543
|
-
function
|
|
544
|
-
const metadataResult = readWorkspaceBlockIframeMetadata(projectDir, blockSlug);
|
|
545
|
-
if (!metadataResult.document) {
|
|
546
|
-
return [
|
|
547
|
-
createDoctorCheck(`Block iframe/API v3 ${blockSlug}`, "warn", metadataResult.error ?? `Unable to inspect ${metadataResult.blockJsonRelativePath}`, WORKSPACE_BLOCK_IFRAME_DIAGNOSTIC_CODES.API_VERSION)
|
|
548
|
-
];
|
|
549
|
-
}
|
|
550
|
-
const blockJson = metadataResult.document;
|
|
551
|
-
const apiVersion = typeof blockJson.apiVersion === "number" && Number.isFinite(blockJson.apiVersion) ? blockJson.apiVersion : null;
|
|
552
|
-
const blockDir = path4.join(projectDir, "src", "blocks", blockSlug);
|
|
553
|
-
const localStyleFiles = WORKSPACE_BLOCK_LOCAL_STYLE_FILES.filter((fileName) => fs4.existsSync(path4.join(blockDir, fileName))).map((fileName) => normalizePathSeparators(path4.join("src", "blocks", blockSlug, fileName)));
|
|
554
|
-
const hasRegisteredEditorStyles = hasRegisteredBlockAsset(blockJson.style) || hasRegisteredBlockAsset(blockJson.editorStyle);
|
|
555
|
-
const editorSources = collectWorkspaceBlockEditorSources(projectDir, blockSlug);
|
|
556
|
-
const editorWrapperSources = editorSources.filter((source) => !isWorkspaceBlockSaveSource(source.relativePath));
|
|
557
|
-
const globalDomAccesses = findWorkspaceBlockGlobalDomAccesses(editorSources);
|
|
558
|
-
const hasBlockPropsUsage = editorSources.some(({ source }) => hasExecutablePattern(source, WORKSPACE_BLOCK_PROPS_PATTERN));
|
|
559
|
-
const hasEditorBlockPropsUsage = editorWrapperSources.some(({ source }) => hasExecutablePattern(source, WORKSPACE_BLOCK_PROPS_PATTERN));
|
|
560
|
-
const blockWrapperStatus = editorWrapperSources.length === 0 || hasEditorBlockPropsUsage ? "pass" : "warn";
|
|
561
|
-
const blockWrapperDetail = editorSources.length === 0 ? "No editor-facing block source files found; general file checks will report missing entrypoints" : editorWrapperSources.length === 0 ? "No editor wrapper source files found; general file checks will report missing entrypoints" : hasEditorBlockPropsUsage ? "Editor-facing sources use block wrapper props" : hasBlockPropsUsage ? "Only save-facing useBlockProps.save() usage was detected. Confirm the editor wrapper also receives useBlockProps() or useInnerBlocksProps() before relying on iframe editor rendering." : "No useBlockProps(), useBlockProps.save(), or useInnerBlocksProps() usage was detected in editor-facing sources. Confirm the block wrapper receives WordPress block editor props before relying on iframe editor rendering.";
|
|
769
|
+
function getWorkspaceBlockCoreDoctorChecks(workspace, block) {
|
|
562
770
|
return [
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
771
|
+
checkExistingFiles(workspace.projectDir, `Block ${block.slug}`, getWorkspaceBlockRequiredFiles(block)),
|
|
772
|
+
checkWorkspaceBlockMetadata(workspace.projectDir, workspace, block),
|
|
773
|
+
checkWorkspaceBlockHooks(workspace.projectDir, block.slug),
|
|
774
|
+
checkWorkspaceBlockCollectionImport(workspace.projectDir, block.slug)
|
|
567
775
|
];
|
|
568
776
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
777
|
+
|
|
778
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-blocks.ts
|
|
779
|
+
function getWorkspaceBlockDoctorChecks(workspace, inventory) {
|
|
780
|
+
const checks = [];
|
|
781
|
+
for (const block of inventory.blocks) {
|
|
782
|
+
checks.push(...getWorkspaceBlockCoreDoctorChecks(workspace, block));
|
|
783
|
+
checks.push(...getWorkspaceBlockIframeCompatibilityChecks(workspace.projectDir, block.slug));
|
|
573
784
|
}
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
return createDoctorCheck("Pattern bootstrap", hasCategoryAnchor && hasPatternGlob ? "pass" : "fail", hasCategoryAnchor && hasPatternGlob ? "Pattern category and loader hooks are present" : "Missing pattern category registration or src/patterns loader hook");
|
|
785
|
+
const registeredBlockSlugs = new Set(inventory.blocks.map((block) => block.slug));
|
|
786
|
+
checks.push(...getWorkspaceBlockAddonDoctorChecks(workspace, inventory, registeredBlockSlugs));
|
|
787
|
+
return checks;
|
|
578
788
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
789
|
+
|
|
790
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features-abilities.ts
|
|
791
|
+
import fs7 from "fs";
|
|
792
|
+
import path7 from "path";
|
|
793
|
+
function getWorkspaceAbilityRequiredFiles(ability) {
|
|
794
|
+
return Array.from(new Set([
|
|
795
|
+
ability.clientFile,
|
|
796
|
+
ability.configFile,
|
|
797
|
+
ability.dataFile,
|
|
798
|
+
ability.inputSchemaFile,
|
|
799
|
+
ability.outputSchemaFile,
|
|
800
|
+
ability.phpFile,
|
|
801
|
+
ability.typesFile
|
|
802
|
+
]));
|
|
588
803
|
}
|
|
589
|
-
function
|
|
590
|
-
const
|
|
591
|
-
if (!
|
|
592
|
-
return createDoctorCheck(`
|
|
804
|
+
function checkWorkspaceAbilityConfig(projectDir, ability) {
|
|
805
|
+
const configPath = path7.join(projectDir, ability.configFile);
|
|
806
|
+
if (!fs7.existsSync(configPath)) {
|
|
807
|
+
return createDoctorCheck(`Ability config ${ability.slug}`, "fail", `Missing ${ability.configFile}`);
|
|
593
808
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
return createDoctorCheck(`
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
const hasImport = hasUncommentedPattern(source, WORKSPACE_BLOCK_TRANSFORMS_IMPORT_PATTERN);
|
|
606
|
-
const hasCall = hasExecutablePattern(source, WORKSPACE_BLOCK_TRANSFORMS_CALL_PATTERN);
|
|
607
|
-
return createDoctorCheck(`Block transform entrypoint ${blockSlug}`, hasImport && hasCall ? "pass" : "fail", hasImport && hasCall ? "Block transform registration hook is present" : "Missing ./transforms import or applyWorkspaceBlockTransforms(registration.settings) call");
|
|
608
|
-
}
|
|
609
|
-
function checkBlockTransformConfig(workspace, transform) {
|
|
610
|
-
const expectedTo = `${workspace.workspace.namespace}/${transform.block}`;
|
|
611
|
-
const issues = [];
|
|
612
|
-
if (!WORKSPACE_FULL_BLOCK_NAME_PATTERN.test(transform.from)) {
|
|
613
|
-
issues.push("from must use full namespace/block format");
|
|
614
|
-
}
|
|
615
|
-
if (transform.to !== expectedTo) {
|
|
616
|
-
issues.push(`to must equal "${expectedTo}" for workspace block "${transform.block}"`);
|
|
617
|
-
}
|
|
618
|
-
return createDoctorCheck(`Block transform config ${transform.block}/${transform.slug}`, issues.length === 0 ? "pass" : "fail", issues.length === 0 ? `${transform.from} transforms into ${transform.to}` : issues.join("; "));
|
|
619
|
-
}
|
|
620
|
-
function getWorkspaceBlockDoctorChecks(workspace, inventory) {
|
|
621
|
-
const checks = [];
|
|
622
|
-
for (const block of inventory.blocks) {
|
|
623
|
-
checks.push(checkExistingFiles(workspace.projectDir, `Block ${block.slug}`, getWorkspaceBlockRequiredFiles(block)));
|
|
624
|
-
checks.push(checkWorkspaceBlockMetadata(workspace.projectDir, workspace, block));
|
|
625
|
-
checks.push(checkWorkspaceBlockHooks(workspace.projectDir, block.slug));
|
|
626
|
-
checks.push(checkWorkspaceBlockCollectionImport(workspace.projectDir, block.slug));
|
|
627
|
-
checks.push(...checkWorkspaceBlockIframeCompatibility(workspace.projectDir, block.slug));
|
|
628
|
-
}
|
|
629
|
-
const registeredBlockSlugs = new Set(inventory.blocks.map((block) => block.slug));
|
|
630
|
-
const variationTargetBlocks = new Set;
|
|
631
|
-
for (const variation of inventory.variations) {
|
|
632
|
-
if (!registeredBlockSlugs.has(variation.block)) {
|
|
633
|
-
checks.push(createDoctorCheck(`Variation ${variation.block}/${variation.slug}`, "fail", `Variation references unknown block "${variation.block}"`));
|
|
634
|
-
continue;
|
|
635
|
-
}
|
|
636
|
-
variationTargetBlocks.add(variation.block);
|
|
637
|
-
checks.push(checkExistingFiles(workspace.projectDir, `Variation ${variation.block}/${variation.slug}`, [variation.file]));
|
|
638
|
-
}
|
|
639
|
-
for (const blockSlug of variationTargetBlocks) {
|
|
640
|
-
checks.push(checkVariationEntrypoint(workspace.projectDir, blockSlug));
|
|
641
|
-
}
|
|
642
|
-
const blockStyleTargetBlocks = new Set;
|
|
643
|
-
for (const blockStyle of inventory.blockStyles) {
|
|
644
|
-
if (!registeredBlockSlugs.has(blockStyle.block)) {
|
|
645
|
-
checks.push(createDoctorCheck(`Block style ${blockStyle.block}/${blockStyle.slug}`, "fail", `Block style references unknown block "${blockStyle.block}"`));
|
|
646
|
-
continue;
|
|
647
|
-
}
|
|
648
|
-
blockStyleTargetBlocks.add(blockStyle.block);
|
|
649
|
-
checks.push(checkExistingFiles(workspace.projectDir, `Block style ${blockStyle.block}/${blockStyle.slug}`, [blockStyle.file]));
|
|
650
|
-
}
|
|
651
|
-
for (const blockSlug of blockStyleTargetBlocks) {
|
|
652
|
-
checks.push(checkExistingFiles(workspace.projectDir, `Block style registry ${blockSlug}`, [
|
|
653
|
-
path4.join("src", "blocks", blockSlug, "styles", "index.ts")
|
|
654
|
-
]));
|
|
655
|
-
checks.push(checkBlockStyleEntrypoint(workspace.projectDir, blockSlug));
|
|
656
|
-
}
|
|
657
|
-
const blockTransformTargetBlocks = new Set;
|
|
658
|
-
for (const blockTransform of inventory.blockTransforms) {
|
|
659
|
-
if (!registeredBlockSlugs.has(blockTransform.block)) {
|
|
660
|
-
checks.push(createDoctorCheck(`Block transform ${blockTransform.block}/${blockTransform.slug}`, "fail", `Block transform references unknown block "${blockTransform.block}"`));
|
|
661
|
-
continue;
|
|
662
|
-
}
|
|
663
|
-
blockTransformTargetBlocks.add(blockTransform.block);
|
|
664
|
-
checks.push(checkBlockTransformConfig(workspace, blockTransform));
|
|
665
|
-
checks.push(checkExistingFiles(workspace.projectDir, `Block transform ${blockTransform.block}/${blockTransform.slug}`, [blockTransform.file]));
|
|
666
|
-
}
|
|
667
|
-
for (const blockSlug of blockTransformTargetBlocks) {
|
|
668
|
-
checks.push(checkExistingFiles(workspace.projectDir, `Block transform registry ${blockSlug}`, [
|
|
669
|
-
path4.join("src", "blocks", blockSlug, "transforms", "index.ts")
|
|
670
|
-
]));
|
|
671
|
-
checks.push(checkBlockTransformEntrypoint(workspace.projectDir, blockSlug));
|
|
672
|
-
}
|
|
673
|
-
const shouldCheckPatternBootstrap = inventory.patterns.length > 0 || fs4.existsSync(path4.join(workspace.projectDir, "src", "patterns"));
|
|
674
|
-
if (shouldCheckPatternBootstrap) {
|
|
675
|
-
checks.push(checkWorkspacePatternBootstrap(workspace.projectDir, workspace.packageName));
|
|
676
|
-
}
|
|
677
|
-
for (const pattern of inventory.patterns) {
|
|
678
|
-
checks.push(checkExistingFiles(workspace.projectDir, `Pattern ${pattern.slug}`, [pattern.file]));
|
|
679
|
-
}
|
|
680
|
-
return checks;
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features.ts
|
|
684
|
-
import fs5 from "fs";
|
|
685
|
-
import path5 from "path";
|
|
686
|
-
function isManualRestResource(restResource) {
|
|
687
|
-
return restResource.mode === "manual";
|
|
688
|
-
}
|
|
689
|
-
function getWorkspaceRestResourceRequiredFiles(restResource) {
|
|
690
|
-
const schemaNames = new Set;
|
|
691
|
-
if (isManualRestResource(restResource)) {
|
|
692
|
-
schemaNames.add("query");
|
|
693
|
-
if (restResource.bodyTypeName) {
|
|
694
|
-
schemaNames.add("request");
|
|
695
|
-
}
|
|
696
|
-
schemaNames.add("response");
|
|
697
|
-
return Array.from(new Set([
|
|
698
|
-
restResource.apiFile,
|
|
699
|
-
...Array.from(schemaNames, (schemaName) => path5.join(path5.dirname(restResource.typesFile), "api-schemas", `${schemaName}.schema.json`)),
|
|
700
|
-
restResource.clientFile,
|
|
701
|
-
restResource.openApiFile,
|
|
702
|
-
restResource.typesFile,
|
|
703
|
-
restResource.validatorsFile
|
|
704
|
-
]));
|
|
705
|
-
}
|
|
706
|
-
if (restResource.methods.includes("list")) {
|
|
707
|
-
schemaNames.add("list-query");
|
|
708
|
-
schemaNames.add("list-response");
|
|
709
|
-
}
|
|
710
|
-
if (restResource.methods.includes("read")) {
|
|
711
|
-
schemaNames.add("read-query");
|
|
712
|
-
schemaNames.add("read-response");
|
|
713
|
-
}
|
|
714
|
-
if (restResource.methods.includes("create")) {
|
|
715
|
-
schemaNames.add("create-request");
|
|
716
|
-
schemaNames.add("create-response");
|
|
717
|
-
}
|
|
718
|
-
if (restResource.methods.includes("update")) {
|
|
719
|
-
schemaNames.add("update-query");
|
|
720
|
-
schemaNames.add("update-request");
|
|
721
|
-
schemaNames.add("update-response");
|
|
722
|
-
}
|
|
723
|
-
if (restResource.methods.includes("delete")) {
|
|
724
|
-
schemaNames.add("delete-query");
|
|
725
|
-
schemaNames.add("delete-response");
|
|
726
|
-
}
|
|
727
|
-
return Array.from(new Set([
|
|
728
|
-
restResource.apiFile,
|
|
729
|
-
...Array.from(schemaNames, (schemaName) => path5.join(path5.dirname(restResource.typesFile), "api-schemas", `${schemaName}.schema.json`)),
|
|
730
|
-
restResource.clientFile,
|
|
731
|
-
...restResource.dataFile ? [restResource.dataFile] : [],
|
|
732
|
-
restResource.openApiFile,
|
|
733
|
-
...restResource.phpFile ? [restResource.phpFile] : [],
|
|
734
|
-
restResource.typesFile,
|
|
735
|
-
restResource.validatorsFile
|
|
736
|
-
]));
|
|
737
|
-
}
|
|
738
|
-
function checkWorkspaceRestResourceConfig(restResource) {
|
|
739
|
-
const hasNamespace = REST_RESOURCE_NAMESPACE_PATTERN.test(restResource.namespace);
|
|
740
|
-
if (isManualRestResource(restResource)) {
|
|
741
|
-
const hasAuth = restResource.auth == null || MANUAL_REST_CONTRACT_AUTH_IDS.includes(restResource.auth);
|
|
742
|
-
const hasMethod = typeof restResource.method === "string" && MANUAL_REST_CONTRACT_HTTP_METHOD_IDS.includes(restResource.method);
|
|
743
|
-
const hasPathPattern = typeof restResource.pathPattern === "string" && restResource.pathPattern.startsWith("/") && restResource.pathPattern.length > 1;
|
|
744
|
-
return createDoctorCheck(`REST resource config ${restResource.slug}`, hasNamespace && hasAuth && hasMethod && hasPathPattern ? "pass" : "fail", hasNamespace && hasAuth && hasMethod && hasPathPattern ? `Manual REST contract ${restResource.method} /${restResource.namespace}${restResource.pathPattern}` : "Manual REST contract namespace, auth, method, or path pattern is invalid");
|
|
745
|
-
}
|
|
746
|
-
const hasMethods = restResource.methods.length > 0 && restResource.methods.every((method) => REST_RESOURCE_METHOD_IDS.includes(method));
|
|
747
|
-
const hasGeneratedFiles = typeof restResource.dataFile === "string" && restResource.dataFile.length > 0 && typeof restResource.phpFile === "string" && restResource.phpFile.length > 0;
|
|
748
|
-
const hasRoutePattern = restResource.routePattern == null || typeof restResource.routePattern === "string" && restResource.routePattern.startsWith("/") && restResource.routePattern.length > 1 && !/\s/u.test(restResource.routePattern) && isGeneratedRestResourceRoutePatternCompatible(restResource.routePattern);
|
|
749
|
-
return createDoctorCheck(`REST resource config ${restResource.slug}`, hasNamespace && hasMethods && hasGeneratedFiles && hasRoutePattern ? "pass" : "fail", hasNamespace && hasMethods && hasGeneratedFiles && hasRoutePattern ? `REST resource namespace ${restResource.namespace} with methods ${restResource.methods.join(", ")}` : "REST resource namespace, methods, dataFile, phpFile, or routePattern are invalid");
|
|
750
|
-
}
|
|
751
|
-
function checkWorkspaceRestResourceBootstrap(projectDir, packageName, phpPrefix) {
|
|
752
|
-
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
753
|
-
if (!fs5.existsSync(bootstrapPath)) {
|
|
754
|
-
return createDoctorCheck("REST resource bootstrap", "fail", `Missing ${path5.basename(bootstrapPath)}`);
|
|
755
|
-
}
|
|
756
|
-
const source = fs5.readFileSync(bootstrapPath, "utf8");
|
|
757
|
-
const registerFunctionName = `${phpPrefix}_register_rest_resources`;
|
|
758
|
-
const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
|
|
759
|
-
const hasServerGlob = source.includes(WORKSPACE_REST_RESOURCE_GLOB);
|
|
760
|
-
const hasRegisterHook = source.includes(registerHook);
|
|
761
|
-
return createDoctorCheck("REST resource bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook ? "REST resource PHP loader hook is present" : "Missing REST resource PHP require glob or init hook");
|
|
762
|
-
}
|
|
763
|
-
function getWorkspacePostMetaRequiredFiles(postMeta) {
|
|
764
|
-
return Array.from(new Set([
|
|
765
|
-
postMeta.phpFile,
|
|
766
|
-
postMeta.schemaFile,
|
|
767
|
-
postMeta.typesFile
|
|
768
|
-
]));
|
|
769
|
-
}
|
|
770
|
-
function checkWorkspacePostMetaConfig(postMeta) {
|
|
771
|
-
let hasPostType = false;
|
|
772
|
-
try {
|
|
773
|
-
hasPostType = assertValidPostMetaPostType(postMeta.postType) === postMeta.postType;
|
|
774
|
-
} catch {
|
|
775
|
-
hasPostType = false;
|
|
776
|
-
}
|
|
777
|
-
const hasMetaKey = typeof postMeta.metaKey === "string" && postMeta.metaKey.trim().length > 0 && !/\s/u.test(postMeta.metaKey);
|
|
778
|
-
const hasRestExposure = typeof postMeta.showInRest === "boolean";
|
|
779
|
-
return createDoctorCheck(`Post meta config ${postMeta.slug}`, hasPostType && hasMetaKey && hasRestExposure ? "pass" : "fail", hasPostType && hasMetaKey && hasRestExposure ? `Post meta ${postMeta.metaKey} targets ${postMeta.postType}` : "Post meta postType, metaKey, or showInRest configuration is invalid");
|
|
780
|
-
}
|
|
781
|
-
function checkWorkspacePostMetaBootstrap(projectDir, packageName, phpPrefix) {
|
|
782
|
-
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
783
|
-
if (!fs5.existsSync(bootstrapPath)) {
|
|
784
|
-
return createDoctorCheck("Post meta bootstrap", "fail", `Missing ${path5.basename(bootstrapPath)}`);
|
|
785
|
-
}
|
|
786
|
-
const source = fs5.readFileSync(bootstrapPath, "utf8");
|
|
787
|
-
const registerFunctionName = `${phpPrefix}_register_post_meta_contracts`;
|
|
788
|
-
const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
|
|
789
|
-
const hasServerGlob = source.includes(WORKSPACE_POST_META_GLOB);
|
|
790
|
-
const hasRegisterHook = source.includes(registerHook);
|
|
791
|
-
return createDoctorCheck("Post meta bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook ? "Post meta PHP loader hook is present" : "Missing post meta PHP require glob or init hook");
|
|
792
|
-
}
|
|
793
|
-
function checkWorkspacePostMetaPhp(projectDir, postMeta) {
|
|
794
|
-
const phpPath = path5.join(projectDir, postMeta.phpFile);
|
|
795
|
-
if (!fs5.existsSync(phpPath)) {
|
|
796
|
-
return createDoctorCheck(`Post meta PHP ${postMeta.slug}`, "fail", `Missing ${postMeta.phpFile}`);
|
|
797
|
-
}
|
|
798
|
-
const source = fs5.readFileSync(phpPath, "utf8");
|
|
799
|
-
const hasRegisterPostMeta = source.includes("register_post_meta");
|
|
800
|
-
const hasPostType = source.includes(postMeta.postType);
|
|
801
|
-
const hasMetaKey = source.includes(postMeta.metaKey);
|
|
802
|
-
const hasSchemaFile = source.includes(postMeta.schemaFile);
|
|
803
|
-
const hasRestExposure = source.includes("'show_in_rest'");
|
|
804
|
-
return createDoctorCheck(`Post meta PHP ${postMeta.slug}`, hasRegisterPostMeta && hasPostType && hasMetaKey && hasSchemaFile && hasRestExposure ? "pass" : "fail", hasRegisterPostMeta && hasPostType && hasMetaKey && hasSchemaFile && hasRestExposure ? "Post meta registration, schema path, and REST exposure flag are wired" : "Missing register_post_meta, post type, meta key, schema path, or show_in_rest wiring");
|
|
805
|
-
}
|
|
806
|
-
function getWorkspaceAbilityRequiredFiles(ability) {
|
|
807
|
-
return Array.from(new Set([
|
|
808
|
-
ability.clientFile,
|
|
809
|
-
ability.configFile,
|
|
810
|
-
ability.dataFile,
|
|
811
|
-
ability.inputSchemaFile,
|
|
812
|
-
ability.outputSchemaFile,
|
|
813
|
-
ability.phpFile,
|
|
814
|
-
ability.typesFile
|
|
815
|
-
]));
|
|
816
|
-
}
|
|
817
|
-
function checkWorkspaceAbilityConfig(projectDir, ability) {
|
|
818
|
-
const configPath = path5.join(projectDir, ability.configFile);
|
|
819
|
-
if (!fs5.existsSync(configPath)) {
|
|
820
|
-
return createDoctorCheck(`Ability config ${ability.slug}`, "fail", `Missing ${ability.configFile}`);
|
|
821
|
-
}
|
|
822
|
-
try {
|
|
823
|
-
const config = JSON.parse(fs5.readFileSync(configPath, "utf8"));
|
|
824
|
-
const abilityId = typeof config.abilityId === "string" ? config.abilityId.trim() : "";
|
|
825
|
-
const categorySlug = typeof config.category?.slug === "string" ? config.category.slug.trim() : "";
|
|
826
|
-
const hasValidAbilityId = /^[a-z0-9-]+\/[a-z0-9-]+$/u.test(abilityId);
|
|
827
|
-
const hasValidCategorySlug = /^[a-z0-9-]+$/u.test(categorySlug);
|
|
828
|
-
return createDoctorCheck(`Ability config ${ability.slug}`, hasValidAbilityId && hasValidCategorySlug ? "pass" : "fail", hasValidAbilityId && hasValidCategorySlug ? `Ability id ${abilityId} in category ${categorySlug} is valid` : "Ability config must define a valid abilityId (`namespace/ability-name`) and category.slug.");
|
|
829
|
-
} catch (error) {
|
|
830
|
-
return createDoctorCheck(`Ability config ${ability.slug}`, "fail", error instanceof Error ? error.message : String(error));
|
|
809
|
+
try {
|
|
810
|
+
const config = readJsonFileSync(configPath, {
|
|
811
|
+
context: "workspace ability config"
|
|
812
|
+
});
|
|
813
|
+
const abilityId = typeof config.abilityId === "string" ? config.abilityId.trim() : "";
|
|
814
|
+
const categorySlug = typeof config.category?.slug === "string" ? config.category.slug.trim() : "";
|
|
815
|
+
const hasValidAbilityId = /^[a-z0-9-]+\/[a-z0-9-]+$/u.test(abilityId);
|
|
816
|
+
const hasValidCategorySlug = /^[a-z0-9-]+$/u.test(categorySlug);
|
|
817
|
+
return createDoctorCheck(`Ability config ${ability.slug}`, hasValidAbilityId && hasValidCategorySlug ? "pass" : "fail", hasValidAbilityId && hasValidCategorySlug ? `Ability id ${abilityId} in category ${categorySlug} is valid` : "Ability config must define a valid abilityId (`namespace/ability-name`) and category.slug.");
|
|
818
|
+
} catch (error) {
|
|
819
|
+
return createDoctorCheck(`Ability config ${ability.slug}`, "fail", error instanceof Error ? error.message : String(error));
|
|
831
820
|
}
|
|
832
821
|
}
|
|
833
822
|
function checkWorkspaceAbilityBootstrap(projectDir, packageName, phpPrefix) {
|
|
834
823
|
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
835
|
-
if (!
|
|
836
|
-
return createDoctorCheck("Ability bootstrap", "fail", `Missing ${
|
|
824
|
+
if (!fs7.existsSync(bootstrapPath)) {
|
|
825
|
+
return createDoctorCheck("Ability bootstrap", "fail", `Missing ${path7.basename(bootstrapPath)}`);
|
|
837
826
|
}
|
|
838
|
-
const source =
|
|
827
|
+
const source = fs7.readFileSync(bootstrapPath, "utf8");
|
|
839
828
|
const loadFunctionName = `${phpPrefix}_load_workflow_abilities`;
|
|
840
829
|
const enqueueFunctionName = `${phpPrefix}_enqueue_workflow_abilities`;
|
|
841
830
|
const loadHook = `add_action( 'plugins_loaded', '${loadFunctionName}' );`;
|
|
@@ -852,27 +841,130 @@ function checkWorkspaceAbilityBootstrap(projectDir, packageName, phpPrefix) {
|
|
|
852
841
|
}
|
|
853
842
|
function checkWorkspaceAbilityIndex(projectDir, abilities) {
|
|
854
843
|
const indexRelativePath = [
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
].find((relativePath) =>
|
|
844
|
+
path7.join("src", "abilities", "index.ts"),
|
|
845
|
+
path7.join("src", "abilities", "index.js")
|
|
846
|
+
].find((relativePath) => fs7.existsSync(path7.join(projectDir, relativePath)));
|
|
858
847
|
if (!indexRelativePath) {
|
|
859
848
|
return createDoctorCheck("Abilities index", "fail", "Missing src/abilities/index.ts or src/abilities/index.js");
|
|
860
849
|
}
|
|
861
|
-
const indexPath =
|
|
862
|
-
const source =
|
|
850
|
+
const indexPath = path7.join(projectDir, indexRelativePath);
|
|
851
|
+
const source = fs7.readFileSync(indexPath, "utf8");
|
|
863
852
|
const missingExports = abilities.filter((ability) => {
|
|
864
853
|
const exportPattern = new RegExp(`^\\s*export\\s+(?:\\*\\s+from|\\{[^}]+\\}\\s+from)\\s+['"\`]\\./${escapeRegex(ability.slug)}\\/client['"\`]`, "mu");
|
|
865
854
|
return !exportPattern.test(source);
|
|
866
855
|
});
|
|
867
856
|
return createDoctorCheck("Abilities index", missingExports.length === 0 ? "pass" : "fail", missingExports.length === 0 ? "Ability client helpers are aggregated" : `Missing ability exports for: ${missingExports.map((entry) => entry.slug).join(", ")}`);
|
|
868
857
|
}
|
|
858
|
+
function getWorkspaceAbilityDoctorChecks(workspace, abilities) {
|
|
859
|
+
const checks = [];
|
|
860
|
+
if (abilities.length > 0) {
|
|
861
|
+
checks.push(checkWorkspaceAbilityBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
862
|
+
checks.push(checkWorkspaceAbilityIndex(workspace.projectDir, abilities));
|
|
863
|
+
}
|
|
864
|
+
for (const ability of abilities) {
|
|
865
|
+
checks.push(checkWorkspaceAbilityConfig(workspace.projectDir, ability));
|
|
866
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Ability ${ability.slug}`, getWorkspaceAbilityRequiredFiles(ability)));
|
|
867
|
+
}
|
|
868
|
+
return checks;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features-admin-views.ts
|
|
872
|
+
import fs8 from "fs";
|
|
873
|
+
import path8 from "path";
|
|
874
|
+
function getWorkspaceAdminViewRequiredFiles(adminView) {
|
|
875
|
+
const adminViewDir = path8.join("src", "admin-views", adminView.slug);
|
|
876
|
+
return Array.from(new Set([
|
|
877
|
+
adminView.file,
|
|
878
|
+
adminView.phpFile,
|
|
879
|
+
path8.join(adminViewDir, "Screen.tsx"),
|
|
880
|
+
path8.join(adminViewDir, "config.ts"),
|
|
881
|
+
path8.join(adminViewDir, "data.ts"),
|
|
882
|
+
path8.join(adminViewDir, "style.scss"),
|
|
883
|
+
path8.join(adminViewDir, "types.ts")
|
|
884
|
+
]));
|
|
885
|
+
}
|
|
886
|
+
function checkWorkspaceAdminViewConfig(adminView, inventory) {
|
|
887
|
+
if (adminView.source === undefined) {
|
|
888
|
+
return createDoctorCheck(`Admin view config ${adminView.slug}`, "pass", "Admin view uses a replaceable local fetcher");
|
|
889
|
+
}
|
|
890
|
+
const source = adminView.source.trim();
|
|
891
|
+
const restSourceMatch = /^rest-resource:([a-z][a-z0-9-]*)$/u.exec(source);
|
|
892
|
+
const coreDataSourceMatch = /^core-data:(postType|taxonomy)\/([a-z0-9][a-z0-9_-]*)$/u.exec(source);
|
|
893
|
+
const restResourceSlug = restSourceMatch?.[1];
|
|
894
|
+
const restResource = restResourceSlug ? inventory.restResources.find((entry) => entry.slug === restResourceSlug) : undefined;
|
|
895
|
+
const isListCapableRestResource = Boolean(restResource?.methods.includes("list"));
|
|
896
|
+
const isManualSettingsRestResource = isAdminViewManualSettingsRestResource(restResource);
|
|
897
|
+
const hasManualSettingsRouteParameters = isManualSettingsRestResource && hasAdminViewManualSettingsRouteParameters(restResource);
|
|
898
|
+
const isValid = isListCapableRestResource || isManualSettingsRestResource && !hasManualSettingsRouteParameters || Boolean(coreDataSourceMatch);
|
|
899
|
+
const failDetail = hasManualSettingsRouteParameters ? `Admin view source ${source} uses route parameters or regex groups and cannot scaffold a singleton settings form` : "Admin view source must use rest-resource:<slug> with a list-capable REST resource, a manual settings contract with a body type, or core-data:<postType|taxonomy>/<name>";
|
|
900
|
+
return createDoctorCheck(`Admin view config ${adminView.slug}`, isValid ? "pass" : "fail", isValid ? `Admin view source ${source} is ${isManualSettingsRestResource ? "settings-form capable" : coreDataSourceMatch ? "core-data capable" : "list-capable"}` : failDetail);
|
|
901
|
+
}
|
|
902
|
+
function checkWorkspaceAdminViewBootstrap(projectDir, packageName, phpPrefix) {
|
|
903
|
+
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
904
|
+
if (!fs8.existsSync(bootstrapPath)) {
|
|
905
|
+
return createDoctorCheck("Admin view bootstrap", "fail", `Missing ${path8.basename(bootstrapPath)}`);
|
|
906
|
+
}
|
|
907
|
+
const source = fs8.readFileSync(bootstrapPath, "utf8");
|
|
908
|
+
const loadFunctionName = `${phpPrefix}_load_admin_views`;
|
|
909
|
+
const loadHook = `add_action( 'plugins_loaded', '${loadFunctionName}' );`;
|
|
910
|
+
const hasLoaderHook = source.includes(loadHook);
|
|
911
|
+
const hasServerGlob = source.includes(WORKSPACE_ADMIN_VIEW_GLOB);
|
|
912
|
+
return createDoctorCheck("Admin view bootstrap", hasLoaderHook && hasServerGlob ? "pass" : "fail", hasLoaderHook && hasServerGlob ? "Admin view PHP loader hook is present" : "Missing admin view PHP require glob or plugins_loaded hook");
|
|
913
|
+
}
|
|
914
|
+
function checkWorkspaceAdminViewIndex(projectDir, adminViews) {
|
|
915
|
+
const indexRelativePath = [
|
|
916
|
+
path8.join("src", "admin-views", "index.ts"),
|
|
917
|
+
path8.join("src", "admin-views", "index.js")
|
|
918
|
+
].find((relativePath) => fs8.existsSync(path8.join(projectDir, relativePath)));
|
|
919
|
+
if (!indexRelativePath) {
|
|
920
|
+
return createDoctorCheck("Admin views index", "fail", "Missing src/admin-views/index.ts or src/admin-views/index.js");
|
|
921
|
+
}
|
|
922
|
+
const indexPath = path8.join(projectDir, indexRelativePath);
|
|
923
|
+
const source = fs8.readFileSync(indexPath, "utf8");
|
|
924
|
+
const missingImports = adminViews.filter((adminView) => {
|
|
925
|
+
const importPattern = new RegExp(`['"\`]\\./${escapeRegex(adminView.slug)}(?:/[^'"\`]*)?['"\`]`, "u");
|
|
926
|
+
return !importPattern.test(source);
|
|
927
|
+
});
|
|
928
|
+
return createDoctorCheck("Admin views index", missingImports.length === 0 ? "pass" : "fail", missingImports.length === 0 ? "Admin view registrations are aggregated" : `Missing admin view imports for: ${missingImports.map((entry) => entry.slug).join(", ")}`);
|
|
929
|
+
}
|
|
930
|
+
function checkWorkspaceAdminViewPhp(projectDir, adminView) {
|
|
931
|
+
const phpPath = path8.join(projectDir, adminView.phpFile);
|
|
932
|
+
if (!fs8.existsSync(phpPath)) {
|
|
933
|
+
return createDoctorCheck(`Admin view PHP ${adminView.slug}`, "fail", `Missing ${adminView.phpFile}`);
|
|
934
|
+
}
|
|
935
|
+
const source = fs8.readFileSync(phpPath, "utf8");
|
|
936
|
+
const hasAdminMenu = source.includes("add_submenu_page");
|
|
937
|
+
const hasAdminEnqueue = source.includes("admin_enqueue_scripts");
|
|
938
|
+
const hasScript = source.includes(WORKSPACE_ADMIN_VIEW_SCRIPT);
|
|
939
|
+
const hasAsset = source.includes(WORKSPACE_ADMIN_VIEW_ASSET);
|
|
940
|
+
const hasStyle = source.includes(WORKSPACE_ADMIN_VIEW_STYLE);
|
|
941
|
+
const hasComponentsStyleDependency = /['"]wp-components['"]/u.test(source);
|
|
942
|
+
return createDoctorCheck(`Admin view PHP ${adminView.slug}`, hasAdminMenu && hasAdminEnqueue && hasScript && hasAsset && hasStyle && hasComponentsStyleDependency ? "pass" : "fail", hasAdminMenu && hasAdminEnqueue && hasScript && hasAsset && hasStyle && hasComponentsStyleDependency ? "Admin menu, script, style, and wp-components style dependency are wired" : "Missing admin menu, enqueue hook, build/admin-views asset reference, or wp-components style dependency");
|
|
943
|
+
}
|
|
944
|
+
function getWorkspaceAdminViewDoctorChecks(workspace, inventory) {
|
|
945
|
+
const checks = [];
|
|
946
|
+
if (inventory.adminViews.length > 0) {
|
|
947
|
+
checks.push(checkWorkspaceAdminViewBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
948
|
+
checks.push(checkWorkspaceAdminViewIndex(workspace.projectDir, inventory.adminViews));
|
|
949
|
+
}
|
|
950
|
+
for (const adminView of inventory.adminViews) {
|
|
951
|
+
checks.push(checkWorkspaceAdminViewConfig(adminView, inventory));
|
|
952
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Admin view ${adminView.slug}`, getWorkspaceAdminViewRequiredFiles(adminView)));
|
|
953
|
+
checks.push(checkWorkspaceAdminViewPhp(workspace.projectDir, adminView));
|
|
954
|
+
}
|
|
955
|
+
return checks;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features-ai.ts
|
|
959
|
+
import fs9 from "fs";
|
|
960
|
+
import path9 from "path";
|
|
869
961
|
function getWorkspaceAiFeatureRequiredFiles(aiFeature) {
|
|
870
962
|
return Array.from(new Set([
|
|
871
963
|
aiFeature.aiSchemaFile,
|
|
872
964
|
aiFeature.apiFile,
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
965
|
+
path9.join(path9.dirname(aiFeature.typesFile), "api-schemas", "feature-request.schema.json"),
|
|
966
|
+
path9.join(path9.dirname(aiFeature.typesFile), "api-schemas", "feature-response.schema.json"),
|
|
967
|
+
path9.join(path9.dirname(aiFeature.typesFile), "api-schemas", "feature-result.schema.json"),
|
|
876
968
|
aiFeature.clientFile,
|
|
877
969
|
aiFeature.dataFile,
|
|
878
970
|
aiFeature.openApiFile,
|
|
@@ -887,25 +979,40 @@ function checkWorkspaceAiFeatureConfig(aiFeature) {
|
|
|
887
979
|
}
|
|
888
980
|
function checkWorkspaceAiFeatureBootstrap(projectDir, packageName, phpPrefix) {
|
|
889
981
|
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
890
|
-
if (!
|
|
891
|
-
return createDoctorCheck("AI feature bootstrap", "fail", `Missing ${
|
|
982
|
+
if (!fs9.existsSync(bootstrapPath)) {
|
|
983
|
+
return createDoctorCheck("AI feature bootstrap", "fail", `Missing ${path9.basename(bootstrapPath)}`);
|
|
892
984
|
}
|
|
893
|
-
const source =
|
|
985
|
+
const source = fs9.readFileSync(bootstrapPath, "utf8");
|
|
894
986
|
const registerFunctionName = `${phpPrefix}_register_ai_features`;
|
|
895
987
|
const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
|
|
896
988
|
const hasServerGlob = source.includes(WORKSPACE_AI_FEATURE_GLOB);
|
|
897
989
|
const hasRegisterHook = source.includes(registerHook);
|
|
898
990
|
return createDoctorCheck("AI feature bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook ? "AI feature PHP loader hook is present" : "Missing AI feature PHP require glob or init hook");
|
|
899
991
|
}
|
|
992
|
+
function getWorkspaceAiFeatureDoctorChecks(workspace, aiFeatures) {
|
|
993
|
+
const checks = [];
|
|
994
|
+
if (aiFeatures.length > 0) {
|
|
995
|
+
checks.push(checkWorkspaceAiFeatureBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
996
|
+
}
|
|
997
|
+
for (const aiFeature of aiFeatures) {
|
|
998
|
+
checks.push(checkWorkspaceAiFeatureConfig(aiFeature));
|
|
999
|
+
checks.push(checkExistingFiles(workspace.projectDir, `AI feature ${aiFeature.slug}`, getWorkspaceAiFeatureRequiredFiles(aiFeature)));
|
|
1000
|
+
}
|
|
1001
|
+
return checks;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features-editor-plugins.ts
|
|
1005
|
+
import fs10 from "fs";
|
|
1006
|
+
import path10 from "path";
|
|
900
1007
|
function getWorkspaceEditorPluginRequiredFiles(editorPlugin) {
|
|
901
|
-
const editorPluginDir =
|
|
902
|
-
const surfaceFile = editorPlugin.slot === "PluginSidebar" ?
|
|
1008
|
+
const editorPluginDir = path10.join("src", "editor-plugins", editorPlugin.slug);
|
|
1009
|
+
const surfaceFile = editorPlugin.slot === "PluginSidebar" ? path10.join(editorPluginDir, "Sidebar.tsx") : path10.join(editorPluginDir, "Surface.tsx");
|
|
903
1010
|
return Array.from(new Set([
|
|
904
1011
|
editorPlugin.file,
|
|
905
1012
|
surfaceFile,
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
1013
|
+
path10.join(editorPluginDir, "data.ts"),
|
|
1014
|
+
path10.join(editorPluginDir, "types.ts"),
|
|
1015
|
+
path10.join(editorPluginDir, "style.scss")
|
|
909
1016
|
]));
|
|
910
1017
|
}
|
|
911
1018
|
function checkWorkspaceEditorPluginConfig(editorPlugin) {
|
|
@@ -915,10 +1022,10 @@ function checkWorkspaceEditorPluginConfig(editorPlugin) {
|
|
|
915
1022
|
}
|
|
916
1023
|
function checkWorkspaceEditorPluginBootstrap(projectDir, packageName, phpPrefix) {
|
|
917
1024
|
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
918
|
-
if (!
|
|
919
|
-
return createDoctorCheck("Editor plugin bootstrap", "fail", `Missing ${
|
|
1025
|
+
if (!fs10.existsSync(bootstrapPath)) {
|
|
1026
|
+
return createDoctorCheck("Editor plugin bootstrap", "fail", `Missing ${path10.basename(bootstrapPath)}`);
|
|
920
1027
|
}
|
|
921
|
-
const source =
|
|
1028
|
+
const source = fs10.readFileSync(bootstrapPath, "utf8");
|
|
922
1029
|
const enqueueFunctionName = `${phpPrefix}_enqueue_editor_plugins_editor`;
|
|
923
1030
|
const enqueueHook = `add_action( 'enqueue_block_editor_assets', '${enqueueFunctionName}' );`;
|
|
924
1031
|
const hasEditorEnqueueHook = source.includes(enqueueHook);
|
|
@@ -929,151 +1036,205 @@ function checkWorkspaceEditorPluginBootstrap(projectDir, packageName, phpPrefix)
|
|
|
929
1036
|
}
|
|
930
1037
|
function checkWorkspaceEditorPluginIndex(projectDir, editorPlugins) {
|
|
931
1038
|
const indexRelativePath = [
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
].find((relativePath) =>
|
|
1039
|
+
path10.join("src", "editor-plugins", "index.ts"),
|
|
1040
|
+
path10.join("src", "editor-plugins", "index.js")
|
|
1041
|
+
].find((relativePath) => fs10.existsSync(path10.join(projectDir, relativePath)));
|
|
935
1042
|
if (!indexRelativePath) {
|
|
936
1043
|
return createDoctorCheck("Editor plugins index", "fail", "Missing src/editor-plugins/index.ts or src/editor-plugins/index.js");
|
|
937
1044
|
}
|
|
938
|
-
const indexPath =
|
|
939
|
-
const source =
|
|
1045
|
+
const indexPath = path10.join(projectDir, indexRelativePath);
|
|
1046
|
+
const source = fs10.readFileSync(indexPath, "utf8");
|
|
940
1047
|
const missingImports = editorPlugins.filter((editorPlugin) => {
|
|
941
1048
|
const importPattern = new RegExp(`['"\`]\\./${escapeRegex(editorPlugin.slug)}(?:/[^'"\`]*)?['"\`]`, "u");
|
|
942
1049
|
return !importPattern.test(source);
|
|
943
1050
|
});
|
|
944
1051
|
return createDoctorCheck("Editor plugins index", missingImports.length === 0 ? "pass" : "fail", missingImports.length === 0 ? "Editor plugin registrations are aggregated" : `Missing editor plugin imports for: ${missingImports.map((entry) => entry.slug).join(", ")}`);
|
|
945
1052
|
}
|
|
946
|
-
function
|
|
947
|
-
const
|
|
1053
|
+
function getWorkspaceEditorPluginDoctorChecks(workspace, editorPlugins) {
|
|
1054
|
+
const checks = [];
|
|
1055
|
+
if (editorPlugins.length > 0) {
|
|
1056
|
+
checks.push(checkWorkspaceEditorPluginBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
1057
|
+
checks.push(checkWorkspaceEditorPluginIndex(workspace.projectDir, editorPlugins));
|
|
1058
|
+
}
|
|
1059
|
+
for (const editorPlugin of editorPlugins) {
|
|
1060
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Editor plugin ${editorPlugin.slug}`, getWorkspaceEditorPluginRequiredFiles(editorPlugin)));
|
|
1061
|
+
checks.push(checkWorkspaceEditorPluginConfig(editorPlugin));
|
|
1062
|
+
}
|
|
1063
|
+
return checks;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features-post-meta.ts
|
|
1067
|
+
import fs11 from "fs";
|
|
1068
|
+
import path11 from "path";
|
|
1069
|
+
function getWorkspacePostMetaRequiredFiles(postMeta) {
|
|
948
1070
|
return Array.from(new Set([
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
path5.join(adminViewDir, "config.ts"),
|
|
953
|
-
path5.join(adminViewDir, "data.ts"),
|
|
954
|
-
path5.join(adminViewDir, "style.scss"),
|
|
955
|
-
path5.join(adminViewDir, "types.ts")
|
|
1071
|
+
postMeta.phpFile,
|
|
1072
|
+
postMeta.schemaFile,
|
|
1073
|
+
postMeta.typesFile
|
|
956
1074
|
]));
|
|
957
1075
|
}
|
|
958
|
-
function
|
|
959
|
-
|
|
960
|
-
|
|
1076
|
+
function checkWorkspacePostMetaConfig(postMeta) {
|
|
1077
|
+
let hasPostType = false;
|
|
1078
|
+
try {
|
|
1079
|
+
hasPostType = assertValidPostMetaPostType(postMeta.postType) === postMeta.postType;
|
|
1080
|
+
} catch {
|
|
1081
|
+
hasPostType = false;
|
|
961
1082
|
}
|
|
962
|
-
const
|
|
963
|
-
const
|
|
964
|
-
|
|
965
|
-
const restResourceSlug = restSourceMatch?.[1];
|
|
966
|
-
const restResource = restResourceSlug ? inventory.restResources.find((entry) => entry.slug === restResourceSlug) : undefined;
|
|
967
|
-
const isListCapableRestResource = Boolean(restResource?.methods.includes("list"));
|
|
968
|
-
const isManualSettingsRestResource = isAdminViewManualSettingsRestResource(restResource);
|
|
969
|
-
const hasManualSettingsRouteParameters = isManualSettingsRestResource && hasAdminViewManualSettingsRouteParameters(restResource);
|
|
970
|
-
const isValid = isListCapableRestResource || isManualSettingsRestResource && !hasManualSettingsRouteParameters || Boolean(coreDataSourceMatch);
|
|
971
|
-
const failDetail = hasManualSettingsRouteParameters ? `Admin view source ${source} uses route parameters or regex groups and cannot scaffold a singleton settings form` : "Admin view source must use rest-resource:<slug> with a list-capable REST resource, a manual settings contract with a body type, or core-data:<postType|taxonomy>/<name>";
|
|
972
|
-
return createDoctorCheck(`Admin view config ${adminView.slug}`, isValid ? "pass" : "fail", isValid ? `Admin view source ${source} is ${isManualSettingsRestResource ? "settings-form capable" : coreDataSourceMatch ? "core-data capable" : "list-capable"}` : failDetail);
|
|
1083
|
+
const hasMetaKey = typeof postMeta.metaKey === "string" && postMeta.metaKey.trim().length > 0 && !/\s/u.test(postMeta.metaKey);
|
|
1084
|
+
const hasRestExposure = typeof postMeta.showInRest === "boolean";
|
|
1085
|
+
return createDoctorCheck(`Post meta config ${postMeta.slug}`, hasPostType && hasMetaKey && hasRestExposure ? "pass" : "fail", hasPostType && hasMetaKey && hasRestExposure ? `Post meta ${postMeta.metaKey} targets ${postMeta.postType}` : "Post meta postType, metaKey, or showInRest configuration is invalid");
|
|
973
1086
|
}
|
|
974
|
-
function
|
|
1087
|
+
function checkWorkspacePostMetaBootstrap(projectDir, packageName, phpPrefix) {
|
|
975
1088
|
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
976
|
-
if (!
|
|
977
|
-
return createDoctorCheck("
|
|
978
|
-
}
|
|
979
|
-
const source = fs5.readFileSync(bootstrapPath, "utf8");
|
|
980
|
-
const loadFunctionName = `${phpPrefix}_load_admin_views`;
|
|
981
|
-
const loadHook = `add_action( 'plugins_loaded', '${loadFunctionName}' );`;
|
|
982
|
-
const hasLoaderHook = source.includes(loadHook);
|
|
983
|
-
const hasServerGlob = source.includes(WORKSPACE_ADMIN_VIEW_GLOB);
|
|
984
|
-
return createDoctorCheck("Admin view bootstrap", hasLoaderHook && hasServerGlob ? "pass" : "fail", hasLoaderHook && hasServerGlob ? "Admin view PHP loader hook is present" : "Missing admin view PHP require glob or plugins_loaded hook");
|
|
985
|
-
}
|
|
986
|
-
function checkWorkspaceAdminViewIndex(projectDir, adminViews) {
|
|
987
|
-
const indexRelativePath = [
|
|
988
|
-
path5.join("src", "admin-views", "index.ts"),
|
|
989
|
-
path5.join("src", "admin-views", "index.js")
|
|
990
|
-
].find((relativePath) => fs5.existsSync(path5.join(projectDir, relativePath)));
|
|
991
|
-
if (!indexRelativePath) {
|
|
992
|
-
return createDoctorCheck("Admin views index", "fail", "Missing src/admin-views/index.ts or src/admin-views/index.js");
|
|
1089
|
+
if (!fs11.existsSync(bootstrapPath)) {
|
|
1090
|
+
return createDoctorCheck("Post meta bootstrap", "fail", `Missing ${path11.basename(bootstrapPath)}`);
|
|
993
1091
|
}
|
|
994
|
-
const
|
|
995
|
-
const
|
|
996
|
-
const
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
return createDoctorCheck("Admin views index", missingImports.length === 0 ? "pass" : "fail", missingImports.length === 0 ? "Admin view registrations are aggregated" : `Missing admin view imports for: ${missingImports.map((entry) => entry.slug).join(", ")}`);
|
|
1092
|
+
const source = fs11.readFileSync(bootstrapPath, "utf8");
|
|
1093
|
+
const registerFunctionName = `${phpPrefix}_register_post_meta_contracts`;
|
|
1094
|
+
const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
|
|
1095
|
+
const hasServerGlob = source.includes(WORKSPACE_POST_META_GLOB);
|
|
1096
|
+
const hasRegisterHook = source.includes(registerHook);
|
|
1097
|
+
return createDoctorCheck("Post meta bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook ? "Post meta PHP loader hook is present" : "Missing post meta PHP require glob or init hook");
|
|
1001
1098
|
}
|
|
1002
|
-
function
|
|
1003
|
-
const phpPath =
|
|
1004
|
-
if (!
|
|
1005
|
-
return createDoctorCheck(`
|
|
1099
|
+
function checkWorkspacePostMetaPhp(projectDir, postMeta) {
|
|
1100
|
+
const phpPath = path11.join(projectDir, postMeta.phpFile);
|
|
1101
|
+
if (!fs11.existsSync(phpPath)) {
|
|
1102
|
+
return createDoctorCheck(`Post meta PHP ${postMeta.slug}`, "fail", `Missing ${postMeta.phpFile}`);
|
|
1006
1103
|
}
|
|
1007
|
-
const source =
|
|
1008
|
-
const
|
|
1009
|
-
const
|
|
1010
|
-
const
|
|
1011
|
-
const
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1014
|
-
return createDoctorCheck(`Admin view PHP ${adminView.slug}`, hasAdminMenu && hasAdminEnqueue && hasScript && hasAsset && hasStyle && hasComponentsStyleDependency ? "pass" : "fail", hasAdminMenu && hasAdminEnqueue && hasScript && hasAsset && hasStyle && hasComponentsStyleDependency ? "Admin menu, script, style, and wp-components style dependency are wired" : "Missing admin menu, enqueue hook, build/admin-views asset reference, or wp-components style dependency");
|
|
1104
|
+
const source = fs11.readFileSync(phpPath, "utf8");
|
|
1105
|
+
const hasRegisterPostMeta = source.includes("register_post_meta");
|
|
1106
|
+
const hasPostType = source.includes(postMeta.postType);
|
|
1107
|
+
const hasMetaKey = source.includes(postMeta.metaKey);
|
|
1108
|
+
const hasSchemaFile = source.includes(postMeta.schemaFile);
|
|
1109
|
+
const hasRestExposure = source.includes("'show_in_rest'");
|
|
1110
|
+
return createDoctorCheck(`Post meta PHP ${postMeta.slug}`, hasRegisterPostMeta && hasPostType && hasMetaKey && hasSchemaFile && hasRestExposure ? "pass" : "fail", hasRegisterPostMeta && hasPostType && hasMetaKey && hasSchemaFile && hasRestExposure ? "Post meta registration, schema path, and REST exposure flag are wired" : "Missing register_post_meta, post type, meta key, schema path, or show_in_rest wiring");
|
|
1015
1111
|
}
|
|
1016
|
-
function
|
|
1112
|
+
function getWorkspacePostMetaDoctorChecks(workspace, postMetaEntries) {
|
|
1017
1113
|
const checks = [];
|
|
1018
|
-
if (
|
|
1019
|
-
checks.push(checkWorkspaceRestResourceBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
1020
|
-
}
|
|
1021
|
-
for (const restResource of inventory.restResources) {
|
|
1022
|
-
checks.push(checkWorkspaceRestResourceConfig(restResource));
|
|
1023
|
-
checks.push(checkExistingFiles(workspace.projectDir, `REST resource ${restResource.slug}`, getWorkspaceRestResourceRequiredFiles(restResource)));
|
|
1024
|
-
}
|
|
1025
|
-
if (inventory.postMeta.length > 0) {
|
|
1114
|
+
if (postMetaEntries.length > 0) {
|
|
1026
1115
|
checks.push(checkWorkspacePostMetaBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
1027
1116
|
}
|
|
1028
|
-
for (const postMeta of
|
|
1117
|
+
for (const postMeta of postMetaEntries) {
|
|
1029
1118
|
checks.push(checkWorkspacePostMetaConfig(postMeta));
|
|
1030
1119
|
checks.push(checkExistingFiles(workspace.projectDir, `Post meta ${postMeta.slug}`, getWorkspacePostMetaRequiredFiles(postMeta)));
|
|
1031
1120
|
checks.push(checkWorkspacePostMetaPhp(workspace.projectDir, postMeta));
|
|
1032
1121
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1122
|
+
return checks;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features-rest.ts
|
|
1126
|
+
import fs12 from "fs";
|
|
1127
|
+
import path12 from "path";
|
|
1128
|
+
function isManualRestResource(restResource) {
|
|
1129
|
+
return restResource.mode === "manual";
|
|
1130
|
+
}
|
|
1131
|
+
function getWorkspaceRestResourceRequiredFiles(restResource) {
|
|
1132
|
+
const schemaNames = new Set;
|
|
1133
|
+
if (isManualRestResource(restResource)) {
|
|
1134
|
+
schemaNames.add("query");
|
|
1135
|
+
if (restResource.bodyTypeName) {
|
|
1136
|
+
schemaNames.add("request");
|
|
1137
|
+
}
|
|
1138
|
+
schemaNames.add("response");
|
|
1139
|
+
return Array.from(new Set([
|
|
1140
|
+
restResource.apiFile,
|
|
1141
|
+
...Array.from(schemaNames, (schemaName) => path12.join(path12.dirname(restResource.typesFile), "api-schemas", `${schemaName}.schema.json`)),
|
|
1142
|
+
restResource.clientFile,
|
|
1143
|
+
restResource.openApiFile,
|
|
1144
|
+
restResource.typesFile,
|
|
1145
|
+
restResource.validatorsFile
|
|
1146
|
+
]));
|
|
1036
1147
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1148
|
+
if (restResource.methods.includes("list")) {
|
|
1149
|
+
schemaNames.add("list-query");
|
|
1150
|
+
schemaNames.add("list-response");
|
|
1040
1151
|
}
|
|
1041
|
-
if (
|
|
1042
|
-
|
|
1152
|
+
if (restResource.methods.includes("read")) {
|
|
1153
|
+
schemaNames.add("read-query");
|
|
1154
|
+
schemaNames.add("read-response");
|
|
1043
1155
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1156
|
+
if (restResource.methods.includes("create")) {
|
|
1157
|
+
schemaNames.add("create-request");
|
|
1158
|
+
schemaNames.add("create-response");
|
|
1047
1159
|
}
|
|
1048
|
-
if (
|
|
1049
|
-
|
|
1050
|
-
|
|
1160
|
+
if (restResource.methods.includes("update")) {
|
|
1161
|
+
schemaNames.add("update-query");
|
|
1162
|
+
schemaNames.add("update-request");
|
|
1163
|
+
schemaNames.add("update-response");
|
|
1051
1164
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1165
|
+
if (restResource.methods.includes("delete")) {
|
|
1166
|
+
schemaNames.add("delete-query");
|
|
1167
|
+
schemaNames.add("delete-response");
|
|
1055
1168
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1169
|
+
return Array.from(new Set([
|
|
1170
|
+
restResource.apiFile,
|
|
1171
|
+
...Array.from(schemaNames, (schemaName) => path12.join(path12.dirname(restResource.typesFile), "api-schemas", `${schemaName}.schema.json`)),
|
|
1172
|
+
restResource.clientFile,
|
|
1173
|
+
...restResource.dataFile ? [restResource.dataFile] : [],
|
|
1174
|
+
restResource.openApiFile,
|
|
1175
|
+
...restResource.phpFile ? [restResource.phpFile] : [],
|
|
1176
|
+
restResource.typesFile,
|
|
1177
|
+
restResource.validatorsFile
|
|
1178
|
+
]));
|
|
1179
|
+
}
|
|
1180
|
+
function checkWorkspaceRestResourceConfig(restResource) {
|
|
1181
|
+
const hasNamespace = REST_RESOURCE_NAMESPACE_PATTERN.test(restResource.namespace);
|
|
1182
|
+
if (isManualRestResource(restResource)) {
|
|
1183
|
+
const hasAuth = restResource.auth == null || MANUAL_REST_CONTRACT_AUTH_IDS.includes(restResource.auth);
|
|
1184
|
+
const hasMethod = typeof restResource.method === "string" && MANUAL_REST_CONTRACT_HTTP_METHOD_IDS.includes(restResource.method);
|
|
1185
|
+
const hasPathPattern = typeof restResource.pathPattern === "string" && restResource.pathPattern.startsWith("/") && restResource.pathPattern.length > 1;
|
|
1186
|
+
return createDoctorCheck(`REST resource config ${restResource.slug}`, hasNamespace && hasAuth && hasMethod && hasPathPattern ? "pass" : "fail", hasNamespace && hasAuth && hasMethod && hasPathPattern ? `Manual REST contract ${restResource.method} /${restResource.namespace}${restResource.pathPattern}` : "Manual REST contract namespace, auth, method, or path pattern is invalid");
|
|
1059
1187
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1188
|
+
const hasMethods = restResource.methods.length > 0 && restResource.methods.every((method) => REST_RESOURCE_METHOD_IDS.includes(method));
|
|
1189
|
+
const hasGeneratedFiles = typeof restResource.dataFile === "string" && restResource.dataFile.length > 0 && typeof restResource.phpFile === "string" && restResource.phpFile.length > 0;
|
|
1190
|
+
const hasRoutePattern = restResource.routePattern == null || typeof restResource.routePattern === "string" && restResource.routePattern.startsWith("/") && restResource.routePattern.length > 1 && !/\s/u.test(restResource.routePattern) && isGeneratedRestResourceRoutePatternCompatible(restResource.routePattern);
|
|
1191
|
+
return createDoctorCheck(`REST resource config ${restResource.slug}`, hasNamespace && hasMethods && hasGeneratedFiles && hasRoutePattern ? "pass" : "fail", hasNamespace && hasMethods && hasGeneratedFiles && hasRoutePattern ? `REST resource namespace ${restResource.namespace} with methods ${restResource.methods.join(", ")}` : "REST resource namespace, methods, dataFile, phpFile, or routePattern are invalid");
|
|
1192
|
+
}
|
|
1193
|
+
function checkWorkspaceRestResourceBootstrap(projectDir, packageName, phpPrefix) {
|
|
1194
|
+
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
1195
|
+
if (!fs12.existsSync(bootstrapPath)) {
|
|
1196
|
+
return createDoctorCheck("REST resource bootstrap", "fail", `Missing ${path12.basename(bootstrapPath)}`);
|
|
1197
|
+
}
|
|
1198
|
+
const source = fs12.readFileSync(bootstrapPath, "utf8");
|
|
1199
|
+
const registerFunctionName = `${phpPrefix}_register_rest_resources`;
|
|
1200
|
+
const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
|
|
1201
|
+
const hasServerGlob = source.includes(WORKSPACE_REST_RESOURCE_GLOB);
|
|
1202
|
+
const hasRegisterHook = source.includes(registerHook);
|
|
1203
|
+
return createDoctorCheck("REST resource bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook ? "REST resource PHP loader hook is present" : "Missing REST resource PHP require glob or init hook");
|
|
1204
|
+
}
|
|
1205
|
+
function getWorkspaceRestResourceDoctorChecks(workspace, restResources) {
|
|
1206
|
+
const checks = [];
|
|
1207
|
+
if (restResources.some((restResource) => !isManualRestResource(restResource))) {
|
|
1208
|
+
checks.push(checkWorkspaceRestResourceBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
1209
|
+
}
|
|
1210
|
+
for (const restResource of restResources) {
|
|
1211
|
+
checks.push(checkWorkspaceRestResourceConfig(restResource));
|
|
1212
|
+
checks.push(checkExistingFiles(workspace.projectDir, `REST resource ${restResource.slug}`, getWorkspaceRestResourceRequiredFiles(restResource)));
|
|
1064
1213
|
}
|
|
1065
1214
|
return checks;
|
|
1066
1215
|
}
|
|
1067
1216
|
|
|
1217
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features.ts
|
|
1218
|
+
function getWorkspaceFeatureDoctorChecks(workspace, inventory) {
|
|
1219
|
+
return [
|
|
1220
|
+
...getWorkspaceRestResourceDoctorChecks(workspace, inventory.restResources),
|
|
1221
|
+
...getWorkspacePostMetaDoctorChecks(workspace, inventory.postMeta),
|
|
1222
|
+
...getWorkspaceAbilityDoctorChecks(workspace, inventory.abilities),
|
|
1223
|
+
...getWorkspaceAiFeatureDoctorChecks(workspace, inventory.aiFeatures),
|
|
1224
|
+
...getWorkspaceEditorPluginDoctorChecks(workspace, inventory.editorPlugins),
|
|
1225
|
+
...getWorkspaceAdminViewDoctorChecks(workspace, inventory)
|
|
1226
|
+
];
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1068
1229
|
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-package.ts
|
|
1069
|
-
import
|
|
1230
|
+
import path13 from "path";
|
|
1070
1231
|
async function prepareWorkspacePackageDoctorSnapshot(workspace, packageJson) {
|
|
1071
1232
|
const packageName = packageJson.name;
|
|
1072
1233
|
const bootstrapRelativePath = getWorkspaceBootstrapRelativePath(typeof packageName === "string" && packageName.length > 0 ? packageName : workspace.packageName);
|
|
1073
|
-
const migrationConfigRelativePath =
|
|
1234
|
+
const migrationConfigRelativePath = path13.join("src", "migrations", "config.ts");
|
|
1074
1235
|
const [bootstrapExists, migrationConfigExists] = await Promise.all([
|
|
1075
|
-
pathExists(
|
|
1076
|
-
pathExists(
|
|
1236
|
+
pathExists(path13.join(workspace.projectDir, bootstrapRelativePath)),
|
|
1237
|
+
pathExists(path13.join(workspace.projectDir, migrationConfigRelativePath))
|
|
1077
1238
|
]);
|
|
1078
1239
|
return {
|
|
1079
1240
|
bootstrapExists,
|
|
@@ -1182,23 +1343,88 @@ async function getWorkspaceDoctorChecks(cwd) {
|
|
|
1182
1343
|
}
|
|
1183
1344
|
|
|
1184
1345
|
// ../wp-typia-project-tools/src/runtime/cli-doctor.ts
|
|
1346
|
+
var DEFAULT_DOCTOR_EXIT_POLICY = "strict";
|
|
1347
|
+
var defaultDoctorLinePrinter = (line) => {
|
|
1348
|
+
process.stdout.write(`${line}
|
|
1349
|
+
`);
|
|
1350
|
+
};
|
|
1351
|
+
function renderDefaultDoctorCheckLine(check) {
|
|
1352
|
+
defaultDoctorLinePrinter(formatDoctorCheckLine(check));
|
|
1353
|
+
}
|
|
1354
|
+
function renderDefaultDoctorSummaryLine(summaryLine) {
|
|
1355
|
+
defaultDoctorLinePrinter(summaryLine);
|
|
1356
|
+
}
|
|
1357
|
+
function annotateDoctorChecks(checks, scope) {
|
|
1358
|
+
return checks.map((check) => ({
|
|
1359
|
+
...check,
|
|
1360
|
+
scope: check.scope ?? scope
|
|
1361
|
+
}));
|
|
1362
|
+
}
|
|
1363
|
+
function resolveDoctorExitPolicy(options) {
|
|
1364
|
+
return options.exitPolicy ?? DEFAULT_DOCTOR_EXIT_POLICY;
|
|
1365
|
+
}
|
|
1366
|
+
function doesCheckContributeToExit(check, exitPolicy) {
|
|
1367
|
+
if (check.status !== "fail") {
|
|
1368
|
+
return false;
|
|
1369
|
+
}
|
|
1370
|
+
if (exitPolicy === "strict") {
|
|
1371
|
+
return true;
|
|
1372
|
+
}
|
|
1373
|
+
return check.scope === "workspace";
|
|
1374
|
+
}
|
|
1375
|
+
function toFailureSummary(check, severity) {
|
|
1376
|
+
return {
|
|
1377
|
+
...check.code ? { code: check.code } : {},
|
|
1378
|
+
label: check.label,
|
|
1379
|
+
scope: check.scope ?? "unknown",
|
|
1380
|
+
severity
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1185
1383
|
async function getDoctorChecks(cwd) {
|
|
1186
1384
|
return [
|
|
1187
|
-
...await getEnvironmentDoctorChecks(cwd),
|
|
1188
|
-
...await getWorkspaceDoctorChecks(cwd)
|
|
1385
|
+
...annotateDoctorChecks(await getEnvironmentDoctorChecks(cwd), "environment"),
|
|
1386
|
+
...annotateDoctorChecks(await getWorkspaceDoctorChecks(cwd), "workspace")
|
|
1189
1387
|
];
|
|
1190
1388
|
}
|
|
1389
|
+
function getDoctorExitFailureChecks(checks, options = {}) {
|
|
1390
|
+
const exitPolicy = resolveDoctorExitPolicy(options);
|
|
1391
|
+
return checks.filter((check) => doesCheckContributeToExit(check, exitPolicy));
|
|
1392
|
+
}
|
|
1393
|
+
function getDoctorExitFailureDetailLines(checks, options = {}) {
|
|
1394
|
+
return getDoctorExitFailureChecks(checks, options).map((check) => `${check.label}: ${check.detail}`);
|
|
1395
|
+
}
|
|
1396
|
+
function createDoctorRunSummary(checks, options = {}) {
|
|
1397
|
+
const exitPolicy = resolveDoctorExitPolicy(options);
|
|
1398
|
+
const failedChecks = checks.filter((check) => check.status === "fail");
|
|
1399
|
+
const exitFailureChecks = failedChecks.filter((check) => doesCheckContributeToExit(check, exitPolicy));
|
|
1400
|
+
const advisoryFailureChecks = failedChecks.filter((check) => !doesCheckContributeToExit(check, exitPolicy));
|
|
1401
|
+
const warnings = checks.filter((check) => check.status === "warn").length;
|
|
1402
|
+
return {
|
|
1403
|
+
advisoryFailureCount: advisoryFailureChecks.length,
|
|
1404
|
+
advisoryFailures: advisoryFailureChecks.map((check) => toFailureSummary(check, "advisory")),
|
|
1405
|
+
exitCode: exitFailureChecks.length > 0 ? 1 : 0,
|
|
1406
|
+
exitFailureCount: exitFailureChecks.length,
|
|
1407
|
+
exitFailures: exitFailureChecks.map((check) => toFailureSummary(check, "error")),
|
|
1408
|
+
exitPolicy,
|
|
1409
|
+
failed: failedChecks.length,
|
|
1410
|
+
passed: checks.length - failedChecks.length - warnings,
|
|
1411
|
+
total: checks.length,
|
|
1412
|
+
warnings
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1191
1415
|
async function runDoctor(cwd, options = {}) {
|
|
1192
|
-
const
|
|
1416
|
+
const exitPolicy = resolveDoctorExitPolicy(options);
|
|
1417
|
+
const renderLine = options.renderLine ?? renderDefaultDoctorCheckLine;
|
|
1193
1418
|
const renderSummaryLine = options.renderSummaryLine ?? (options.renderLine ? () => {
|
|
1194
1419
|
return;
|
|
1195
|
-
} :
|
|
1420
|
+
} : renderDefaultDoctorSummaryLine);
|
|
1196
1421
|
const checks = await getDoctorChecks(cwd);
|
|
1197
1422
|
for (const check of checks) {
|
|
1198
1423
|
renderLine(check);
|
|
1199
1424
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1425
|
+
const exitFailureChecks = getDoctorExitFailureChecks(checks, { exitPolicy });
|
|
1426
|
+
renderSummaryLine(formatDoctorSummaryLine(checks, { exitFailureChecks }));
|
|
1427
|
+
const failureDetailLines = getDoctorExitFailureDetailLines(checks, { exitPolicy });
|
|
1202
1428
|
if (failureDetailLines.length > 0) {
|
|
1203
1429
|
throw createCliCommandError({
|
|
1204
1430
|
code: CLI_DIAGNOSTIC_CODES.DOCTOR_CHECK_FAILED,
|
|
@@ -1211,7 +1437,10 @@ async function runDoctor(cwd, options = {}) {
|
|
|
1211
1437
|
}
|
|
1212
1438
|
export {
|
|
1213
1439
|
runDoctor,
|
|
1214
|
-
|
|
1440
|
+
getDoctorExitFailureDetailLines,
|
|
1441
|
+
getDoctorExitFailureChecks,
|
|
1442
|
+
getDoctorChecks,
|
|
1443
|
+
createDoctorRunSummary
|
|
1215
1444
|
};
|
|
1216
1445
|
|
|
1217
|
-
//# debugId=
|
|
1446
|
+
//# debugId=7D607F817A3CF55764756E2164756E21
|