@wp-typia/project-tools 0.22.10 → 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/dist/runtime/ai-feature-artifacts.js +4 -1
- package/dist/runtime/block-generator-service-spec.js +2 -1
- package/dist/runtime/cli-add-block-json.js +5 -1
- package/dist/runtime/cli-add-collision.d.ts +25 -0
- package/dist/runtime/cli-add-collision.js +76 -0
- package/dist/runtime/cli-add-help.js +12 -2
- package/dist/runtime/cli-add-kind-ids.d.ts +1 -1
- package/dist/runtime/cli-add-kind-ids.js +3 -0
- package/dist/runtime/cli-add-types.d.ts +129 -0
- package/dist/runtime/cli-add-types.js +26 -0
- package/dist/runtime/cli-add-validation.d.ts +97 -1
- package/dist/runtime/cli-add-validation.js +313 -1
- package/dist/runtime/cli-add-workspace-ability-scaffold.js +4 -1
- package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +79 -20
- package/dist/runtime/cli-add-workspace-admin-view-source.js +11 -2
- package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.d.ts +34 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.js +483 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-default.d.ts +30 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-default.js +310 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-rest.d.ts +25 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-rest.js +124 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-settings.d.ts +34 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-settings.js +370 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-shared.d.ts +49 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates-shared.js +259 -0
- package/dist/runtime/cli-add-workspace-admin-view-templates.d.ts +19 -10
- package/dist/runtime/cli-add-workspace-admin-view-templates.js +31 -971
- package/dist/runtime/cli-add-workspace-admin-view-types.d.ts +21 -0
- package/dist/runtime/cli-add-workspace-admin-view-types.js +22 -0
- package/dist/runtime/cli-add-workspace-ai-anchors.js +125 -32
- package/dist/runtime/cli-add-workspace-ai-source-emitters.js +17 -1
- package/dist/runtime/cli-add-workspace-contract-source-emitters.d.ts +15 -0
- package/dist/runtime/cli-add-workspace-contract-source-emitters.js +42 -0
- package/dist/runtime/cli-add-workspace-contract.d.ts +15 -0
- package/dist/runtime/cli-add-workspace-contract.js +65 -0
- package/dist/runtime/cli-add-workspace-integration-env.d.ts +26 -0
- package/dist/runtime/cli-add-workspace-integration-env.js +428 -0
- package/dist/runtime/cli-add-workspace-post-meta-anchors.d.ts +23 -0
- package/dist/runtime/cli-add-workspace-post-meta-anchors.js +244 -0
- package/dist/runtime/cli-add-workspace-post-meta-source-emitters.d.ts +63 -0
- package/dist/runtime/cli-add-workspace-post-meta-source-emitters.js +179 -0
- package/dist/runtime/cli-add-workspace-post-meta.d.ts +15 -0
- package/dist/runtime/cli-add-workspace-post-meta.js +107 -0
- package/dist/runtime/cli-add-workspace-rest-anchors.d.ts +9 -0
- package/dist/runtime/cli-add-workspace-rest-anchors.js +326 -21
- package/dist/runtime/cli-add-workspace-rest-generated.d.ts +9 -0
- package/dist/runtime/cli-add-workspace-rest-generated.js +158 -0
- package/dist/runtime/cli-add-workspace-rest-manual.d.ts +8 -0
- package/dist/runtime/cli-add-workspace-rest-manual.js +279 -0
- package/dist/runtime/cli-add-workspace-rest-php-templates.d.ts +24 -0
- package/dist/runtime/cli-add-workspace-rest-php-templates.js +678 -0
- package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +98 -2
- package/dist/runtime/cli-add-workspace-rest-source-emitters.js +323 -29
- package/dist/runtime/cli-add-workspace-rest-types.d.ts +108 -0
- package/dist/runtime/cli-add-workspace-rest-types.js +1 -0
- package/dist/runtime/cli-add-workspace-rest.d.ts +3 -7
- package/dist/runtime/cli-add-workspace-rest.js +34 -481
- package/dist/runtime/cli-add-workspace.d.ts +15 -0
- package/dist/runtime/cli-add-workspace.js +15 -0
- package/dist/runtime/cli-add.d.ts +1 -1
- package/dist/runtime/cli-add.js +1 -1
- package/dist/runtime/cli-core.d.ts +3 -2
- package/dist/runtime/cli-core.js +3 -2
- package/dist/runtime/cli-diagnostics.d.ts +3 -1
- package/dist/runtime/cli-diagnostics.js +17 -5
- package/dist/runtime/cli-doctor-environment.js +1 -3
- package/dist/runtime/cli-doctor-workspace-bindings.js +4 -1
- package/dist/runtime/cli-doctor-workspace-block-addons.d.ts +12 -0
- package/dist/runtime/cli-doctor-workspace-block-addons.js +134 -0
- package/dist/runtime/cli-doctor-workspace-block-iframe.d.ts +9 -0
- package/dist/runtime/cli-doctor-workspace-block-iframe.js +228 -0
- package/dist/runtime/cli-doctor-workspace-block-metadata.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-block-metadata.js +111 -0
- package/dist/runtime/cli-doctor-workspace-blocks.js +6 -424
- package/dist/runtime/cli-doctor-workspace-features-abilities.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-abilities.js +112 -0
- package/dist/runtime/cli-doctor-workspace-features-admin-views.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-admin-views.js +128 -0
- package/dist/runtime/cli-doctor-workspace-features-ai.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-ai.js +57 -0
- package/dist/runtime/cli-doctor-workspace-features-editor-plugins.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-editor-plugins.js +80 -0
- package/dist/runtime/cli-doctor-workspace-features-post-meta.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-post-meta.js +77 -0
- package/dist/runtime/cli-doctor-workspace-features-rest.d.ts +11 -0
- package/dist/runtime/cli-doctor-workspace-features-rest.js +120 -0
- package/dist/runtime/cli-doctor-workspace-features.js +14 -369
- package/dist/runtime/cli-doctor-workspace-package.d.ts +25 -3
- package/dist/runtime/cli-doctor-workspace-package.js +35 -13
- package/dist/runtime/cli-doctor-workspace-shared.d.ts +2 -0
- package/dist/runtime/cli-doctor-workspace-shared.js +2 -0
- package/dist/runtime/cli-doctor-workspace.js +8 -3
- package/dist/runtime/cli-doctor.d.ts +52 -3
- package/dist/runtime/cli-doctor.js +79 -8
- package/dist/runtime/cli-help.js +10 -0
- package/dist/runtime/cli-init-package-json.js +4 -2
- package/dist/runtime/cli-init-templates.js +11 -1
- package/dist/runtime/cli-prompt.d.ts +16 -2
- package/dist/runtime/cli-prompt.js +29 -12
- package/dist/runtime/cli-scaffold.d.ts +2 -1
- package/dist/runtime/cli-scaffold.js +19 -10
- package/dist/runtime/contract-artifacts.d.ts +14 -0
- package/dist/runtime/contract-artifacts.js +15 -0
- package/dist/runtime/external-template-guards.js +4 -6
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/json-utils.d.ts +62 -4
- package/dist/runtime/json-utils.js +78 -4
- package/dist/runtime/local-dev-presets.js +4 -1
- package/dist/runtime/migration-ui-capability.js +4 -1
- package/dist/runtime/migration-utils.js +4 -1
- package/dist/runtime/package-managers.js +6 -1
- package/dist/runtime/package-versions.js +6 -1
- package/dist/runtime/rest-resource-artifacts.d.ts +57 -1
- package/dist/runtime/rest-resource-artifacts.js +97 -1
- package/dist/runtime/scaffold-bootstrap.js +7 -2
- package/dist/runtime/scaffold-package-manager-files.js +5 -1
- package/dist/runtime/scaffold-repository-reference.js +4 -2
- package/dist/runtime/scaffold-template-variables.js +2 -1
- package/dist/runtime/scaffold.d.ts +18 -1
- package/dist/runtime/scaffold.js +55 -2
- package/dist/runtime/temp-roots.js +4 -1
- package/dist/runtime/template-layers.js +4 -1
- package/dist/runtime/template-registry.js +9 -3
- package/dist/runtime/template-render.d.ts +1 -1
- package/dist/runtime/template-render.js +1 -1
- package/dist/runtime/template-source-cache-markers.d.ts +37 -0
- package/dist/runtime/template-source-cache-markers.js +125 -0
- package/dist/runtime/template-source-cache.d.ts +1 -4
- package/dist/runtime/template-source-cache.js +16 -122
- package/dist/runtime/template-source-contracts.d.ts +2 -0
- package/dist/runtime/template-source-external.d.ts +4 -2
- package/dist/runtime/template-source-external.js +4 -2
- package/dist/runtime/template-source-normalization.js +2 -1
- package/dist/runtime/template-source-remote.d.ts +8 -4
- package/dist/runtime/template-source-remote.js +26 -9
- package/dist/runtime/template-source-seeds.js +10 -3
- package/dist/runtime/workspace-inventory-mutations.js +54 -4
- package/dist/runtime/workspace-inventory-parser-entries.d.ts +17 -0
- package/dist/runtime/workspace-inventory-parser-entries.js +157 -0
- package/dist/runtime/workspace-inventory-parser-validation.d.ts +104 -0
- package/dist/runtime/workspace-inventory-parser-validation.js +34 -0
- package/dist/runtime/workspace-inventory-parser.d.ts +3 -44
- package/dist/runtime/workspace-inventory-parser.js +7 -464
- package/dist/runtime/workspace-inventory-read.d.ts +9 -2
- package/dist/runtime/workspace-inventory-read.js +9 -2
- package/dist/runtime/workspace-inventory-section-descriptors.d.ts +19 -0
- package/dist/runtime/workspace-inventory-section-descriptors.js +435 -0
- package/dist/runtime/workspace-inventory-templates.d.ts +16 -1
- package/dist/runtime/workspace-inventory-templates.js +75 -4
- package/dist/runtime/workspace-inventory-types.d.ts +52 -2
- package/dist/runtime/workspace-inventory.d.ts +2 -2
- package/dist/runtime/workspace-inventory.js +1 -1
- package/dist/runtime/workspace-project.js +4 -6
- package/package.json +2 -2
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { hasAdminViewManualSettingsRouteParameters, isAdminViewManualSettingsRestResource, } from "./cli-add-workspace-admin-view-types.js";
|
|
4
|
+
import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_ADMIN_VIEW_ASSET, WORKSPACE_ADMIN_VIEW_GLOB, WORKSPACE_ADMIN_VIEW_SCRIPT, WORKSPACE_ADMIN_VIEW_STYLE, } from "./cli-doctor-workspace-shared.js";
|
|
5
|
+
import { escapeRegex } from "./php-utils.js";
|
|
6
|
+
function getWorkspaceAdminViewRequiredFiles(adminView) {
|
|
7
|
+
const adminViewDir = path.join("src", "admin-views", adminView.slug);
|
|
8
|
+
return Array.from(new Set([
|
|
9
|
+
adminView.file,
|
|
10
|
+
adminView.phpFile,
|
|
11
|
+
path.join(adminViewDir, "Screen.tsx"),
|
|
12
|
+
path.join(adminViewDir, "config.ts"),
|
|
13
|
+
path.join(adminViewDir, "data.ts"),
|
|
14
|
+
path.join(adminViewDir, "style.scss"),
|
|
15
|
+
path.join(adminViewDir, "types.ts"),
|
|
16
|
+
]));
|
|
17
|
+
}
|
|
18
|
+
function checkWorkspaceAdminViewConfig(adminView, inventory) {
|
|
19
|
+
if (adminView.source === undefined) {
|
|
20
|
+
return createDoctorCheck(`Admin view config ${adminView.slug}`, "pass", "Admin view uses a replaceable local fetcher");
|
|
21
|
+
}
|
|
22
|
+
const source = adminView.source.trim();
|
|
23
|
+
const restSourceMatch = /^rest-resource:([a-z][a-z0-9-]*)$/u.exec(source);
|
|
24
|
+
const coreDataSourceMatch = /^core-data:(postType|taxonomy)\/([a-z0-9][a-z0-9_-]*)$/u.exec(source);
|
|
25
|
+
const restResourceSlug = restSourceMatch?.[1];
|
|
26
|
+
const restResource = restResourceSlug
|
|
27
|
+
? inventory.restResources.find((entry) => entry.slug === restResourceSlug)
|
|
28
|
+
: undefined;
|
|
29
|
+
const isListCapableRestResource = Boolean(restResource?.methods.includes("list"));
|
|
30
|
+
const isManualSettingsRestResource = isAdminViewManualSettingsRestResource(restResource);
|
|
31
|
+
const hasManualSettingsRouteParameters = isManualSettingsRestResource &&
|
|
32
|
+
hasAdminViewManualSettingsRouteParameters(restResource);
|
|
33
|
+
const isValid = isListCapableRestResource ||
|
|
34
|
+
(isManualSettingsRestResource && !hasManualSettingsRouteParameters) ||
|
|
35
|
+
Boolean(coreDataSourceMatch);
|
|
36
|
+
const failDetail = hasManualSettingsRouteParameters
|
|
37
|
+
? `Admin view source ${source} uses route parameters or regex groups and cannot scaffold a singleton settings form`
|
|
38
|
+
: "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>";
|
|
39
|
+
return createDoctorCheck(`Admin view config ${adminView.slug}`, isValid ? "pass" : "fail", isValid
|
|
40
|
+
? `Admin view source ${source} is ${isManualSettingsRestResource
|
|
41
|
+
? "settings-form capable"
|
|
42
|
+
: coreDataSourceMatch
|
|
43
|
+
? "core-data capable"
|
|
44
|
+
: "list-capable"}`
|
|
45
|
+
: failDetail);
|
|
46
|
+
}
|
|
47
|
+
function checkWorkspaceAdminViewBootstrap(projectDir, packageName, phpPrefix) {
|
|
48
|
+
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
49
|
+
if (!fs.existsSync(bootstrapPath)) {
|
|
50
|
+
return createDoctorCheck("Admin view bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
|
|
51
|
+
}
|
|
52
|
+
const source = fs.readFileSync(bootstrapPath, "utf8");
|
|
53
|
+
const loadFunctionName = `${phpPrefix}_load_admin_views`;
|
|
54
|
+
const loadHook = `add_action( 'plugins_loaded', '${loadFunctionName}' );`;
|
|
55
|
+
const hasLoaderHook = source.includes(loadHook);
|
|
56
|
+
const hasServerGlob = source.includes(WORKSPACE_ADMIN_VIEW_GLOB);
|
|
57
|
+
return createDoctorCheck("Admin view bootstrap", hasLoaderHook && hasServerGlob ? "pass" : "fail", hasLoaderHook && hasServerGlob
|
|
58
|
+
? "Admin view PHP loader hook is present"
|
|
59
|
+
: "Missing admin view PHP require glob or plugins_loaded hook");
|
|
60
|
+
}
|
|
61
|
+
function checkWorkspaceAdminViewIndex(projectDir, adminViews) {
|
|
62
|
+
const indexRelativePath = [
|
|
63
|
+
path.join("src", "admin-views", "index.ts"),
|
|
64
|
+
path.join("src", "admin-views", "index.js"),
|
|
65
|
+
].find((relativePath) => fs.existsSync(path.join(projectDir, relativePath)));
|
|
66
|
+
if (!indexRelativePath) {
|
|
67
|
+
return createDoctorCheck("Admin views index", "fail", "Missing src/admin-views/index.ts or src/admin-views/index.js");
|
|
68
|
+
}
|
|
69
|
+
const indexPath = path.join(projectDir, indexRelativePath);
|
|
70
|
+
const source = fs.readFileSync(indexPath, "utf8");
|
|
71
|
+
const missingImports = adminViews.filter((adminView) => {
|
|
72
|
+
const importPattern = new RegExp(`['"\`]\\./${escapeRegex(adminView.slug)}(?:/[^'"\`]*)?['"\`]`, "u");
|
|
73
|
+
return !importPattern.test(source);
|
|
74
|
+
});
|
|
75
|
+
return createDoctorCheck("Admin views index", missingImports.length === 0 ? "pass" : "fail", missingImports.length === 0
|
|
76
|
+
? "Admin view registrations are aggregated"
|
|
77
|
+
: `Missing admin view imports for: ${missingImports
|
|
78
|
+
.map((entry) => entry.slug)
|
|
79
|
+
.join(", ")}`);
|
|
80
|
+
}
|
|
81
|
+
function checkWorkspaceAdminViewPhp(projectDir, adminView) {
|
|
82
|
+
const phpPath = path.join(projectDir, adminView.phpFile);
|
|
83
|
+
if (!fs.existsSync(phpPath)) {
|
|
84
|
+
return createDoctorCheck(`Admin view PHP ${adminView.slug}`, "fail", `Missing ${adminView.phpFile}`);
|
|
85
|
+
}
|
|
86
|
+
const source = fs.readFileSync(phpPath, "utf8");
|
|
87
|
+
const hasAdminMenu = source.includes("add_submenu_page");
|
|
88
|
+
const hasAdminEnqueue = source.includes("admin_enqueue_scripts");
|
|
89
|
+
const hasScript = source.includes(WORKSPACE_ADMIN_VIEW_SCRIPT);
|
|
90
|
+
const hasAsset = source.includes(WORKSPACE_ADMIN_VIEW_ASSET);
|
|
91
|
+
const hasStyle = source.includes(WORKSPACE_ADMIN_VIEW_STYLE);
|
|
92
|
+
const hasComponentsStyleDependency = /['"]wp-components['"]/u.test(source);
|
|
93
|
+
return createDoctorCheck(`Admin view PHP ${adminView.slug}`, hasAdminMenu &&
|
|
94
|
+
hasAdminEnqueue &&
|
|
95
|
+
hasScript &&
|
|
96
|
+
hasAsset &&
|
|
97
|
+
hasStyle &&
|
|
98
|
+
hasComponentsStyleDependency
|
|
99
|
+
? "pass"
|
|
100
|
+
: "fail", hasAdminMenu &&
|
|
101
|
+
hasAdminEnqueue &&
|
|
102
|
+
hasScript &&
|
|
103
|
+
hasAsset &&
|
|
104
|
+
hasStyle &&
|
|
105
|
+
hasComponentsStyleDependency
|
|
106
|
+
? "Admin menu, script, style, and wp-components style dependency are wired"
|
|
107
|
+
: "Missing admin menu, enqueue hook, build/admin-views asset reference, or wp-components style dependency");
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Collect admin view workspace doctor checks while preserving existing row order.
|
|
111
|
+
*
|
|
112
|
+
* @param workspace Resolved workspace metadata and filesystem paths.
|
|
113
|
+
* @param inventory Parsed workspace inventory from `scripts/block-config.ts`.
|
|
114
|
+
* @returns Ordered admin view doctor checks.
|
|
115
|
+
*/
|
|
116
|
+
export function getWorkspaceAdminViewDoctorChecks(workspace, inventory) {
|
|
117
|
+
const checks = [];
|
|
118
|
+
if (inventory.adminViews.length > 0) {
|
|
119
|
+
checks.push(checkWorkspaceAdminViewBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
120
|
+
checks.push(checkWorkspaceAdminViewIndex(workspace.projectDir, inventory.adminViews));
|
|
121
|
+
}
|
|
122
|
+
for (const adminView of inventory.adminViews) {
|
|
123
|
+
checks.push(checkWorkspaceAdminViewConfig(adminView, inventory));
|
|
124
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Admin view ${adminView.slug}`, getWorkspaceAdminViewRequiredFiles(adminView)));
|
|
125
|
+
checks.push(checkWorkspaceAdminViewPhp(workspace.projectDir, adminView));
|
|
126
|
+
}
|
|
127
|
+
return checks;
|
|
128
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DoctorCheck } from "./cli-doctor.js";
|
|
2
|
+
import type { WorkspaceInventory } from "./workspace-inventory.js";
|
|
3
|
+
import type { WorkspaceProject } from "./workspace-project.js";
|
|
4
|
+
/**
|
|
5
|
+
* Collect AI feature workspace doctor checks while preserving existing row order.
|
|
6
|
+
*
|
|
7
|
+
* @param workspace Resolved workspace metadata and filesystem paths.
|
|
8
|
+
* @param aiFeatures AI feature entries parsed from the workspace inventory.
|
|
9
|
+
* @returns Ordered AI feature doctor checks.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getWorkspaceAiFeatureDoctorChecks(workspace: WorkspaceProject, aiFeatures: WorkspaceInventory["aiFeatures"]): DoctorCheck[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { REST_RESOURCE_NAMESPACE_PATTERN } from "./cli-add-shared.js";
|
|
4
|
+
import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_AI_FEATURE_GLOB, } from "./cli-doctor-workspace-shared.js";
|
|
5
|
+
function getWorkspaceAiFeatureRequiredFiles(aiFeature) {
|
|
6
|
+
return Array.from(new Set([
|
|
7
|
+
aiFeature.aiSchemaFile,
|
|
8
|
+
aiFeature.apiFile,
|
|
9
|
+
path.join(path.dirname(aiFeature.typesFile), "api-schemas", "feature-request.schema.json"),
|
|
10
|
+
path.join(path.dirname(aiFeature.typesFile), "api-schemas", "feature-response.schema.json"),
|
|
11
|
+
path.join(path.dirname(aiFeature.typesFile), "api-schemas", "feature-result.schema.json"),
|
|
12
|
+
aiFeature.clientFile,
|
|
13
|
+
aiFeature.dataFile,
|
|
14
|
+
aiFeature.openApiFile,
|
|
15
|
+
aiFeature.phpFile,
|
|
16
|
+
aiFeature.typesFile,
|
|
17
|
+
aiFeature.validatorsFile,
|
|
18
|
+
]));
|
|
19
|
+
}
|
|
20
|
+
function checkWorkspaceAiFeatureConfig(aiFeature) {
|
|
21
|
+
const hasNamespace = REST_RESOURCE_NAMESPACE_PATTERN.test(aiFeature.namespace);
|
|
22
|
+
return createDoctorCheck(`AI feature config ${aiFeature.slug}`, hasNamespace ? "pass" : "fail", hasNamespace
|
|
23
|
+
? `AI feature namespace ${aiFeature.namespace} is valid`
|
|
24
|
+
: "AI feature namespace is invalid");
|
|
25
|
+
}
|
|
26
|
+
function checkWorkspaceAiFeatureBootstrap(projectDir, packageName, phpPrefix) {
|
|
27
|
+
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
28
|
+
if (!fs.existsSync(bootstrapPath)) {
|
|
29
|
+
return createDoctorCheck("AI feature bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
|
|
30
|
+
}
|
|
31
|
+
const source = fs.readFileSync(bootstrapPath, "utf8");
|
|
32
|
+
const registerFunctionName = `${phpPrefix}_register_ai_features`;
|
|
33
|
+
const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
|
|
34
|
+
const hasServerGlob = source.includes(WORKSPACE_AI_FEATURE_GLOB);
|
|
35
|
+
const hasRegisterHook = source.includes(registerHook);
|
|
36
|
+
return createDoctorCheck("AI feature bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook
|
|
37
|
+
? "AI feature PHP loader hook is present"
|
|
38
|
+
: "Missing AI feature PHP require glob or init hook");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Collect AI feature workspace doctor checks while preserving existing row order.
|
|
42
|
+
*
|
|
43
|
+
* @param workspace Resolved workspace metadata and filesystem paths.
|
|
44
|
+
* @param aiFeatures AI feature entries parsed from the workspace inventory.
|
|
45
|
+
* @returns Ordered AI feature doctor checks.
|
|
46
|
+
*/
|
|
47
|
+
export function getWorkspaceAiFeatureDoctorChecks(workspace, aiFeatures) {
|
|
48
|
+
const checks = [];
|
|
49
|
+
if (aiFeatures.length > 0) {
|
|
50
|
+
checks.push(checkWorkspaceAiFeatureBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
51
|
+
}
|
|
52
|
+
for (const aiFeature of aiFeatures) {
|
|
53
|
+
checks.push(checkWorkspaceAiFeatureConfig(aiFeature));
|
|
54
|
+
checks.push(checkExistingFiles(workspace.projectDir, `AI feature ${aiFeature.slug}`, getWorkspaceAiFeatureRequiredFiles(aiFeature)));
|
|
55
|
+
}
|
|
56
|
+
return checks;
|
|
57
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DoctorCheck } from "./cli-doctor.js";
|
|
2
|
+
import type { WorkspaceInventory } from "./workspace-inventory.js";
|
|
3
|
+
import type { WorkspaceProject } from "./workspace-project.js";
|
|
4
|
+
/**
|
|
5
|
+
* Collect editor plugin workspace doctor checks while preserving existing row order.
|
|
6
|
+
*
|
|
7
|
+
* @param workspace Resolved workspace metadata and filesystem paths.
|
|
8
|
+
* @param editorPlugins Editor plugin entries parsed from the workspace inventory.
|
|
9
|
+
* @returns Ordered editor plugin doctor checks.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getWorkspaceEditorPluginDoctorChecks(workspace: WorkspaceProject, editorPlugins: WorkspaceInventory["editorPlugins"]): DoctorCheck[];
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { EDITOR_PLUGIN_SLOT_IDS, resolveEditorPluginSlotAlias, } from "./cli-add-shared.js";
|
|
4
|
+
import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_EDITOR_PLUGIN_EDITOR_ASSET, WORKSPACE_EDITOR_PLUGIN_EDITOR_SCRIPT, WORKSPACE_EDITOR_PLUGIN_EDITOR_STYLE, } from "./cli-doctor-workspace-shared.js";
|
|
5
|
+
import { escapeRegex } from "./php-utils.js";
|
|
6
|
+
function getWorkspaceEditorPluginRequiredFiles(editorPlugin) {
|
|
7
|
+
const editorPluginDir = path.join("src", "editor-plugins", editorPlugin.slug);
|
|
8
|
+
const surfaceFile = editorPlugin.slot === "PluginSidebar"
|
|
9
|
+
? path.join(editorPluginDir, "Sidebar.tsx")
|
|
10
|
+
: path.join(editorPluginDir, "Surface.tsx");
|
|
11
|
+
return Array.from(new Set([
|
|
12
|
+
editorPlugin.file,
|
|
13
|
+
surfaceFile,
|
|
14
|
+
path.join(editorPluginDir, "data.ts"),
|
|
15
|
+
path.join(editorPluginDir, "types.ts"),
|
|
16
|
+
path.join(editorPluginDir, "style.scss"),
|
|
17
|
+
]));
|
|
18
|
+
}
|
|
19
|
+
function checkWorkspaceEditorPluginConfig(editorPlugin) {
|
|
20
|
+
const normalizedSlot = resolveEditorPluginSlotAlias(editorPlugin.slot);
|
|
21
|
+
const isValidSlot = Boolean(normalizedSlot);
|
|
22
|
+
return createDoctorCheck(`Editor plugin config ${editorPlugin.slug}`, isValidSlot ? "pass" : "fail", isValidSlot
|
|
23
|
+
? `Editor plugin slot ${editorPlugin.slot} is supported as ${normalizedSlot}`
|
|
24
|
+
: `Unsupported editor plugin slot "${editorPlugin.slot}". Expected one of: ${EDITOR_PLUGIN_SLOT_IDS.join(", ")} or legacy aliases PluginSidebar, PluginDocumentSettingPanel.`);
|
|
25
|
+
}
|
|
26
|
+
function checkWorkspaceEditorPluginBootstrap(projectDir, packageName, phpPrefix) {
|
|
27
|
+
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
28
|
+
if (!fs.existsSync(bootstrapPath)) {
|
|
29
|
+
return createDoctorCheck("Editor plugin bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
|
|
30
|
+
}
|
|
31
|
+
const source = fs.readFileSync(bootstrapPath, "utf8");
|
|
32
|
+
const enqueueFunctionName = `${phpPrefix}_enqueue_editor_plugins_editor`;
|
|
33
|
+
const enqueueHook = `add_action( 'enqueue_block_editor_assets', '${enqueueFunctionName}' );`;
|
|
34
|
+
const hasEditorEnqueueHook = source.includes(enqueueHook);
|
|
35
|
+
const hasEditorScript = source.includes(WORKSPACE_EDITOR_PLUGIN_EDITOR_SCRIPT);
|
|
36
|
+
const hasEditorAsset = source.includes(WORKSPACE_EDITOR_PLUGIN_EDITOR_ASSET);
|
|
37
|
+
const hasEditorStyle = source.includes(WORKSPACE_EDITOR_PLUGIN_EDITOR_STYLE);
|
|
38
|
+
return createDoctorCheck("Editor plugin bootstrap", hasEditorEnqueueHook && hasEditorScript && hasEditorAsset && hasEditorStyle ? "pass" : "fail", hasEditorEnqueueHook && hasEditorScript && hasEditorAsset && hasEditorStyle
|
|
39
|
+
? "Editor plugin enqueue hook is present"
|
|
40
|
+
: "Missing editor plugin enqueue hook or build/editor-plugins script/style asset references");
|
|
41
|
+
}
|
|
42
|
+
function checkWorkspaceEditorPluginIndex(projectDir, editorPlugins) {
|
|
43
|
+
const indexRelativePath = [
|
|
44
|
+
path.join("src", "editor-plugins", "index.ts"),
|
|
45
|
+
path.join("src", "editor-plugins", "index.js"),
|
|
46
|
+
].find((relativePath) => fs.existsSync(path.join(projectDir, relativePath)));
|
|
47
|
+
if (!indexRelativePath) {
|
|
48
|
+
return createDoctorCheck("Editor plugins index", "fail", "Missing src/editor-plugins/index.ts or src/editor-plugins/index.js");
|
|
49
|
+
}
|
|
50
|
+
const indexPath = path.join(projectDir, indexRelativePath);
|
|
51
|
+
const source = fs.readFileSync(indexPath, "utf8");
|
|
52
|
+
const missingImports = editorPlugins.filter((editorPlugin) => {
|
|
53
|
+
const importPattern = new RegExp(`['"\`]\\./${escapeRegex(editorPlugin.slug)}(?:/[^'"\`]*)?['"\`]`, "u");
|
|
54
|
+
return !importPattern.test(source);
|
|
55
|
+
});
|
|
56
|
+
return createDoctorCheck("Editor plugins index", missingImports.length === 0 ? "pass" : "fail", missingImports.length === 0
|
|
57
|
+
? "Editor plugin registrations are aggregated"
|
|
58
|
+
: `Missing editor plugin imports for: ${missingImports
|
|
59
|
+
.map((entry) => entry.slug)
|
|
60
|
+
.join(", ")}`);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Collect editor plugin workspace doctor checks while preserving existing row order.
|
|
64
|
+
*
|
|
65
|
+
* @param workspace Resolved workspace metadata and filesystem paths.
|
|
66
|
+
* @param editorPlugins Editor plugin entries parsed from the workspace inventory.
|
|
67
|
+
* @returns Ordered editor plugin doctor checks.
|
|
68
|
+
*/
|
|
69
|
+
export function getWorkspaceEditorPluginDoctorChecks(workspace, editorPlugins) {
|
|
70
|
+
const checks = [];
|
|
71
|
+
if (editorPlugins.length > 0) {
|
|
72
|
+
checks.push(checkWorkspaceEditorPluginBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
73
|
+
checks.push(checkWorkspaceEditorPluginIndex(workspace.projectDir, editorPlugins));
|
|
74
|
+
}
|
|
75
|
+
for (const editorPlugin of editorPlugins) {
|
|
76
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Editor plugin ${editorPlugin.slug}`, getWorkspaceEditorPluginRequiredFiles(editorPlugin)));
|
|
77
|
+
checks.push(checkWorkspaceEditorPluginConfig(editorPlugin));
|
|
78
|
+
}
|
|
79
|
+
return checks;
|
|
80
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DoctorCheck } from "./cli-doctor.js";
|
|
2
|
+
import type { WorkspaceInventory } from "./workspace-inventory.js";
|
|
3
|
+
import type { WorkspaceProject } from "./workspace-project.js";
|
|
4
|
+
/**
|
|
5
|
+
* Collect post meta workspace doctor checks while preserving existing row order.
|
|
6
|
+
*
|
|
7
|
+
* @param workspace Resolved workspace metadata and filesystem paths.
|
|
8
|
+
* @param postMetaEntries Post meta entries parsed from the workspace inventory.
|
|
9
|
+
* @returns Ordered post meta doctor checks.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getWorkspacePostMetaDoctorChecks(workspace: WorkspaceProject, postMetaEntries: WorkspaceInventory["postMeta"]): DoctorCheck[];
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { assertValidPostMetaPostType } from "./cli-add-shared.js";
|
|
4
|
+
import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_POST_META_GLOB, } from "./cli-doctor-workspace-shared.js";
|
|
5
|
+
function getWorkspacePostMetaRequiredFiles(postMeta) {
|
|
6
|
+
return Array.from(new Set([
|
|
7
|
+
postMeta.phpFile,
|
|
8
|
+
postMeta.schemaFile,
|
|
9
|
+
postMeta.typesFile,
|
|
10
|
+
]));
|
|
11
|
+
}
|
|
12
|
+
function checkWorkspacePostMetaConfig(postMeta) {
|
|
13
|
+
let hasPostType = false;
|
|
14
|
+
try {
|
|
15
|
+
hasPostType = assertValidPostMetaPostType(postMeta.postType) === postMeta.postType;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
hasPostType = false;
|
|
19
|
+
}
|
|
20
|
+
const hasMetaKey = typeof postMeta.metaKey === "string" &&
|
|
21
|
+
postMeta.metaKey.trim().length > 0 &&
|
|
22
|
+
!/\s/u.test(postMeta.metaKey);
|
|
23
|
+
const hasRestExposure = typeof postMeta.showInRest === "boolean";
|
|
24
|
+
return createDoctorCheck(`Post meta config ${postMeta.slug}`, hasPostType && hasMetaKey && hasRestExposure ? "pass" : "fail", hasPostType && hasMetaKey && hasRestExposure
|
|
25
|
+
? `Post meta ${postMeta.metaKey} targets ${postMeta.postType}`
|
|
26
|
+
: "Post meta postType, metaKey, or showInRest configuration is invalid");
|
|
27
|
+
}
|
|
28
|
+
function checkWorkspacePostMetaBootstrap(projectDir, packageName, phpPrefix) {
|
|
29
|
+
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
30
|
+
if (!fs.existsSync(bootstrapPath)) {
|
|
31
|
+
return createDoctorCheck("Post meta bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
|
|
32
|
+
}
|
|
33
|
+
const source = fs.readFileSync(bootstrapPath, "utf8");
|
|
34
|
+
const registerFunctionName = `${phpPrefix}_register_post_meta_contracts`;
|
|
35
|
+
const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
|
|
36
|
+
const hasServerGlob = source.includes(WORKSPACE_POST_META_GLOB);
|
|
37
|
+
const hasRegisterHook = source.includes(registerHook);
|
|
38
|
+
return createDoctorCheck("Post meta bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook
|
|
39
|
+
? "Post meta PHP loader hook is present"
|
|
40
|
+
: "Missing post meta PHP require glob or init hook");
|
|
41
|
+
}
|
|
42
|
+
function checkWorkspacePostMetaPhp(projectDir, postMeta) {
|
|
43
|
+
const phpPath = path.join(projectDir, postMeta.phpFile);
|
|
44
|
+
if (!fs.existsSync(phpPath)) {
|
|
45
|
+
return createDoctorCheck(`Post meta PHP ${postMeta.slug}`, "fail", `Missing ${postMeta.phpFile}`);
|
|
46
|
+
}
|
|
47
|
+
const source = fs.readFileSync(phpPath, "utf8");
|
|
48
|
+
const hasRegisterPostMeta = source.includes("register_post_meta");
|
|
49
|
+
const hasPostType = source.includes(postMeta.postType);
|
|
50
|
+
const hasMetaKey = source.includes(postMeta.metaKey);
|
|
51
|
+
const hasSchemaFile = source.includes(postMeta.schemaFile);
|
|
52
|
+
const hasRestExposure = source.includes("'show_in_rest'");
|
|
53
|
+
return createDoctorCheck(`Post meta PHP ${postMeta.slug}`, hasRegisterPostMeta && hasPostType && hasMetaKey && hasSchemaFile && hasRestExposure
|
|
54
|
+
? "pass"
|
|
55
|
+
: "fail", hasRegisterPostMeta && hasPostType && hasMetaKey && hasSchemaFile && hasRestExposure
|
|
56
|
+
? "Post meta registration, schema path, and REST exposure flag are wired"
|
|
57
|
+
: "Missing register_post_meta, post type, meta key, schema path, or show_in_rest wiring");
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Collect post meta workspace doctor checks while preserving existing row order.
|
|
61
|
+
*
|
|
62
|
+
* @param workspace Resolved workspace metadata and filesystem paths.
|
|
63
|
+
* @param postMetaEntries Post meta entries parsed from the workspace inventory.
|
|
64
|
+
* @returns Ordered post meta doctor checks.
|
|
65
|
+
*/
|
|
66
|
+
export function getWorkspacePostMetaDoctorChecks(workspace, postMetaEntries) {
|
|
67
|
+
const checks = [];
|
|
68
|
+
if (postMetaEntries.length > 0) {
|
|
69
|
+
checks.push(checkWorkspacePostMetaBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
70
|
+
}
|
|
71
|
+
for (const postMeta of postMetaEntries) {
|
|
72
|
+
checks.push(checkWorkspacePostMetaConfig(postMeta));
|
|
73
|
+
checks.push(checkExistingFiles(workspace.projectDir, `Post meta ${postMeta.slug}`, getWorkspacePostMetaRequiredFiles(postMeta)));
|
|
74
|
+
checks.push(checkWorkspacePostMetaPhp(workspace.projectDir, postMeta));
|
|
75
|
+
}
|
|
76
|
+
return checks;
|
|
77
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DoctorCheck } from "./cli-doctor.js";
|
|
2
|
+
import type { WorkspaceInventory } from "./workspace-inventory.js";
|
|
3
|
+
import type { WorkspaceProject } from "./workspace-project.js";
|
|
4
|
+
/**
|
|
5
|
+
* Collect REST resource workspace doctor checks while preserving existing row order.
|
|
6
|
+
*
|
|
7
|
+
* @param workspace Resolved workspace metadata and filesystem paths.
|
|
8
|
+
* @param restResources REST resources parsed from the workspace inventory.
|
|
9
|
+
* @returns Ordered REST resource doctor checks.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getWorkspaceRestResourceDoctorChecks(workspace: WorkspaceProject, restResources: WorkspaceInventory["restResources"]): DoctorCheck[];
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { MANUAL_REST_CONTRACT_AUTH_IDS, MANUAL_REST_CONTRACT_HTTP_METHOD_IDS, REST_RESOURCE_METHOD_IDS, REST_RESOURCE_NAMESPACE_PATTERN, isGeneratedRestResourceRoutePatternCompatible, } from "./cli-add-shared.js";
|
|
4
|
+
import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_REST_RESOURCE_GLOB, } from "./cli-doctor-workspace-shared.js";
|
|
5
|
+
function isManualRestResource(restResource) {
|
|
6
|
+
return restResource.mode === "manual";
|
|
7
|
+
}
|
|
8
|
+
function getWorkspaceRestResourceRequiredFiles(restResource) {
|
|
9
|
+
const schemaNames = new Set();
|
|
10
|
+
if (isManualRestResource(restResource)) {
|
|
11
|
+
schemaNames.add("query");
|
|
12
|
+
if (restResource.bodyTypeName) {
|
|
13
|
+
schemaNames.add("request");
|
|
14
|
+
}
|
|
15
|
+
schemaNames.add("response");
|
|
16
|
+
return Array.from(new Set([
|
|
17
|
+
restResource.apiFile,
|
|
18
|
+
...Array.from(schemaNames, (schemaName) => path.join(path.dirname(restResource.typesFile), "api-schemas", `${schemaName}.schema.json`)),
|
|
19
|
+
restResource.clientFile,
|
|
20
|
+
restResource.openApiFile,
|
|
21
|
+
restResource.typesFile,
|
|
22
|
+
restResource.validatorsFile,
|
|
23
|
+
]));
|
|
24
|
+
}
|
|
25
|
+
if (restResource.methods.includes("list")) {
|
|
26
|
+
schemaNames.add("list-query");
|
|
27
|
+
schemaNames.add("list-response");
|
|
28
|
+
}
|
|
29
|
+
if (restResource.methods.includes("read")) {
|
|
30
|
+
schemaNames.add("read-query");
|
|
31
|
+
schemaNames.add("read-response");
|
|
32
|
+
}
|
|
33
|
+
if (restResource.methods.includes("create")) {
|
|
34
|
+
schemaNames.add("create-request");
|
|
35
|
+
schemaNames.add("create-response");
|
|
36
|
+
}
|
|
37
|
+
if (restResource.methods.includes("update")) {
|
|
38
|
+
schemaNames.add("update-query");
|
|
39
|
+
schemaNames.add("update-request");
|
|
40
|
+
schemaNames.add("update-response");
|
|
41
|
+
}
|
|
42
|
+
if (restResource.methods.includes("delete")) {
|
|
43
|
+
schemaNames.add("delete-query");
|
|
44
|
+
schemaNames.add("delete-response");
|
|
45
|
+
}
|
|
46
|
+
return Array.from(new Set([
|
|
47
|
+
restResource.apiFile,
|
|
48
|
+
...Array.from(schemaNames, (schemaName) => path.join(path.dirname(restResource.typesFile), "api-schemas", `${schemaName}.schema.json`)),
|
|
49
|
+
restResource.clientFile,
|
|
50
|
+
...(restResource.dataFile ? [restResource.dataFile] : []),
|
|
51
|
+
restResource.openApiFile,
|
|
52
|
+
...(restResource.phpFile ? [restResource.phpFile] : []),
|
|
53
|
+
restResource.typesFile,
|
|
54
|
+
restResource.validatorsFile,
|
|
55
|
+
]));
|
|
56
|
+
}
|
|
57
|
+
function checkWorkspaceRestResourceConfig(restResource) {
|
|
58
|
+
const hasNamespace = REST_RESOURCE_NAMESPACE_PATTERN.test(restResource.namespace);
|
|
59
|
+
if (isManualRestResource(restResource)) {
|
|
60
|
+
const hasAuth = restResource.auth == null ||
|
|
61
|
+
MANUAL_REST_CONTRACT_AUTH_IDS.includes(restResource.auth);
|
|
62
|
+
const hasMethod = typeof restResource.method === "string" &&
|
|
63
|
+
MANUAL_REST_CONTRACT_HTTP_METHOD_IDS.includes(restResource.method);
|
|
64
|
+
const hasPathPattern = typeof restResource.pathPattern === "string" &&
|
|
65
|
+
restResource.pathPattern.startsWith("/") &&
|
|
66
|
+
restResource.pathPattern.length > 1;
|
|
67
|
+
return createDoctorCheck(`REST resource config ${restResource.slug}`, hasNamespace && hasAuth && hasMethod && hasPathPattern ? "pass" : "fail", hasNamespace && hasAuth && hasMethod && hasPathPattern
|
|
68
|
+
? `Manual REST contract ${restResource.method} /${restResource.namespace}${restResource.pathPattern}`
|
|
69
|
+
: "Manual REST contract namespace, auth, method, or path pattern is invalid");
|
|
70
|
+
}
|
|
71
|
+
const hasMethods = restResource.methods.length > 0 &&
|
|
72
|
+
restResource.methods.every((method) => REST_RESOURCE_METHOD_IDS.includes(method));
|
|
73
|
+
const hasGeneratedFiles = typeof restResource.dataFile === "string" &&
|
|
74
|
+
restResource.dataFile.length > 0 &&
|
|
75
|
+
typeof restResource.phpFile === "string" &&
|
|
76
|
+
restResource.phpFile.length > 0;
|
|
77
|
+
const hasRoutePattern = restResource.routePattern == null ||
|
|
78
|
+
(typeof restResource.routePattern === "string" &&
|
|
79
|
+
restResource.routePattern.startsWith("/") &&
|
|
80
|
+
restResource.routePattern.length > 1 &&
|
|
81
|
+
!/\s/u.test(restResource.routePattern) &&
|
|
82
|
+
isGeneratedRestResourceRoutePatternCompatible(restResource.routePattern));
|
|
83
|
+
return createDoctorCheck(`REST resource config ${restResource.slug}`, hasNamespace && hasMethods && hasGeneratedFiles && hasRoutePattern
|
|
84
|
+
? "pass"
|
|
85
|
+
: "fail", hasNamespace && hasMethods && hasGeneratedFiles && hasRoutePattern
|
|
86
|
+
? `REST resource namespace ${restResource.namespace} with methods ${restResource.methods.join(", ")}`
|
|
87
|
+
: "REST resource namespace, methods, dataFile, phpFile, or routePattern are invalid");
|
|
88
|
+
}
|
|
89
|
+
function checkWorkspaceRestResourceBootstrap(projectDir, packageName, phpPrefix) {
|
|
90
|
+
const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
|
|
91
|
+
if (!fs.existsSync(bootstrapPath)) {
|
|
92
|
+
return createDoctorCheck("REST resource bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
|
|
93
|
+
}
|
|
94
|
+
const source = fs.readFileSync(bootstrapPath, "utf8");
|
|
95
|
+
const registerFunctionName = `${phpPrefix}_register_rest_resources`;
|
|
96
|
+
const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
|
|
97
|
+
const hasServerGlob = source.includes(WORKSPACE_REST_RESOURCE_GLOB);
|
|
98
|
+
const hasRegisterHook = source.includes(registerHook);
|
|
99
|
+
return createDoctorCheck("REST resource bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook
|
|
100
|
+
? "REST resource PHP loader hook is present"
|
|
101
|
+
: "Missing REST resource PHP require glob or init hook");
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Collect REST resource workspace doctor checks while preserving existing row order.
|
|
105
|
+
*
|
|
106
|
+
* @param workspace Resolved workspace metadata and filesystem paths.
|
|
107
|
+
* @param restResources REST resources parsed from the workspace inventory.
|
|
108
|
+
* @returns Ordered REST resource doctor checks.
|
|
109
|
+
*/
|
|
110
|
+
export function getWorkspaceRestResourceDoctorChecks(workspace, restResources) {
|
|
111
|
+
const checks = [];
|
|
112
|
+
if (restResources.some((restResource) => !isManualRestResource(restResource))) {
|
|
113
|
+
checks.push(checkWorkspaceRestResourceBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
|
|
114
|
+
}
|
|
115
|
+
for (const restResource of restResources) {
|
|
116
|
+
checks.push(checkWorkspaceRestResourceConfig(restResource));
|
|
117
|
+
checks.push(checkExistingFiles(workspace.projectDir, `REST resource ${restResource.slug}`, getWorkspaceRestResourceRequiredFiles(restResource)));
|
|
118
|
+
}
|
|
119
|
+
return checks;
|
|
120
|
+
}
|