wp-typia 0.23.0 → 0.23.1
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 +2 -1
- package/bin/routing-metadata.generated.js +4 -0
- package/dist-bunli/.bunli/commands.gen.js +4103 -3086
- package/dist-bunli/{cli-hhp1d348.js → cli-1170yyve.js} +8 -7
- 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-xjaaa01x.js} +1560 -1525
- package/dist-bunli/{cli-52ke0ptp.js → cli-am5x7tb4.js} +8 -2
- package/dist-bunli/cli-ccax7s0s.js +34 -0
- package/dist-bunli/{cli-diagnostics-5dvztm7q.js → cli-diagnostics-10drxh34.js} +1 -1
- package/dist-bunli/{cli-doctor-wy2yjsge.js → cli-doctor-19e8313m.js} +602 -459
- package/dist-bunli/{cli-2rqf6t0b.js → cli-e4bwd81c.js} +8 -11
- package/dist-bunli/{cli-9npd9was.js → cli-epsczb1c.js} +12 -10
- package/dist-bunli/{cli-agywa5n6.js → cli-fp16mntv.js} +8 -4
- package/dist-bunli/{cli-init-xnsbxncv.js → cli-init-2b4yn2cc.js} +14 -10
- package/dist-bunli/{cli-ts9thts5.js → cli-k5q5v8g6.js} +184 -162
- package/dist-bunli/{cli-c2acv5dv.js → cli-nvs5atj1.js} +2 -2
- 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-4tjw4jk5.js} +27 -15
- 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-1meywwsy.js → cli-y7w3pybs.js} +848 -246
- package/dist-bunli/{cli-z5qkx2pn.js → cli-ymecd15q.js} +37 -10
- package/dist-bunli/cli.js +4 -4
- package/dist-bunli/{command-list-aqrkx021.js → command-list-vme7dr5v.js} +81 -45
- package/dist-bunli/{create-template-validation-rtec5sng.js → create-template-validation-4fr851vg.js} +5 -4
- package/dist-bunli/{migrations-bx0yvc2v.js → migrations-pb5vvtdp.js} +9 -8
- package/dist-bunli/node-cli.js +399 -317
- package/dist-bunli/{workspace-project-csnnggz6.js → workspace-project-gmv2a71z.js} +4 -3
- package/package.json +2 -2
|
@@ -9,11 +9,11 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
getBuiltInTemplateLayerDirs,
|
|
11
11
|
isOmittableBuiltInTemplateLayerDir
|
|
12
|
-
} from "./cli-
|
|
12
|
+
} from "./cli-nvs5atj1.js";
|
|
13
13
|
import {
|
|
14
14
|
isBuiltInTemplateId,
|
|
15
15
|
listTemplates
|
|
16
|
-
} from "./cli-
|
|
16
|
+
} from "./cli-8hxf9qw6.js";
|
|
17
17
|
import {
|
|
18
18
|
EDITOR_PLUGIN_SLOT_IDS,
|
|
19
19
|
HOOKED_BLOCK_ANCHOR_PATTERN,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
pathExists,
|
|
29
29
|
readWorkspaceInventoryAsync,
|
|
30
30
|
resolveEditorPluginSlotAlias
|
|
31
|
-
} from "./cli-
|
|
31
|
+
} from "./cli-k5q5v8g6.js";
|
|
32
32
|
import"./cli-cvxvcw7c.js";
|
|
33
33
|
import"./cli-t73q5aqz.js";
|
|
34
34
|
import"./cli-43mx1vfb.js";
|
|
@@ -36,16 +36,18 @@ import {
|
|
|
36
36
|
CLI_DIAGNOSTIC_CODES,
|
|
37
37
|
createCliCommandError,
|
|
38
38
|
formatDoctorCheckLine,
|
|
39
|
-
formatDoctorSummaryLine
|
|
40
|
-
|
|
41
|
-
} from "./cli-p95wr1q8.js";
|
|
39
|
+
formatDoctorSummaryLine
|
|
40
|
+
} from "./cli-tq730sqt.js";
|
|
42
41
|
import {
|
|
43
42
|
WORKSPACE_TEMPLATE_PACKAGE,
|
|
44
43
|
getInvalidWorkspaceProjectReason,
|
|
45
44
|
parseWorkspacePackageJson,
|
|
46
45
|
tryResolveWorkspaceProject
|
|
47
|
-
} from "./cli-
|
|
48
|
-
import"./cli-
|
|
46
|
+
} from "./cli-1170yyve.js";
|
|
47
|
+
import"./cli-am5x7tb4.js";
|
|
48
|
+
import {
|
|
49
|
+
readJsonFileSync
|
|
50
|
+
} from "./cli-ccax7s0s.js";
|
|
49
51
|
import"./cli-xnn9xjcy.js";
|
|
50
52
|
|
|
51
53
|
// ../wp-typia-project-tools/src/runtime/cli-doctor-environment.ts
|
|
@@ -229,7 +231,9 @@ function checkWorkspaceBindingTarget(projectDir, workspace, registeredBlockSlugs
|
|
|
229
231
|
const blockJsonPath = path3.join(projectDir, blockJsonRelativePath);
|
|
230
232
|
const issues = [];
|
|
231
233
|
try {
|
|
232
|
-
const blockJson = parseScaffoldBlockMetadata(
|
|
234
|
+
const blockJson = parseScaffoldBlockMetadata(readJsonFileSync(blockJsonPath, {
|
|
235
|
+
context: "workspace block metadata"
|
|
236
|
+
}));
|
|
233
237
|
const attributes = blockJson.attributes;
|
|
234
238
|
if (!attributes || typeof attributes !== "object" || Array.isArray(attributes)) {
|
|
235
239
|
issues.push(`${blockJsonRelativePath} must define an attributes object`);
|
|
@@ -298,18 +302,125 @@ function getWorkspaceBindingDoctorChecks(workspace, inventory) {
|
|
|
298
302
|
return checks;
|
|
299
303
|
}
|
|
300
304
|
|
|
301
|
-
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-
|
|
305
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-block-addons.ts
|
|
302
306
|
import fs4 from "fs";
|
|
303
307
|
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
308
|
var WORKSPACE_VARIATIONS_IMPORT_PATTERN = /^\s*import\s*\{\s*registerWorkspaceVariations\s*\}\s*from\s*["']\.\/variations["']\s*;?\s*$/mu;
|
|
308
309
|
var WORKSPACE_VARIATIONS_CALL_PATTERN = /registerWorkspaceVariations\s*\(\s*\)\s*;?/u;
|
|
309
310
|
var WORKSPACE_BLOCK_STYLES_IMPORT_PATTERN = /^\s*import\s*\{\s*registerWorkspaceBlockStyles\s*\}\s*from\s*["']\.\/styles["']\s*;?\s*$/mu;
|
|
310
311
|
var WORKSPACE_BLOCK_STYLES_CALL_PATTERN = /registerWorkspaceBlockStyles\s*\(\s*\)\s*;?/u;
|
|
311
312
|
var WORKSPACE_BLOCK_TRANSFORMS_IMPORT_PATTERN = /^\s*import\s*\{\s*applyWorkspaceBlockTransforms\s*\}\s*from\s*["']\.\/transforms["']\s*;?\s*$/mu;
|
|
312
313
|
var WORKSPACE_BLOCK_TRANSFORMS_CALL_PATTERN = /applyWorkspaceBlockTransforms\s*\(\s*registration\s*\.\s*settings\s*\)\s*;?/u;
|
|
314
|
+
function checkWorkspacePatternBootstrap(projectDir, packageName) {
|
|
315
|
+
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
316
|
+
if (!fs4.existsSync(bootstrapPath)) {
|
|
317
|
+
return createDoctorCheck("Pattern bootstrap", "fail", `Missing ${path4.basename(bootstrapPath)}`);
|
|
318
|
+
}
|
|
319
|
+
const source = fs4.readFileSync(bootstrapPath, "utf8");
|
|
320
|
+
const hasCategoryAnchor = source.includes("register_block_pattern_category");
|
|
321
|
+
const hasPatternGlob = source.includes("/src/patterns/*.php");
|
|
322
|
+
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");
|
|
323
|
+
}
|
|
324
|
+
function checkVariationEntrypoint(projectDir, blockSlug) {
|
|
325
|
+
const entryPath = path4.join(projectDir, "src", "blocks", blockSlug, "index.tsx");
|
|
326
|
+
if (!fs4.existsSync(entryPath)) {
|
|
327
|
+
return createDoctorCheck(`Variation entrypoint ${blockSlug}`, "fail", `Missing ${path4.relative(projectDir, entryPath)}`);
|
|
328
|
+
}
|
|
329
|
+
const source = fs4.readFileSync(entryPath, "utf8");
|
|
330
|
+
const hasImport = hasUncommentedPattern(source, WORKSPACE_VARIATIONS_IMPORT_PATTERN);
|
|
331
|
+
const hasCall = hasExecutablePattern(source, WORKSPACE_VARIATIONS_CALL_PATTERN);
|
|
332
|
+
return createDoctorCheck(`Variation entrypoint ${blockSlug}`, hasImport && hasCall ? "pass" : "fail", hasImport && hasCall ? "Variations registration hook is present" : "Missing ./variations import or registerWorkspaceVariations() call");
|
|
333
|
+
}
|
|
334
|
+
function checkBlockStyleEntrypoint(projectDir, blockSlug) {
|
|
335
|
+
const entryPath = path4.join(projectDir, "src", "blocks", blockSlug, "index.tsx");
|
|
336
|
+
if (!fs4.existsSync(entryPath)) {
|
|
337
|
+
return createDoctorCheck(`Block style entrypoint ${blockSlug}`, "fail", `Missing ${path4.relative(projectDir, entryPath)}`);
|
|
338
|
+
}
|
|
339
|
+
const source = fs4.readFileSync(entryPath, "utf8");
|
|
340
|
+
const hasImport = hasUncommentedPattern(source, WORKSPACE_BLOCK_STYLES_IMPORT_PATTERN);
|
|
341
|
+
const hasCall = hasExecutablePattern(source, WORKSPACE_BLOCK_STYLES_CALL_PATTERN);
|
|
342
|
+
return createDoctorCheck(`Block style entrypoint ${blockSlug}`, hasImport && hasCall ? "pass" : "fail", hasImport && hasCall ? "Block style registration hook is present" : "Missing ./styles import or registerWorkspaceBlockStyles() call");
|
|
343
|
+
}
|
|
344
|
+
function checkBlockTransformEntrypoint(projectDir, blockSlug) {
|
|
345
|
+
const entryPath = path4.join(projectDir, "src", "blocks", blockSlug, "index.tsx");
|
|
346
|
+
if (!fs4.existsSync(entryPath)) {
|
|
347
|
+
return createDoctorCheck(`Block transform entrypoint ${blockSlug}`, "fail", `Missing ${path4.relative(projectDir, entryPath)}`);
|
|
348
|
+
}
|
|
349
|
+
const source = fs4.readFileSync(entryPath, "utf8");
|
|
350
|
+
const hasImport = hasUncommentedPattern(source, WORKSPACE_BLOCK_TRANSFORMS_IMPORT_PATTERN);
|
|
351
|
+
const hasCall = hasExecutablePattern(source, WORKSPACE_BLOCK_TRANSFORMS_CALL_PATTERN);
|
|
352
|
+
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");
|
|
353
|
+
}
|
|
354
|
+
function checkBlockTransformConfig(workspace, transform) {
|
|
355
|
+
const expectedTo = `${workspace.workspace.namespace}/${transform.block}`;
|
|
356
|
+
const issues = [];
|
|
357
|
+
if (!WORKSPACE_FULL_BLOCK_NAME_PATTERN.test(transform.from)) {
|
|
358
|
+
issues.push("from must use full namespace/block format");
|
|
359
|
+
}
|
|
360
|
+
if (transform.to !== expectedTo) {
|
|
361
|
+
issues.push(`to must equal "${expectedTo}" for workspace block "${transform.block}"`);
|
|
362
|
+
}
|
|
363
|
+
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("; "));
|
|
364
|
+
}
|
|
365
|
+
function getWorkspaceBlockAddonDoctorChecks(workspace, inventory, registeredBlockSlugs) {
|
|
366
|
+
const checks = [];
|
|
367
|
+
const variationTargetBlocks = new Set;
|
|
368
|
+
for (const variation of inventory.variations) {
|
|
369
|
+
if (!registeredBlockSlugs.has(variation.block)) {
|
|
370
|
+
checks.push(createDoctorCheck(`Variation ${variation.block}/${variation.slug}`, "fail", `Variation references unknown block "${variation.block}"`));
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
variationTargetBlocks.add(variation.block);
|
|
374
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Variation ${variation.block}/${variation.slug}`, [variation.file]));
|
|
375
|
+
}
|
|
376
|
+
for (const blockSlug of variationTargetBlocks) {
|
|
377
|
+
checks.push(checkVariationEntrypoint(workspace.projectDir, blockSlug));
|
|
378
|
+
}
|
|
379
|
+
const blockStyleTargetBlocks = new Set;
|
|
380
|
+
for (const blockStyle of inventory.blockStyles) {
|
|
381
|
+
if (!registeredBlockSlugs.has(blockStyle.block)) {
|
|
382
|
+
checks.push(createDoctorCheck(`Block style ${blockStyle.block}/${blockStyle.slug}`, "fail", `Block style references unknown block "${blockStyle.block}"`));
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
blockStyleTargetBlocks.add(blockStyle.block);
|
|
386
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Block style ${blockStyle.block}/${blockStyle.slug}`, [blockStyle.file]));
|
|
387
|
+
}
|
|
388
|
+
for (const blockSlug of blockStyleTargetBlocks) {
|
|
389
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Block style registry ${blockSlug}`, [
|
|
390
|
+
path4.join("src", "blocks", blockSlug, "styles", "index.ts")
|
|
391
|
+
]));
|
|
392
|
+
checks.push(checkBlockStyleEntrypoint(workspace.projectDir, blockSlug));
|
|
393
|
+
}
|
|
394
|
+
const blockTransformTargetBlocks = new Set;
|
|
395
|
+
for (const blockTransform of inventory.blockTransforms) {
|
|
396
|
+
if (!registeredBlockSlugs.has(blockTransform.block)) {
|
|
397
|
+
checks.push(createDoctorCheck(`Block transform ${blockTransform.block}/${blockTransform.slug}`, "fail", `Block transform references unknown block "${blockTransform.block}"`));
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
blockTransformTargetBlocks.add(blockTransform.block);
|
|
401
|
+
checks.push(checkBlockTransformConfig(workspace, blockTransform));
|
|
402
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Block transform ${blockTransform.block}/${blockTransform.slug}`, [blockTransform.file]));
|
|
403
|
+
}
|
|
404
|
+
for (const blockSlug of blockTransformTargetBlocks) {
|
|
405
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Block transform registry ${blockSlug}`, [
|
|
406
|
+
path4.join("src", "blocks", blockSlug, "transforms", "index.ts")
|
|
407
|
+
]));
|
|
408
|
+
checks.push(checkBlockTransformEntrypoint(workspace.projectDir, blockSlug));
|
|
409
|
+
}
|
|
410
|
+
const shouldCheckPatternBootstrap = inventory.patterns.length > 0 || fs4.existsSync(path4.join(workspace.projectDir, "src", "patterns"));
|
|
411
|
+
if (shouldCheckPatternBootstrap) {
|
|
412
|
+
checks.push(checkWorkspacePatternBootstrap(workspace.projectDir, workspace.packageName));
|
|
413
|
+
}
|
|
414
|
+
for (const pattern of inventory.patterns) {
|
|
415
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Pattern ${pattern.slug}`, [pattern.file]));
|
|
416
|
+
}
|
|
417
|
+
return checks;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-block-iframe.ts
|
|
421
|
+
import fs5 from "fs";
|
|
422
|
+
import path5 from "path";
|
|
423
|
+
import { parseScaffoldBlockMetadata as parseScaffoldBlockMetadata2 } from "@wp-typia/block-runtime/blocks";
|
|
313
424
|
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
425
|
var WORKSPACE_BLOCK_IFRAME_DIAGNOSTIC_CODES = {
|
|
315
426
|
API_VERSION: "wp-typia.workspace.block.iframe.api-version",
|
|
@@ -340,7 +451,7 @@ var WORKSPACE_BLOCK_LOCAL_STYLE_FILES = [
|
|
|
340
451
|
var WORKSPACE_BLOCK_IFRAME_GLOBAL_DOM_PATTERN = /\b(?:document|window)\b|\b(?:parent|top)\b(?!\s*:)/gu;
|
|
341
452
|
var WORKSPACE_BLOCK_PROPS_PATTERN = /\buse(?:Block|InnerBlocks)Props(?:\.save)?\s*\(/u;
|
|
342
453
|
function normalizePathSeparators(relativePath) {
|
|
343
|
-
return relativePath.split(
|
|
454
|
+
return relativePath.split(path5.sep).join("/");
|
|
344
455
|
}
|
|
345
456
|
function hasRegisteredBlockAsset(value) {
|
|
346
457
|
if (typeof value === "string") {
|
|
@@ -352,16 +463,18 @@ function hasRegisteredBlockAsset(value) {
|
|
|
352
463
|
return false;
|
|
353
464
|
}
|
|
354
465
|
function readWorkspaceBlockIframeMetadata(projectDir, blockSlug) {
|
|
355
|
-
const blockJsonRelativePath =
|
|
356
|
-
const blockJsonPath =
|
|
357
|
-
if (!
|
|
466
|
+
const blockJsonRelativePath = path5.join("src", "blocks", blockSlug, "block.json");
|
|
467
|
+
const blockJsonPath = path5.join(projectDir, blockJsonRelativePath);
|
|
468
|
+
if (!fs5.existsSync(blockJsonPath)) {
|
|
358
469
|
return {
|
|
359
470
|
blockJsonRelativePath,
|
|
360
471
|
error: `Missing ${blockJsonRelativePath}`
|
|
361
472
|
};
|
|
362
473
|
}
|
|
363
474
|
try {
|
|
364
|
-
const document = parseScaffoldBlockMetadata2(
|
|
475
|
+
const document = parseScaffoldBlockMetadata2(readJsonFileSync(blockJsonPath, {
|
|
476
|
+
context: "workspace block metadata"
|
|
477
|
+
}));
|
|
365
478
|
return {
|
|
366
479
|
blockJsonRelativePath,
|
|
367
480
|
document
|
|
@@ -378,8 +491,8 @@ function isWorkspaceBlockEditorSource(relativePath) {
|
|
|
378
491
|
return false;
|
|
379
492
|
}
|
|
380
493
|
const normalizedRelativePath = normalizePathSeparators(relativePath);
|
|
381
|
-
const normalizedDirName =
|
|
382
|
-
const normalizedBaseName =
|
|
494
|
+
const normalizedDirName = path5.posix.dirname(normalizedRelativePath);
|
|
495
|
+
const normalizedBaseName = path5.posix.basename(normalizedRelativePath, path5.posix.extname(normalizedRelativePath));
|
|
383
496
|
if (WORKSPACE_BLOCK_EDITOR_SOURCE_BASENAMES.has(normalizedBaseName)) {
|
|
384
497
|
return true;
|
|
385
498
|
}
|
|
@@ -387,12 +500,12 @@ function isWorkspaceBlockEditorSource(relativePath) {
|
|
|
387
500
|
return pathSegments.some((segment) => WORKSPACE_BLOCK_EDITOR_SOURCE_DIRECTORIES.has(segment));
|
|
388
501
|
}
|
|
389
502
|
function isWorkspaceBlockSaveSource(relativePath) {
|
|
390
|
-
const normalizedBaseName =
|
|
503
|
+
const normalizedBaseName = path5.basename(relativePath, path5.extname(relativePath));
|
|
391
504
|
return normalizedBaseName === "save";
|
|
392
505
|
}
|
|
393
506
|
function collectWorkspaceBlockEditorSources(projectDir, blockSlug) {
|
|
394
|
-
const blockDir =
|
|
395
|
-
if (!
|
|
507
|
+
const blockDir = path5.join(projectDir, "src", "blocks", blockSlug);
|
|
508
|
+
if (!fs5.existsSync(blockDir)) {
|
|
396
509
|
return [];
|
|
397
510
|
}
|
|
398
511
|
const collected = [];
|
|
@@ -402,8 +515,8 @@ function collectWorkspaceBlockEditorSources(projectDir, blockSlug) {
|
|
|
402
515
|
if (!currentDir) {
|
|
403
516
|
continue;
|
|
404
517
|
}
|
|
405
|
-
for (const entry of
|
|
406
|
-
const absolutePath =
|
|
518
|
+
for (const entry of fs5.readdirSync(currentDir, { withFileTypes: true })) {
|
|
519
|
+
const absolutePath = path5.join(currentDir, entry.name);
|
|
407
520
|
if (entry.isDirectory()) {
|
|
408
521
|
queue.push(absolutePath);
|
|
409
522
|
continue;
|
|
@@ -411,13 +524,13 @@ function collectWorkspaceBlockEditorSources(projectDir, blockSlug) {
|
|
|
411
524
|
if (!entry.isFile()) {
|
|
412
525
|
continue;
|
|
413
526
|
}
|
|
414
|
-
const relativePath =
|
|
527
|
+
const relativePath = path5.relative(projectDir, absolutePath);
|
|
415
528
|
if (!isWorkspaceBlockEditorSource(relativePath)) {
|
|
416
529
|
continue;
|
|
417
530
|
}
|
|
418
531
|
collected.push({
|
|
419
532
|
relativePath: normalizePathSeparators(relativePath),
|
|
420
|
-
source:
|
|
533
|
+
source: fs5.readFileSync(absolutePath, "utf8")
|
|
421
534
|
});
|
|
422
535
|
}
|
|
423
536
|
}
|
|
@@ -475,25 +588,60 @@ function findWorkspaceBlockGlobalDomAccesses(editorSources) {
|
|
|
475
588
|
return findings;
|
|
476
589
|
});
|
|
477
590
|
}
|
|
591
|
+
function getWorkspaceBlockIframeCompatibilityChecks(projectDir, blockSlug) {
|
|
592
|
+
const metadataResult = readWorkspaceBlockIframeMetadata(projectDir, blockSlug);
|
|
593
|
+
if (!metadataResult.document) {
|
|
594
|
+
return [
|
|
595
|
+
createDoctorCheck(`Block iframe/API v3 ${blockSlug}`, "warn", metadataResult.error ?? `Unable to inspect ${metadataResult.blockJsonRelativePath}`, WORKSPACE_BLOCK_IFRAME_DIAGNOSTIC_CODES.API_VERSION)
|
|
596
|
+
];
|
|
597
|
+
}
|
|
598
|
+
const blockJson = metadataResult.document;
|
|
599
|
+
const apiVersion = typeof blockJson.apiVersion === "number" && Number.isFinite(blockJson.apiVersion) ? blockJson.apiVersion : null;
|
|
600
|
+
const blockDir = path5.join(projectDir, "src", "blocks", blockSlug);
|
|
601
|
+
const localStyleFiles = WORKSPACE_BLOCK_LOCAL_STYLE_FILES.filter((fileName) => fs5.existsSync(path5.join(blockDir, fileName))).map((fileName) => normalizePathSeparators(path5.join("src", "blocks", blockSlug, fileName)));
|
|
602
|
+
const hasRegisteredEditorStyles = hasRegisteredBlockAsset(blockJson.style) || hasRegisteredBlockAsset(blockJson.editorStyle);
|
|
603
|
+
const editorSources = collectWorkspaceBlockEditorSources(projectDir, blockSlug);
|
|
604
|
+
const editorWrapperSources = editorSources.filter((source) => !isWorkspaceBlockSaveSource(source.relativePath));
|
|
605
|
+
const globalDomAccesses = findWorkspaceBlockGlobalDomAccesses(editorSources);
|
|
606
|
+
const hasBlockPropsUsage = editorSources.some(({ source }) => hasExecutablePattern(source, WORKSPACE_BLOCK_PROPS_PATTERN));
|
|
607
|
+
const hasEditorBlockPropsUsage = editorWrapperSources.some(({ source }) => hasExecutablePattern(source, WORKSPACE_BLOCK_PROPS_PATTERN));
|
|
608
|
+
const blockWrapperStatus = editorWrapperSources.length === 0 || hasEditorBlockPropsUsage ? "pass" : "warn";
|
|
609
|
+
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.";
|
|
610
|
+
return [
|
|
611
|
+
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),
|
|
612
|
+
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),
|
|
613
|
+
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),
|
|
614
|
+
createDoctorCheck(`Block iframe wrapper ${blockSlug}`, blockWrapperStatus, blockWrapperDetail, WORKSPACE_BLOCK_IFRAME_DIAGNOSTIC_CODES.BLOCK_PROPS)
|
|
615
|
+
];
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-block-metadata.ts
|
|
619
|
+
import fs6 from "fs";
|
|
620
|
+
import path6 from "path";
|
|
621
|
+
import { parseScaffoldBlockMetadata as parseScaffoldBlockMetadata3 } from "@wp-typia/block-runtime/blocks";
|
|
622
|
+
var WORKSPACE_COLLECTION_IMPORT_LINE = "import '../../collection';";
|
|
623
|
+
var WORKSPACE_COLLECTION_IMPORT_PATTERN = /^\s*import\s+["']\.\.\/\.\.\/collection["']\s*;?\s*$/m;
|
|
478
624
|
function getWorkspaceBlockRequiredFiles(block) {
|
|
479
|
-
const blockDir =
|
|
625
|
+
const blockDir = path6.join("src", "blocks", block.slug);
|
|
480
626
|
return Array.from(new Set([
|
|
481
627
|
block.typesFile,
|
|
482
628
|
block.apiTypesFile,
|
|
483
629
|
block.openApiFile,
|
|
484
|
-
|
|
485
|
-
...WORKSPACE_GENERATED_BLOCK_ARTIFACTS.map((fileName) =>
|
|
630
|
+
path6.join(blockDir, "index.tsx"),
|
|
631
|
+
...WORKSPACE_GENERATED_BLOCK_ARTIFACTS.map((fileName) => path6.join(blockDir, fileName))
|
|
486
632
|
].filter((filePath) => typeof filePath === "string")));
|
|
487
633
|
}
|
|
488
634
|
function checkWorkspaceBlockMetadata(projectDir, workspace, block) {
|
|
489
|
-
const blockJsonRelativePath =
|
|
490
|
-
const blockJsonPath =
|
|
491
|
-
if (!
|
|
635
|
+
const blockJsonRelativePath = path6.join("src", "blocks", block.slug, "block.json");
|
|
636
|
+
const blockJsonPath = path6.join(projectDir, blockJsonRelativePath);
|
|
637
|
+
if (!fs6.existsSync(blockJsonPath)) {
|
|
492
638
|
return createDoctorCheck(`Block metadata ${block.slug}`, "fail", `Missing ${blockJsonRelativePath}`);
|
|
493
639
|
}
|
|
494
640
|
let blockJson;
|
|
495
641
|
try {
|
|
496
|
-
blockJson =
|
|
642
|
+
blockJson = parseScaffoldBlockMetadata3(readJsonFileSync(blockJsonPath, {
|
|
643
|
+
context: "workspace block metadata"
|
|
644
|
+
}));
|
|
497
645
|
} catch (error) {
|
|
498
646
|
return createDoctorCheck(`Block metadata ${block.slug}`, "fail", error instanceof Error ? error.message : String(error));
|
|
499
647
|
}
|
|
@@ -508,14 +656,16 @@ function checkWorkspaceBlockMetadata(projectDir, workspace, block) {
|
|
|
508
656
|
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
657
|
}
|
|
510
658
|
function checkWorkspaceBlockHooks(projectDir, blockSlug) {
|
|
511
|
-
const blockJsonRelativePath =
|
|
512
|
-
const blockJsonPath =
|
|
513
|
-
if (!
|
|
659
|
+
const blockJsonRelativePath = path6.join("src", "blocks", blockSlug, "block.json");
|
|
660
|
+
const blockJsonPath = path6.join(projectDir, blockJsonRelativePath);
|
|
661
|
+
if (!fs6.existsSync(blockJsonPath)) {
|
|
514
662
|
return createDoctorCheck(`Block hooks ${blockSlug}`, "fail", `Missing ${blockJsonRelativePath}`);
|
|
515
663
|
}
|
|
516
664
|
let blockJson;
|
|
517
665
|
try {
|
|
518
|
-
blockJson =
|
|
666
|
+
blockJson = parseScaffoldBlockMetadata3(readJsonFileSync(blockJsonPath, {
|
|
667
|
+
context: "workspace block metadata"
|
|
668
|
+
}));
|
|
519
669
|
} catch (error) {
|
|
520
670
|
return createDoctorCheck(`Block hooks ${blockSlug}`, "fail", error instanceof Error ? error.message : String(error));
|
|
521
671
|
}
|
|
@@ -531,348 +681,214 @@ function checkWorkspaceBlockHooks(projectDir, blockSlug) {
|
|
|
531
681
|
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
682
|
}
|
|
533
683
|
function checkWorkspaceBlockCollectionImport(projectDir, blockSlug) {
|
|
534
|
-
const entryRelativePath =
|
|
535
|
-
const entryPath =
|
|
536
|
-
if (!
|
|
684
|
+
const entryRelativePath = path6.join("src", "blocks", blockSlug, "index.tsx");
|
|
685
|
+
const entryPath = path6.join(projectDir, entryRelativePath);
|
|
686
|
+
if (!fs6.existsSync(entryPath)) {
|
|
537
687
|
return createDoctorCheck(`Block collection ${blockSlug}`, "fail", `Missing ${entryRelativePath}`);
|
|
538
688
|
}
|
|
539
|
-
const source =
|
|
689
|
+
const source = fs6.readFileSync(entryPath, "utf8");
|
|
540
690
|
const hasCollectionImport = WORKSPACE_COLLECTION_IMPORT_PATTERN.test(source);
|
|
541
691
|
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
692
|
}
|
|
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.";
|
|
693
|
+
function getWorkspaceBlockCoreDoctorChecks(workspace, block) {
|
|
562
694
|
return [
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
695
|
+
checkExistingFiles(workspace.projectDir, `Block ${block.slug}`, getWorkspaceBlockRequiredFiles(block)),
|
|
696
|
+
checkWorkspaceBlockMetadata(workspace.projectDir, workspace, block),
|
|
697
|
+
checkWorkspaceBlockHooks(workspace.projectDir, block.slug),
|
|
698
|
+
checkWorkspaceBlockCollectionImport(workspace.projectDir, block.slug)
|
|
567
699
|
];
|
|
568
700
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
701
|
+
|
|
702
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-blocks.ts
|
|
703
|
+
function getWorkspaceBlockDoctorChecks(workspace, inventory) {
|
|
704
|
+
const checks = [];
|
|
705
|
+
for (const block of inventory.blocks) {
|
|
706
|
+
checks.push(...getWorkspaceBlockCoreDoctorChecks(workspace, block));
|
|
707
|
+
checks.push(...getWorkspaceBlockIframeCompatibilityChecks(workspace.projectDir, block.slug));
|
|
573
708
|
}
|
|
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");
|
|
709
|
+
const registeredBlockSlugs = new Set(inventory.blocks.map((block) => block.slug));
|
|
710
|
+
checks.push(...getWorkspaceBlockAddonDoctorChecks(workspace, inventory, registeredBlockSlugs));
|
|
711
|
+
return checks;
|
|
578
712
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
713
|
+
|
|
714
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features-abilities.ts
|
|
715
|
+
import fs7 from "fs";
|
|
716
|
+
import path7 from "path";
|
|
717
|
+
function getWorkspaceAbilityRequiredFiles(ability) {
|
|
718
|
+
return Array.from(new Set([
|
|
719
|
+
ability.clientFile,
|
|
720
|
+
ability.configFile,
|
|
721
|
+
ability.dataFile,
|
|
722
|
+
ability.inputSchemaFile,
|
|
723
|
+
ability.outputSchemaFile,
|
|
724
|
+
ability.phpFile,
|
|
725
|
+
ability.typesFile
|
|
726
|
+
]));
|
|
588
727
|
}
|
|
589
|
-
function
|
|
590
|
-
const
|
|
591
|
-
if (!
|
|
592
|
-
return createDoctorCheck(`
|
|
728
|
+
function checkWorkspaceAbilityConfig(projectDir, ability) {
|
|
729
|
+
const configPath = path7.join(projectDir, ability.configFile);
|
|
730
|
+
if (!fs7.existsSync(configPath)) {
|
|
731
|
+
return createDoctorCheck(`Ability config ${ability.slug}`, "fail", `Missing ${ability.configFile}`);
|
|
593
732
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
return createDoctorCheck(`
|
|
733
|
+
try {
|
|
734
|
+
const config = readJsonFileSync(configPath, {
|
|
735
|
+
context: "workspace ability config"
|
|
736
|
+
});
|
|
737
|
+
const abilityId = typeof config.abilityId === "string" ? config.abilityId.trim() : "";
|
|
738
|
+
const categorySlug = typeof config.category?.slug === "string" ? config.category.slug.trim() : "";
|
|
739
|
+
const hasValidAbilityId = /^[a-z0-9-]+\/[a-z0-9-]+$/u.test(abilityId);
|
|
740
|
+
const hasValidCategorySlug = /^[a-z0-9-]+$/u.test(categorySlug);
|
|
741
|
+
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.");
|
|
742
|
+
} catch (error) {
|
|
743
|
+
return createDoctorCheck(`Ability config ${ability.slug}`, "fail", error instanceof Error ? error.message : String(error));
|
|
603
744
|
}
|
|
604
|
-
const source = fs4.readFileSync(entryPath, "utf8");
|
|
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
745
|
}
|
|
609
|
-
function
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
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}"`);
|
|
746
|
+
function checkWorkspaceAbilityBootstrap(projectDir, packageName, phpPrefix) {
|
|
747
|
+
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
748
|
+
if (!fs7.existsSync(bootstrapPath)) {
|
|
749
|
+
return createDoctorCheck("Ability bootstrap", "fail", `Missing ${path7.basename(bootstrapPath)}`);
|
|
617
750
|
}
|
|
618
|
-
|
|
751
|
+
const source = fs7.readFileSync(bootstrapPath, "utf8");
|
|
752
|
+
const loadFunctionName = `${phpPrefix}_load_workflow_abilities`;
|
|
753
|
+
const enqueueFunctionName = `${phpPrefix}_enqueue_workflow_abilities`;
|
|
754
|
+
const loadHook = `add_action( 'plugins_loaded', '${loadFunctionName}' );`;
|
|
755
|
+
const adminEnqueueHook = `add_action( 'admin_enqueue_scripts', '${enqueueFunctionName}' );`;
|
|
756
|
+
const editorEnqueueHook = `add_action( 'enqueue_block_editor_assets', '${enqueueFunctionName}' );`;
|
|
757
|
+
const hasLoaderHook = source.includes(loadHook);
|
|
758
|
+
const hasAdminEnqueueHook = source.includes(adminEnqueueHook);
|
|
759
|
+
const hasEditorEnqueueHook = source.includes(editorEnqueueHook);
|
|
760
|
+
const hasServerGlob = source.includes(WORKSPACE_ABILITY_GLOB);
|
|
761
|
+
const hasEditorScript = source.includes(WORKSPACE_ABILITY_EDITOR_SCRIPT);
|
|
762
|
+
const hasEditorAsset = source.includes(WORKSPACE_ABILITY_EDITOR_ASSET);
|
|
763
|
+
const hasScriptModuleEnqueue = source.includes("wp_enqueue_script_module");
|
|
764
|
+
return createDoctorCheck("Ability bootstrap", hasLoaderHook && hasAdminEnqueueHook && hasEditorEnqueueHook && hasServerGlob && hasEditorScript && hasEditorAsset && hasScriptModuleEnqueue ? "pass" : "fail", hasLoaderHook && hasAdminEnqueueHook && hasEditorEnqueueHook && hasServerGlob && hasEditorScript && hasEditorAsset && hasScriptModuleEnqueue ? "Ability loader and admin/editor script-module bootstrap hooks are present" : "Missing ability loader hook, script-module enqueue, or build/abilities asset references");
|
|
619
765
|
}
|
|
620
|
-
function
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
checks.push(...checkWorkspaceBlockIframeCompatibility(workspace.projectDir, block.slug));
|
|
766
|
+
function checkWorkspaceAbilityIndex(projectDir, abilities) {
|
|
767
|
+
const indexRelativePath = [
|
|
768
|
+
path7.join("src", "abilities", "index.ts"),
|
|
769
|
+
path7.join("src", "abilities", "index.js")
|
|
770
|
+
].find((relativePath) => fs7.existsSync(path7.join(projectDir, relativePath)));
|
|
771
|
+
if (!indexRelativePath) {
|
|
772
|
+
return createDoctorCheck("Abilities index", "fail", "Missing src/abilities/index.ts or src/abilities/index.js");
|
|
628
773
|
}
|
|
629
|
-
const
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
checks.push(
|
|
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));
|
|
774
|
+
const indexPath = path7.join(projectDir, indexRelativePath);
|
|
775
|
+
const source = fs7.readFileSync(indexPath, "utf8");
|
|
776
|
+
const missingExports = abilities.filter((ability) => {
|
|
777
|
+
const exportPattern = new RegExp(`^\\s*export\\s+(?:\\*\\s+from|\\{[^}]+\\}\\s+from)\\s+['"\`]\\./${escapeRegex(ability.slug)}\\/client['"\`]`, "mu");
|
|
778
|
+
return !exportPattern.test(source);
|
|
779
|
+
});
|
|
780
|
+
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(", ")}`);
|
|
781
|
+
}
|
|
782
|
+
function getWorkspaceAbilityDoctorChecks(workspace, abilities) {
|
|
783
|
+
const checks = [];
|
|
784
|
+
if (abilities.length > 0) {
|
|
785
|
+
checks.push(checkWorkspaceAbilityBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
786
|
+
checks.push(checkWorkspaceAbilityIndex(workspace.projectDir, abilities));
|
|
676
787
|
}
|
|
677
|
-
for (const
|
|
678
|
-
checks.push(
|
|
788
|
+
for (const ability of abilities) {
|
|
789
|
+
checks.push(checkWorkspaceAbilityConfig(workspace.projectDir, ability));
|
|
790
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Ability ${ability.slug}`, getWorkspaceAbilityRequiredFiles(ability)));
|
|
679
791
|
}
|
|
680
792
|
return checks;
|
|
681
793
|
}
|
|
682
794
|
|
|
683
|
-
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features.ts
|
|
684
|
-
import
|
|
685
|
-
import
|
|
686
|
-
function
|
|
687
|
-
|
|
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) {
|
|
795
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features-admin-views.ts
|
|
796
|
+
import fs8 from "fs";
|
|
797
|
+
import path8 from "path";
|
|
798
|
+
function getWorkspaceAdminViewRequiredFiles(adminView) {
|
|
799
|
+
const adminViewDir = path8.join("src", "admin-views", adminView.slug);
|
|
807
800
|
return Array.from(new Set([
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
801
|
+
adminView.file,
|
|
802
|
+
adminView.phpFile,
|
|
803
|
+
path8.join(adminViewDir, "Screen.tsx"),
|
|
804
|
+
path8.join(adminViewDir, "config.ts"),
|
|
805
|
+
path8.join(adminViewDir, "data.ts"),
|
|
806
|
+
path8.join(adminViewDir, "style.scss"),
|
|
807
|
+
path8.join(adminViewDir, "types.ts")
|
|
815
808
|
]));
|
|
816
809
|
}
|
|
817
|
-
function
|
|
818
|
-
|
|
819
|
-
|
|
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));
|
|
810
|
+
function checkWorkspaceAdminViewConfig(adminView, inventory) {
|
|
811
|
+
if (adminView.source === undefined) {
|
|
812
|
+
return createDoctorCheck(`Admin view config ${adminView.slug}`, "pass", "Admin view uses a replaceable local fetcher");
|
|
831
813
|
}
|
|
814
|
+
const source = adminView.source.trim();
|
|
815
|
+
const restSourceMatch = /^rest-resource:([a-z][a-z0-9-]*)$/u.exec(source);
|
|
816
|
+
const coreDataSourceMatch = /^core-data:(postType|taxonomy)\/([a-z0-9][a-z0-9_-]*)$/u.exec(source);
|
|
817
|
+
const restResourceSlug = restSourceMatch?.[1];
|
|
818
|
+
const restResource = restResourceSlug ? inventory.restResources.find((entry) => entry.slug === restResourceSlug) : undefined;
|
|
819
|
+
const isListCapableRestResource = Boolean(restResource?.methods.includes("list"));
|
|
820
|
+
const isManualSettingsRestResource = isAdminViewManualSettingsRestResource(restResource);
|
|
821
|
+
const hasManualSettingsRouteParameters = isManualSettingsRestResource && hasAdminViewManualSettingsRouteParameters(restResource);
|
|
822
|
+
const isValid = isListCapableRestResource || isManualSettingsRestResource && !hasManualSettingsRouteParameters || Boolean(coreDataSourceMatch);
|
|
823
|
+
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>";
|
|
824
|
+
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);
|
|
832
825
|
}
|
|
833
|
-
function
|
|
826
|
+
function checkWorkspaceAdminViewBootstrap(projectDir, packageName, phpPrefix) {
|
|
834
827
|
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
835
|
-
if (!
|
|
836
|
-
return createDoctorCheck("
|
|
828
|
+
if (!fs8.existsSync(bootstrapPath)) {
|
|
829
|
+
return createDoctorCheck("Admin view bootstrap", "fail", `Missing ${path8.basename(bootstrapPath)}`);
|
|
837
830
|
}
|
|
838
|
-
const source =
|
|
839
|
-
const loadFunctionName = `${phpPrefix}
|
|
840
|
-
const enqueueFunctionName = `${phpPrefix}_enqueue_workflow_abilities`;
|
|
831
|
+
const source = fs8.readFileSync(bootstrapPath, "utf8");
|
|
832
|
+
const loadFunctionName = `${phpPrefix}_load_admin_views`;
|
|
841
833
|
const loadHook = `add_action( 'plugins_loaded', '${loadFunctionName}' );`;
|
|
842
|
-
const adminEnqueueHook = `add_action( 'admin_enqueue_scripts', '${enqueueFunctionName}' );`;
|
|
843
|
-
const editorEnqueueHook = `add_action( 'enqueue_block_editor_assets', '${enqueueFunctionName}' );`;
|
|
844
834
|
const hasLoaderHook = source.includes(loadHook);
|
|
845
|
-
const
|
|
846
|
-
|
|
847
|
-
const hasServerGlob = source.includes(WORKSPACE_ABILITY_GLOB);
|
|
848
|
-
const hasEditorScript = source.includes(WORKSPACE_ABILITY_EDITOR_SCRIPT);
|
|
849
|
-
const hasEditorAsset = source.includes(WORKSPACE_ABILITY_EDITOR_ASSET);
|
|
850
|
-
const hasScriptModuleEnqueue = source.includes("wp_enqueue_script_module");
|
|
851
|
-
return createDoctorCheck("Ability bootstrap", hasLoaderHook && hasAdminEnqueueHook && hasEditorEnqueueHook && hasServerGlob && hasEditorScript && hasEditorAsset && hasScriptModuleEnqueue ? "pass" : "fail", hasLoaderHook && hasAdminEnqueueHook && hasEditorEnqueueHook && hasServerGlob && hasEditorScript && hasEditorAsset && hasScriptModuleEnqueue ? "Ability loader and admin/editor script-module bootstrap hooks are present" : "Missing ability loader hook, script-module enqueue, or build/abilities asset references");
|
|
835
|
+
const hasServerGlob = source.includes(WORKSPACE_ADMIN_VIEW_GLOB);
|
|
836
|
+
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");
|
|
852
837
|
}
|
|
853
|
-
function
|
|
838
|
+
function checkWorkspaceAdminViewIndex(projectDir, adminViews) {
|
|
854
839
|
const indexRelativePath = [
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
].find((relativePath) =>
|
|
840
|
+
path8.join("src", "admin-views", "index.ts"),
|
|
841
|
+
path8.join("src", "admin-views", "index.js")
|
|
842
|
+
].find((relativePath) => fs8.existsSync(path8.join(projectDir, relativePath)));
|
|
858
843
|
if (!indexRelativePath) {
|
|
859
|
-
return createDoctorCheck("
|
|
844
|
+
return createDoctorCheck("Admin views index", "fail", "Missing src/admin-views/index.ts or src/admin-views/index.js");
|
|
860
845
|
}
|
|
861
|
-
const indexPath =
|
|
862
|
-
const source =
|
|
863
|
-
const
|
|
864
|
-
const
|
|
865
|
-
return !
|
|
846
|
+
const indexPath = path8.join(projectDir, indexRelativePath);
|
|
847
|
+
const source = fs8.readFileSync(indexPath, "utf8");
|
|
848
|
+
const missingImports = adminViews.filter((adminView) => {
|
|
849
|
+
const importPattern = new RegExp(`['"\`]\\./${escapeRegex(adminView.slug)}(?:/[^'"\`]*)?['"\`]`, "u");
|
|
850
|
+
return !importPattern.test(source);
|
|
866
851
|
});
|
|
867
|
-
return createDoctorCheck("
|
|
852
|
+
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(", ")}`);
|
|
853
|
+
}
|
|
854
|
+
function checkWorkspaceAdminViewPhp(projectDir, adminView) {
|
|
855
|
+
const phpPath = path8.join(projectDir, adminView.phpFile);
|
|
856
|
+
if (!fs8.existsSync(phpPath)) {
|
|
857
|
+
return createDoctorCheck(`Admin view PHP ${adminView.slug}`, "fail", `Missing ${adminView.phpFile}`);
|
|
858
|
+
}
|
|
859
|
+
const source = fs8.readFileSync(phpPath, "utf8");
|
|
860
|
+
const hasAdminMenu = source.includes("add_submenu_page");
|
|
861
|
+
const hasAdminEnqueue = source.includes("admin_enqueue_scripts");
|
|
862
|
+
const hasScript = source.includes(WORKSPACE_ADMIN_VIEW_SCRIPT);
|
|
863
|
+
const hasAsset = source.includes(WORKSPACE_ADMIN_VIEW_ASSET);
|
|
864
|
+
const hasStyle = source.includes(WORKSPACE_ADMIN_VIEW_STYLE);
|
|
865
|
+
const hasComponentsStyleDependency = /['"]wp-components['"]/u.test(source);
|
|
866
|
+
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");
|
|
868
867
|
}
|
|
868
|
+
function getWorkspaceAdminViewDoctorChecks(workspace, inventory) {
|
|
869
|
+
const checks = [];
|
|
870
|
+
if (inventory.adminViews.length > 0) {
|
|
871
|
+
checks.push(checkWorkspaceAdminViewBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
872
|
+
checks.push(checkWorkspaceAdminViewIndex(workspace.projectDir, inventory.adminViews));
|
|
873
|
+
}
|
|
874
|
+
for (const adminView of inventory.adminViews) {
|
|
875
|
+
checks.push(checkWorkspaceAdminViewConfig(adminView, inventory));
|
|
876
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Admin view ${adminView.slug}`, getWorkspaceAdminViewRequiredFiles(adminView)));
|
|
877
|
+
checks.push(checkWorkspaceAdminViewPhp(workspace.projectDir, adminView));
|
|
878
|
+
}
|
|
879
|
+
return checks;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features-ai.ts
|
|
883
|
+
import fs9 from "fs";
|
|
884
|
+
import path9 from "path";
|
|
869
885
|
function getWorkspaceAiFeatureRequiredFiles(aiFeature) {
|
|
870
886
|
return Array.from(new Set([
|
|
871
887
|
aiFeature.aiSchemaFile,
|
|
872
888
|
aiFeature.apiFile,
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
889
|
+
path9.join(path9.dirname(aiFeature.typesFile), "api-schemas", "feature-request.schema.json"),
|
|
890
|
+
path9.join(path9.dirname(aiFeature.typesFile), "api-schemas", "feature-response.schema.json"),
|
|
891
|
+
path9.join(path9.dirname(aiFeature.typesFile), "api-schemas", "feature-result.schema.json"),
|
|
876
892
|
aiFeature.clientFile,
|
|
877
893
|
aiFeature.dataFile,
|
|
878
894
|
aiFeature.openApiFile,
|
|
@@ -887,25 +903,40 @@ function checkWorkspaceAiFeatureConfig(aiFeature) {
|
|
|
887
903
|
}
|
|
888
904
|
function checkWorkspaceAiFeatureBootstrap(projectDir, packageName, phpPrefix) {
|
|
889
905
|
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
890
|
-
if (!
|
|
891
|
-
return createDoctorCheck("AI feature bootstrap", "fail", `Missing ${
|
|
906
|
+
if (!fs9.existsSync(bootstrapPath)) {
|
|
907
|
+
return createDoctorCheck("AI feature bootstrap", "fail", `Missing ${path9.basename(bootstrapPath)}`);
|
|
892
908
|
}
|
|
893
|
-
const source =
|
|
909
|
+
const source = fs9.readFileSync(bootstrapPath, "utf8");
|
|
894
910
|
const registerFunctionName = `${phpPrefix}_register_ai_features`;
|
|
895
911
|
const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
|
|
896
912
|
const hasServerGlob = source.includes(WORKSPACE_AI_FEATURE_GLOB);
|
|
897
913
|
const hasRegisterHook = source.includes(registerHook);
|
|
898
914
|
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
915
|
}
|
|
916
|
+
function getWorkspaceAiFeatureDoctorChecks(workspace, aiFeatures) {
|
|
917
|
+
const checks = [];
|
|
918
|
+
if (aiFeatures.length > 0) {
|
|
919
|
+
checks.push(checkWorkspaceAiFeatureBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
920
|
+
}
|
|
921
|
+
for (const aiFeature of aiFeatures) {
|
|
922
|
+
checks.push(checkWorkspaceAiFeatureConfig(aiFeature));
|
|
923
|
+
checks.push(checkExistingFiles(workspace.projectDir, `AI feature ${aiFeature.slug}`, getWorkspaceAiFeatureRequiredFiles(aiFeature)));
|
|
924
|
+
}
|
|
925
|
+
return checks;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features-editor-plugins.ts
|
|
929
|
+
import fs10 from "fs";
|
|
930
|
+
import path10 from "path";
|
|
900
931
|
function getWorkspaceEditorPluginRequiredFiles(editorPlugin) {
|
|
901
|
-
const editorPluginDir =
|
|
902
|
-
const surfaceFile = editorPlugin.slot === "PluginSidebar" ?
|
|
932
|
+
const editorPluginDir = path10.join("src", "editor-plugins", editorPlugin.slug);
|
|
933
|
+
const surfaceFile = editorPlugin.slot === "PluginSidebar" ? path10.join(editorPluginDir, "Sidebar.tsx") : path10.join(editorPluginDir, "Surface.tsx");
|
|
903
934
|
return Array.from(new Set([
|
|
904
935
|
editorPlugin.file,
|
|
905
936
|
surfaceFile,
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
937
|
+
path10.join(editorPluginDir, "data.ts"),
|
|
938
|
+
path10.join(editorPluginDir, "types.ts"),
|
|
939
|
+
path10.join(editorPluginDir, "style.scss")
|
|
909
940
|
]));
|
|
910
941
|
}
|
|
911
942
|
function checkWorkspaceEditorPluginConfig(editorPlugin) {
|
|
@@ -915,10 +946,10 @@ function checkWorkspaceEditorPluginConfig(editorPlugin) {
|
|
|
915
946
|
}
|
|
916
947
|
function checkWorkspaceEditorPluginBootstrap(projectDir, packageName, phpPrefix) {
|
|
917
948
|
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
918
|
-
if (!
|
|
919
|
-
return createDoctorCheck("Editor plugin bootstrap", "fail", `Missing ${
|
|
949
|
+
if (!fs10.existsSync(bootstrapPath)) {
|
|
950
|
+
return createDoctorCheck("Editor plugin bootstrap", "fail", `Missing ${path10.basename(bootstrapPath)}`);
|
|
920
951
|
}
|
|
921
|
-
const source =
|
|
952
|
+
const source = fs10.readFileSync(bootstrapPath, "utf8");
|
|
922
953
|
const enqueueFunctionName = `${phpPrefix}_enqueue_editor_plugins_editor`;
|
|
923
954
|
const enqueueHook = `add_action( 'enqueue_block_editor_assets', '${enqueueFunctionName}' );`;
|
|
924
955
|
const hasEditorEnqueueHook = source.includes(enqueueHook);
|
|
@@ -929,151 +960,205 @@ function checkWorkspaceEditorPluginBootstrap(projectDir, packageName, phpPrefix)
|
|
|
929
960
|
}
|
|
930
961
|
function checkWorkspaceEditorPluginIndex(projectDir, editorPlugins) {
|
|
931
962
|
const indexRelativePath = [
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
].find((relativePath) =>
|
|
963
|
+
path10.join("src", "editor-plugins", "index.ts"),
|
|
964
|
+
path10.join("src", "editor-plugins", "index.js")
|
|
965
|
+
].find((relativePath) => fs10.existsSync(path10.join(projectDir, relativePath)));
|
|
935
966
|
if (!indexRelativePath) {
|
|
936
967
|
return createDoctorCheck("Editor plugins index", "fail", "Missing src/editor-plugins/index.ts or src/editor-plugins/index.js");
|
|
937
968
|
}
|
|
938
|
-
const indexPath =
|
|
939
|
-
const source =
|
|
969
|
+
const indexPath = path10.join(projectDir, indexRelativePath);
|
|
970
|
+
const source = fs10.readFileSync(indexPath, "utf8");
|
|
940
971
|
const missingImports = editorPlugins.filter((editorPlugin) => {
|
|
941
972
|
const importPattern = new RegExp(`['"\`]\\./${escapeRegex(editorPlugin.slug)}(?:/[^'"\`]*)?['"\`]`, "u");
|
|
942
973
|
return !importPattern.test(source);
|
|
943
974
|
});
|
|
944
975
|
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
976
|
}
|
|
946
|
-
function
|
|
947
|
-
const
|
|
977
|
+
function getWorkspaceEditorPluginDoctorChecks(workspace, editorPlugins) {
|
|
978
|
+
const checks = [];
|
|
979
|
+
if (editorPlugins.length > 0) {
|
|
980
|
+
checks.push(checkWorkspaceEditorPluginBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
981
|
+
checks.push(checkWorkspaceEditorPluginIndex(workspace.projectDir, editorPlugins));
|
|
982
|
+
}
|
|
983
|
+
for (const editorPlugin of editorPlugins) {
|
|
984
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Editor plugin ${editorPlugin.slug}`, getWorkspaceEditorPluginRequiredFiles(editorPlugin)));
|
|
985
|
+
checks.push(checkWorkspaceEditorPluginConfig(editorPlugin));
|
|
986
|
+
}
|
|
987
|
+
return checks;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features-post-meta.ts
|
|
991
|
+
import fs11 from "fs";
|
|
992
|
+
import path11 from "path";
|
|
993
|
+
function getWorkspacePostMetaRequiredFiles(postMeta) {
|
|
948
994
|
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")
|
|
995
|
+
postMeta.phpFile,
|
|
996
|
+
postMeta.schemaFile,
|
|
997
|
+
postMeta.typesFile
|
|
956
998
|
]));
|
|
957
999
|
}
|
|
958
|
-
function
|
|
959
|
-
|
|
960
|
-
|
|
1000
|
+
function checkWorkspacePostMetaConfig(postMeta) {
|
|
1001
|
+
let hasPostType = false;
|
|
1002
|
+
try {
|
|
1003
|
+
hasPostType = assertValidPostMetaPostType(postMeta.postType) === postMeta.postType;
|
|
1004
|
+
} catch {
|
|
1005
|
+
hasPostType = false;
|
|
961
1006
|
}
|
|
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);
|
|
1007
|
+
const hasMetaKey = typeof postMeta.metaKey === "string" && postMeta.metaKey.trim().length > 0 && !/\s/u.test(postMeta.metaKey);
|
|
1008
|
+
const hasRestExposure = typeof postMeta.showInRest === "boolean";
|
|
1009
|
+
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
1010
|
}
|
|
974
|
-
function
|
|
1011
|
+
function checkWorkspacePostMetaBootstrap(projectDir, packageName, phpPrefix) {
|
|
975
1012
|
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
976
|
-
if (!
|
|
977
|
-
return createDoctorCheck("
|
|
1013
|
+
if (!fs11.existsSync(bootstrapPath)) {
|
|
1014
|
+
return createDoctorCheck("Post meta bootstrap", "fail", `Missing ${path11.basename(bootstrapPath)}`);
|
|
978
1015
|
}
|
|
979
|
-
const source =
|
|
980
|
-
const
|
|
981
|
-
const
|
|
982
|
-
const
|
|
983
|
-
const
|
|
984
|
-
return createDoctorCheck("
|
|
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");
|
|
993
|
-
}
|
|
994
|
-
const indexPath = path5.join(projectDir, indexRelativePath);
|
|
995
|
-
const source = fs5.readFileSync(indexPath, "utf8");
|
|
996
|
-
const missingImports = adminViews.filter((adminView) => {
|
|
997
|
-
const importPattern = new RegExp(`['"\`]\\./${escapeRegex(adminView.slug)}(?:/[^'"\`]*)?['"\`]`, "u");
|
|
998
|
-
return !importPattern.test(source);
|
|
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(", ")}`);
|
|
1016
|
+
const source = fs11.readFileSync(bootstrapPath, "utf8");
|
|
1017
|
+
const registerFunctionName = `${phpPrefix}_register_post_meta_contracts`;
|
|
1018
|
+
const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
|
|
1019
|
+
const hasServerGlob = source.includes(WORKSPACE_POST_META_GLOB);
|
|
1020
|
+
const hasRegisterHook = source.includes(registerHook);
|
|
1021
|
+
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
1022
|
}
|
|
1002
|
-
function
|
|
1003
|
-
const phpPath =
|
|
1004
|
-
if (!
|
|
1005
|
-
return createDoctorCheck(`
|
|
1023
|
+
function checkWorkspacePostMetaPhp(projectDir, postMeta) {
|
|
1024
|
+
const phpPath = path11.join(projectDir, postMeta.phpFile);
|
|
1025
|
+
if (!fs11.existsSync(phpPath)) {
|
|
1026
|
+
return createDoctorCheck(`Post meta PHP ${postMeta.slug}`, "fail", `Missing ${postMeta.phpFile}`);
|
|
1006
1027
|
}
|
|
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");
|
|
1028
|
+
const source = fs11.readFileSync(phpPath, "utf8");
|
|
1029
|
+
const hasRegisterPostMeta = source.includes("register_post_meta");
|
|
1030
|
+
const hasPostType = source.includes(postMeta.postType);
|
|
1031
|
+
const hasMetaKey = source.includes(postMeta.metaKey);
|
|
1032
|
+
const hasSchemaFile = source.includes(postMeta.schemaFile);
|
|
1033
|
+
const hasRestExposure = source.includes("'show_in_rest'");
|
|
1034
|
+
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
1035
|
}
|
|
1016
|
-
function
|
|
1036
|
+
function getWorkspacePostMetaDoctorChecks(workspace, postMetaEntries) {
|
|
1017
1037
|
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) {
|
|
1038
|
+
if (postMetaEntries.length > 0) {
|
|
1026
1039
|
checks.push(checkWorkspacePostMetaBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
1027
1040
|
}
|
|
1028
|
-
for (const postMeta of
|
|
1041
|
+
for (const postMeta of postMetaEntries) {
|
|
1029
1042
|
checks.push(checkWorkspacePostMetaConfig(postMeta));
|
|
1030
1043
|
checks.push(checkExistingFiles(workspace.projectDir, `Post meta ${postMeta.slug}`, getWorkspacePostMetaRequiredFiles(postMeta)));
|
|
1031
1044
|
checks.push(checkWorkspacePostMetaPhp(workspace.projectDir, postMeta));
|
|
1032
1045
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1046
|
+
return checks;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features-rest.ts
|
|
1050
|
+
import fs12 from "fs";
|
|
1051
|
+
import path12 from "path";
|
|
1052
|
+
function isManualRestResource(restResource) {
|
|
1053
|
+
return restResource.mode === "manual";
|
|
1054
|
+
}
|
|
1055
|
+
function getWorkspaceRestResourceRequiredFiles(restResource) {
|
|
1056
|
+
const schemaNames = new Set;
|
|
1057
|
+
if (isManualRestResource(restResource)) {
|
|
1058
|
+
schemaNames.add("query");
|
|
1059
|
+
if (restResource.bodyTypeName) {
|
|
1060
|
+
schemaNames.add("request");
|
|
1061
|
+
}
|
|
1062
|
+
schemaNames.add("response");
|
|
1063
|
+
return Array.from(new Set([
|
|
1064
|
+
restResource.apiFile,
|
|
1065
|
+
...Array.from(schemaNames, (schemaName) => path12.join(path12.dirname(restResource.typesFile), "api-schemas", `${schemaName}.schema.json`)),
|
|
1066
|
+
restResource.clientFile,
|
|
1067
|
+
restResource.openApiFile,
|
|
1068
|
+
restResource.typesFile,
|
|
1069
|
+
restResource.validatorsFile
|
|
1070
|
+
]));
|
|
1036
1071
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1072
|
+
if (restResource.methods.includes("list")) {
|
|
1073
|
+
schemaNames.add("list-query");
|
|
1074
|
+
schemaNames.add("list-response");
|
|
1040
1075
|
}
|
|
1041
|
-
if (
|
|
1042
|
-
|
|
1076
|
+
if (restResource.methods.includes("read")) {
|
|
1077
|
+
schemaNames.add("read-query");
|
|
1078
|
+
schemaNames.add("read-response");
|
|
1043
1079
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1080
|
+
if (restResource.methods.includes("create")) {
|
|
1081
|
+
schemaNames.add("create-request");
|
|
1082
|
+
schemaNames.add("create-response");
|
|
1047
1083
|
}
|
|
1048
|
-
if (
|
|
1049
|
-
|
|
1050
|
-
|
|
1084
|
+
if (restResource.methods.includes("update")) {
|
|
1085
|
+
schemaNames.add("update-query");
|
|
1086
|
+
schemaNames.add("update-request");
|
|
1087
|
+
schemaNames.add("update-response");
|
|
1051
1088
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1089
|
+
if (restResource.methods.includes("delete")) {
|
|
1090
|
+
schemaNames.add("delete-query");
|
|
1091
|
+
schemaNames.add("delete-response");
|
|
1055
1092
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1093
|
+
return Array.from(new Set([
|
|
1094
|
+
restResource.apiFile,
|
|
1095
|
+
...Array.from(schemaNames, (schemaName) => path12.join(path12.dirname(restResource.typesFile), "api-schemas", `${schemaName}.schema.json`)),
|
|
1096
|
+
restResource.clientFile,
|
|
1097
|
+
...restResource.dataFile ? [restResource.dataFile] : [],
|
|
1098
|
+
restResource.openApiFile,
|
|
1099
|
+
...restResource.phpFile ? [restResource.phpFile] : [],
|
|
1100
|
+
restResource.typesFile,
|
|
1101
|
+
restResource.validatorsFile
|
|
1102
|
+
]));
|
|
1103
|
+
}
|
|
1104
|
+
function checkWorkspaceRestResourceConfig(restResource) {
|
|
1105
|
+
const hasNamespace = REST_RESOURCE_NAMESPACE_PATTERN.test(restResource.namespace);
|
|
1106
|
+
if (isManualRestResource(restResource)) {
|
|
1107
|
+
const hasAuth = restResource.auth == null || MANUAL_REST_CONTRACT_AUTH_IDS.includes(restResource.auth);
|
|
1108
|
+
const hasMethod = typeof restResource.method === "string" && MANUAL_REST_CONTRACT_HTTP_METHOD_IDS.includes(restResource.method);
|
|
1109
|
+
const hasPathPattern = typeof restResource.pathPattern === "string" && restResource.pathPattern.startsWith("/") && restResource.pathPattern.length > 1;
|
|
1110
|
+
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
1111
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1112
|
+
const hasMethods = restResource.methods.length > 0 && restResource.methods.every((method) => REST_RESOURCE_METHOD_IDS.includes(method));
|
|
1113
|
+
const hasGeneratedFiles = typeof restResource.dataFile === "string" && restResource.dataFile.length > 0 && typeof restResource.phpFile === "string" && restResource.phpFile.length > 0;
|
|
1114
|
+
const hasRoutePattern = restResource.routePattern == null || typeof restResource.routePattern === "string" && restResource.routePattern.startsWith("/") && restResource.routePattern.length > 1 && !/\s/u.test(restResource.routePattern) && isGeneratedRestResourceRoutePatternCompatible(restResource.routePattern);
|
|
1115
|
+
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");
|
|
1116
|
+
}
|
|
1117
|
+
function checkWorkspaceRestResourceBootstrap(projectDir, packageName, phpPrefix) {
|
|
1118
|
+
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
1119
|
+
if (!fs12.existsSync(bootstrapPath)) {
|
|
1120
|
+
return createDoctorCheck("REST resource bootstrap", "fail", `Missing ${path12.basename(bootstrapPath)}`);
|
|
1121
|
+
}
|
|
1122
|
+
const source = fs12.readFileSync(bootstrapPath, "utf8");
|
|
1123
|
+
const registerFunctionName = `${phpPrefix}_register_rest_resources`;
|
|
1124
|
+
const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
|
|
1125
|
+
const hasServerGlob = source.includes(WORKSPACE_REST_RESOURCE_GLOB);
|
|
1126
|
+
const hasRegisterHook = source.includes(registerHook);
|
|
1127
|
+
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");
|
|
1128
|
+
}
|
|
1129
|
+
function getWorkspaceRestResourceDoctorChecks(workspace, restResources) {
|
|
1130
|
+
const checks = [];
|
|
1131
|
+
if (restResources.some((restResource) => !isManualRestResource(restResource))) {
|
|
1132
|
+
checks.push(checkWorkspaceRestResourceBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
1133
|
+
}
|
|
1134
|
+
for (const restResource of restResources) {
|
|
1135
|
+
checks.push(checkWorkspaceRestResourceConfig(restResource));
|
|
1136
|
+
checks.push(checkExistingFiles(workspace.projectDir, `REST resource ${restResource.slug}`, getWorkspaceRestResourceRequiredFiles(restResource)));
|
|
1064
1137
|
}
|
|
1065
1138
|
return checks;
|
|
1066
1139
|
}
|
|
1067
1140
|
|
|
1141
|
+
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-features.ts
|
|
1142
|
+
function getWorkspaceFeatureDoctorChecks(workspace, inventory) {
|
|
1143
|
+
return [
|
|
1144
|
+
...getWorkspaceRestResourceDoctorChecks(workspace, inventory.restResources),
|
|
1145
|
+
...getWorkspacePostMetaDoctorChecks(workspace, inventory.postMeta),
|
|
1146
|
+
...getWorkspaceAbilityDoctorChecks(workspace, inventory.abilities),
|
|
1147
|
+
...getWorkspaceAiFeatureDoctorChecks(workspace, inventory.aiFeatures),
|
|
1148
|
+
...getWorkspaceEditorPluginDoctorChecks(workspace, inventory.editorPlugins),
|
|
1149
|
+
...getWorkspaceAdminViewDoctorChecks(workspace, inventory)
|
|
1150
|
+
];
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1068
1153
|
// ../wp-typia-project-tools/src/runtime/cli-doctor-workspace-package.ts
|
|
1069
|
-
import
|
|
1154
|
+
import path13 from "path";
|
|
1070
1155
|
async function prepareWorkspacePackageDoctorSnapshot(workspace, packageJson) {
|
|
1071
1156
|
const packageName = packageJson.name;
|
|
1072
1157
|
const bootstrapRelativePath = getWorkspaceBootstrapRelativePath(typeof packageName === "string" && packageName.length > 0 ? packageName : workspace.packageName);
|
|
1073
|
-
const migrationConfigRelativePath =
|
|
1158
|
+
const migrationConfigRelativePath = path13.join("src", "migrations", "config.ts");
|
|
1074
1159
|
const [bootstrapExists, migrationConfigExists] = await Promise.all([
|
|
1075
|
-
pathExists(
|
|
1076
|
-
pathExists(
|
|
1160
|
+
pathExists(path13.join(workspace.projectDir, bootstrapRelativePath)),
|
|
1161
|
+
pathExists(path13.join(workspace.projectDir, migrationConfigRelativePath))
|
|
1077
1162
|
]);
|
|
1078
1163
|
return {
|
|
1079
1164
|
bootstrapExists,
|
|
@@ -1182,13 +1267,67 @@ async function getWorkspaceDoctorChecks(cwd) {
|
|
|
1182
1267
|
}
|
|
1183
1268
|
|
|
1184
1269
|
// ../wp-typia-project-tools/src/runtime/cli-doctor.ts
|
|
1270
|
+
var DEFAULT_DOCTOR_EXIT_POLICY = "strict";
|
|
1271
|
+
function annotateDoctorChecks(checks, scope) {
|
|
1272
|
+
return checks.map((check) => ({
|
|
1273
|
+
...check,
|
|
1274
|
+
scope: check.scope ?? scope
|
|
1275
|
+
}));
|
|
1276
|
+
}
|
|
1277
|
+
function resolveDoctorExitPolicy(options) {
|
|
1278
|
+
return options.exitPolicy ?? DEFAULT_DOCTOR_EXIT_POLICY;
|
|
1279
|
+
}
|
|
1280
|
+
function doesCheckContributeToExit(check, exitPolicy) {
|
|
1281
|
+
if (check.status !== "fail") {
|
|
1282
|
+
return false;
|
|
1283
|
+
}
|
|
1284
|
+
if (exitPolicy === "strict") {
|
|
1285
|
+
return true;
|
|
1286
|
+
}
|
|
1287
|
+
return check.scope === "workspace";
|
|
1288
|
+
}
|
|
1289
|
+
function toFailureSummary(check, severity) {
|
|
1290
|
+
return {
|
|
1291
|
+
...check.code ? { code: check.code } : {},
|
|
1292
|
+
label: check.label,
|
|
1293
|
+
scope: check.scope ?? "unknown",
|
|
1294
|
+
severity
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1185
1297
|
async function getDoctorChecks(cwd) {
|
|
1186
1298
|
return [
|
|
1187
|
-
...await getEnvironmentDoctorChecks(cwd),
|
|
1188
|
-
...await getWorkspaceDoctorChecks(cwd)
|
|
1299
|
+
...annotateDoctorChecks(await getEnvironmentDoctorChecks(cwd), "environment"),
|
|
1300
|
+
...annotateDoctorChecks(await getWorkspaceDoctorChecks(cwd), "workspace")
|
|
1189
1301
|
];
|
|
1190
1302
|
}
|
|
1303
|
+
function getDoctorExitFailureChecks(checks, options = {}) {
|
|
1304
|
+
const exitPolicy = resolveDoctorExitPolicy(options);
|
|
1305
|
+
return checks.filter((check) => doesCheckContributeToExit(check, exitPolicy));
|
|
1306
|
+
}
|
|
1307
|
+
function getDoctorExitFailureDetailLines(checks, options = {}) {
|
|
1308
|
+
return getDoctorExitFailureChecks(checks, options).map((check) => `${check.label}: ${check.detail}`);
|
|
1309
|
+
}
|
|
1310
|
+
function createDoctorRunSummary(checks, options = {}) {
|
|
1311
|
+
const exitPolicy = resolveDoctorExitPolicy(options);
|
|
1312
|
+
const failedChecks = checks.filter((check) => check.status === "fail");
|
|
1313
|
+
const exitFailureChecks = failedChecks.filter((check) => doesCheckContributeToExit(check, exitPolicy));
|
|
1314
|
+
const advisoryFailureChecks = failedChecks.filter((check) => !doesCheckContributeToExit(check, exitPolicy));
|
|
1315
|
+
const warnings = checks.filter((check) => check.status === "warn").length;
|
|
1316
|
+
return {
|
|
1317
|
+
advisoryFailureCount: advisoryFailureChecks.length,
|
|
1318
|
+
advisoryFailures: advisoryFailureChecks.map((check) => toFailureSummary(check, "advisory")),
|
|
1319
|
+
exitCode: exitFailureChecks.length > 0 ? 1 : 0,
|
|
1320
|
+
exitFailureCount: exitFailureChecks.length,
|
|
1321
|
+
exitFailures: exitFailureChecks.map((check) => toFailureSummary(check, "error")),
|
|
1322
|
+
exitPolicy,
|
|
1323
|
+
failed: failedChecks.length,
|
|
1324
|
+
passed: checks.length - failedChecks.length - warnings,
|
|
1325
|
+
total: checks.length,
|
|
1326
|
+
warnings
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1191
1329
|
async function runDoctor(cwd, options = {}) {
|
|
1330
|
+
const exitPolicy = resolveDoctorExitPolicy(options);
|
|
1192
1331
|
const renderLine = options.renderLine ?? ((check) => console.log(formatDoctorCheckLine(check)));
|
|
1193
1332
|
const renderSummaryLine = options.renderSummaryLine ?? (options.renderLine ? () => {
|
|
1194
1333
|
return;
|
|
@@ -1197,8 +1336,9 @@ async function runDoctor(cwd, options = {}) {
|
|
|
1197
1336
|
for (const check of checks) {
|
|
1198
1337
|
renderLine(check);
|
|
1199
1338
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1339
|
+
const exitFailureChecks = getDoctorExitFailureChecks(checks, { exitPolicy });
|
|
1340
|
+
renderSummaryLine(formatDoctorSummaryLine(checks, { exitFailureChecks }));
|
|
1341
|
+
const failureDetailLines = getDoctorExitFailureDetailLines(checks, { exitPolicy });
|
|
1202
1342
|
if (failureDetailLines.length > 0) {
|
|
1203
1343
|
throw createCliCommandError({
|
|
1204
1344
|
code: CLI_DIAGNOSTIC_CODES.DOCTOR_CHECK_FAILED,
|
|
@@ -1211,7 +1351,10 @@ async function runDoctor(cwd, options = {}) {
|
|
|
1211
1351
|
}
|
|
1212
1352
|
export {
|
|
1213
1353
|
runDoctor,
|
|
1214
|
-
|
|
1354
|
+
getDoctorExitFailureDetailLines,
|
|
1355
|
+
getDoctorExitFailureChecks,
|
|
1356
|
+
getDoctorChecks,
|
|
1357
|
+
createDoctorRunSummary
|
|
1215
1358
|
};
|
|
1216
1359
|
|
|
1217
|
-
//# debugId=
|
|
1360
|
+
//# debugId=3074C8F7DE85281764756E2164756E21
|