@wp-typia/project-tools 0.20.1 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/runtime/cli-add-block.js +1 -1
- package/dist/runtime/cli-add-shared.d.ts +73 -5
- package/dist/runtime/cli-add-shared.js +58 -11
- package/dist/runtime/cli-add-workspace-ability.js +11 -57
- package/dist/runtime/cli-add-workspace-admin-view.d.ts +23 -0
- package/dist/runtime/cli-add-workspace-admin-view.js +872 -0
- package/dist/runtime/cli-add-workspace-ai-anchors.js +2 -5
- package/dist/runtime/cli-add-workspace-ai-source-emitters.d.ts +0 -4
- package/dist/runtime/cli-add-workspace-ai-source-emitters.js +7 -17
- package/dist/runtime/cli-add-workspace-ai.js +4 -6
- package/dist/runtime/cli-add-workspace-assets.d.ts +13 -5
- package/dist/runtime/cli-add-workspace-assets.js +290 -106
- package/dist/runtime/cli-add-workspace-rest-anchors.js +2 -5
- package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +0 -1
- package/dist/runtime/cli-add-workspace-rest-source-emitters.js +7 -14
- package/dist/runtime/cli-add-workspace-rest.js +4 -6
- package/dist/runtime/cli-add-workspace.d.ts +58 -1
- package/dist/runtime/cli-add-workspace.js +588 -18
- package/dist/runtime/cli-add.d.ts +1 -1
- package/dist/runtime/cli-add.js +1 -1
- package/dist/runtime/cli-core.d.ts +8 -5
- package/dist/runtime/cli-core.js +7 -4
- package/dist/runtime/cli-diagnostics.d.ts +84 -1
- package/dist/runtime/cli-diagnostics.js +90 -3
- package/dist/runtime/cli-doctor-workspace.js +552 -13
- package/dist/runtime/cli-doctor.d.ts +4 -2
- package/dist/runtime/cli-doctor.js +2 -1
- package/dist/runtime/cli-help.js +19 -9
- package/dist/runtime/cli-init.d.ts +67 -3
- package/dist/runtime/cli-init.js +603 -64
- package/dist/runtime/cli-validation.js +4 -3
- package/dist/runtime/index.d.ts +9 -4
- package/dist/runtime/index.js +7 -3
- package/dist/runtime/package-json-types.d.ts +12 -0
- package/dist/runtime/package-json-types.js +1 -0
- package/dist/runtime/package-versions.d.ts +17 -2
- package/dist/runtime/package-versions.js +46 -1
- package/dist/runtime/php-utils.d.ts +16 -0
- package/dist/runtime/php-utils.js +59 -0
- package/dist/runtime/scaffold-answer-resolution.js +35 -10
- package/dist/runtime/scaffold-apply-utils.d.ts +2 -3
- package/dist/runtime/scaffold-apply-utils.js +3 -43
- package/dist/runtime/template-source-cache.d.ts +112 -0
- package/dist/runtime/template-source-cache.js +434 -0
- package/dist/runtime/template-source-seeds.js +333 -53
- package/dist/runtime/workspace-inventory.d.ts +43 -2
- package/dist/runtime/workspace-inventory.js +138 -5
- package/package.json +2 -2
|
@@ -281,7 +281,7 @@ export async function runAddBlockCommand({ alternateRenderTargets, blockName, cw
|
|
|
281
281
|
throw new Error("`wp-typia add block --template query-loop` is not supported. Query Loop is a create-time `core/query` variation scaffold, so use `wp-typia create <project-dir> --template query-loop` instead.");
|
|
282
282
|
}
|
|
283
283
|
if (!isAddBlockTemplateId(templateId)) {
|
|
284
|
-
throw new Error(`Unknown add-block template "${templateId}". Expected one of: ${ADD_BLOCK_TEMPLATE_IDS.join(", ")}
|
|
284
|
+
throw new Error(`Unknown add-block template "${templateId}". Expected one of: ${ADD_BLOCK_TEMPLATE_IDS.join(", ")}. Run \`wp-typia templates list\` to inspect available templates.`);
|
|
285
285
|
}
|
|
286
286
|
const resolvedTemplateId = templateId;
|
|
287
287
|
assertPersistenceFlagsAllowed(resolvedTemplateId, {
|
|
@@ -5,7 +5,7 @@ export { normalizeBlockSlug, } from "./scaffold-identifiers.js";
|
|
|
5
5
|
/**
|
|
6
6
|
* Supported top-level `wp-typia add` kinds exposed by the canonical CLI.
|
|
7
7
|
*/
|
|
8
|
-
export declare const ADD_KIND_IDS: readonly ["block", "variation", "pattern", "binding-source", "rest-resource", "ability", "ai-feature", "hooked-block", "editor-plugin"];
|
|
8
|
+
export declare const ADD_KIND_IDS: readonly ["admin-view", "block", "variation", "style", "transform", "pattern", "binding-source", "rest-resource", "ability", "ai-feature", "hooked-block", "editor-plugin"];
|
|
9
9
|
export type AddKindId = (typeof ADD_KIND_IDS)[number];
|
|
10
10
|
/**
|
|
11
11
|
* Supported plugin-level REST resource methods accepted by
|
|
@@ -14,10 +14,18 @@ export type AddKindId = (typeof ADD_KIND_IDS)[number];
|
|
|
14
14
|
export declare const REST_RESOURCE_METHOD_IDS: readonly ["list", "read", "create", "update", "delete"];
|
|
15
15
|
export type RestResourceMethodId = (typeof REST_RESOURCE_METHOD_IDS)[number];
|
|
16
16
|
/**
|
|
17
|
-
* Supported editor-plugin shell
|
|
17
|
+
* Supported editor-plugin shell surfaces accepted by
|
|
18
|
+
* `wp-typia add editor-plugin --slot`.
|
|
18
19
|
*/
|
|
19
|
-
export declare const EDITOR_PLUGIN_SLOT_IDS: readonly ["
|
|
20
|
+
export declare const EDITOR_PLUGIN_SLOT_IDS: readonly ["sidebar", "document-setting-panel"];
|
|
20
21
|
export type EditorPluginSlotId = (typeof EDITOR_PLUGIN_SLOT_IDS)[number];
|
|
22
|
+
export declare const EDITOR_PLUGIN_SLOT_ALIASES: {
|
|
23
|
+
readonly PluginDocumentSettingPanel: "document-setting-panel";
|
|
24
|
+
readonly PluginSidebar: "sidebar";
|
|
25
|
+
readonly "document-setting-panel": "document-setting-panel";
|
|
26
|
+
readonly sidebar: "sidebar";
|
|
27
|
+
};
|
|
28
|
+
export declare function resolveEditorPluginSlotAlias(slot: string): EditorPluginSlotId | undefined;
|
|
21
29
|
/**
|
|
22
30
|
* Supported built-in block families accepted by `wp-typia add block --template`.
|
|
23
31
|
*/
|
|
@@ -29,11 +37,45 @@ export interface RunAddVariationCommandOptions {
|
|
|
29
37
|
cwd?: string;
|
|
30
38
|
variationName: string;
|
|
31
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Options for `wp-typia add style`.
|
|
42
|
+
*
|
|
43
|
+
* @property blockName Existing workspace block slug that owns the style.
|
|
44
|
+
* @property cwd Working directory used to resolve the nearest official workspace.
|
|
45
|
+
* Defaults to `process.cwd()`.
|
|
46
|
+
* @property styleName Human-entered style name that will be normalized into the
|
|
47
|
+
* generated style slug.
|
|
48
|
+
*/
|
|
49
|
+
export interface RunAddBlockStyleCommandOptions {
|
|
50
|
+
blockName: string;
|
|
51
|
+
cwd?: string;
|
|
52
|
+
styleName: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Options for `wp-typia add transform`.
|
|
56
|
+
*
|
|
57
|
+
* @property cwd Working directory used to resolve the nearest official workspace.
|
|
58
|
+
* Defaults to `process.cwd()`.
|
|
59
|
+
* @property fromBlockName Full `namespace/block` source block name accepted by
|
|
60
|
+
* WordPress block transform definitions.
|
|
61
|
+
* @property toBlockName Existing workspace block slug or full block name that
|
|
62
|
+
* owns the generated transform.
|
|
63
|
+
* @property transformName Human-entered transform name that will be normalized
|
|
64
|
+
* into the generated transform slug.
|
|
65
|
+
*/
|
|
66
|
+
export interface RunAddBlockTransformCommandOptions {
|
|
67
|
+
cwd?: string;
|
|
68
|
+
fromBlockName: string;
|
|
69
|
+
toBlockName: string;
|
|
70
|
+
transformName: string;
|
|
71
|
+
}
|
|
32
72
|
export interface RunAddPatternCommandOptions {
|
|
33
73
|
cwd?: string;
|
|
34
74
|
patternName: string;
|
|
35
75
|
}
|
|
36
76
|
export interface RunAddBindingSourceCommandOptions {
|
|
77
|
+
attributeName?: string;
|
|
78
|
+
blockName?: string;
|
|
37
79
|
bindingSourceName: string;
|
|
38
80
|
cwd?: string;
|
|
39
81
|
}
|
|
@@ -43,6 +85,22 @@ export interface RunAddRestResourceCommandOptions {
|
|
|
43
85
|
namespace?: string;
|
|
44
86
|
restResourceName: string;
|
|
45
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Options for `wp-typia add admin-view`.
|
|
90
|
+
*
|
|
91
|
+
* @property adminViewName Human-entered admin screen name that will be
|
|
92
|
+
* normalized into the generated slug.
|
|
93
|
+
* @property cwd Working directory used to resolve the nearest official workspace.
|
|
94
|
+
* Defaults to `process.cwd()`.
|
|
95
|
+
* @property source Optional data source locator. The first supported source is
|
|
96
|
+
* `rest-resource:<slug>`, which wires the generated screen to an existing
|
|
97
|
+
* list-capable REST resource.
|
|
98
|
+
*/
|
|
99
|
+
export interface RunAddAdminViewCommandOptions {
|
|
100
|
+
adminViewName: string;
|
|
101
|
+
cwd?: string;
|
|
102
|
+
source?: string;
|
|
103
|
+
}
|
|
46
104
|
/**
|
|
47
105
|
* Options for `wp-typia add ability`.
|
|
48
106
|
*
|
|
@@ -73,7 +131,7 @@ export interface RunAddHookedBlockCommandOptions {
|
|
|
73
131
|
* Defaults to `process.cwd()`.
|
|
74
132
|
* @property editorPluginName Human-entered editor plugin name that will be
|
|
75
133
|
* normalized into the generated slug.
|
|
76
|
-
* @property slot Optional editor shell slot. Defaults to `
|
|
134
|
+
* @property slot Optional editor shell slot. Defaults to `sidebar`.
|
|
77
135
|
*/
|
|
78
136
|
export interface RunAddEditorPluginCommandOptions {
|
|
79
137
|
cwd?: string;
|
|
@@ -143,7 +201,7 @@ export declare function assertValidHookAnchor(anchorBlockName: string): string;
|
|
|
143
201
|
/**
|
|
144
202
|
* Validate and normalize the editor plugin shell slot.
|
|
145
203
|
*
|
|
146
|
-
* @param slot Optional shell slot. Defaults to `
|
|
204
|
+
* @param slot Optional shell slot. Defaults to `sidebar`.
|
|
147
205
|
* @returns The canonical editor plugin slot id.
|
|
148
206
|
* @throws {Error} When the slot is not supported by the workspace scaffold.
|
|
149
207
|
*/
|
|
@@ -157,6 +215,16 @@ export declare function assertVariationDoesNotExist(projectDir: string, blockSlu
|
|
|
157
215
|
export declare function assertPatternDoesNotExist(projectDir: string, patternSlug: string, inventory: WorkspaceInventory): void;
|
|
158
216
|
export declare function assertBindingSourceDoesNotExist(projectDir: string, bindingSourceSlug: string, inventory: WorkspaceInventory): void;
|
|
159
217
|
export declare function assertRestResourceDoesNotExist(projectDir: string, restResourceSlug: string, inventory: WorkspaceInventory): void;
|
|
218
|
+
/**
|
|
219
|
+
* Ensure a DataViews admin screen scaffold does not already exist on disk or in
|
|
220
|
+
* the workspace inventory.
|
|
221
|
+
*
|
|
222
|
+
* @param projectDir Workspace root directory.
|
|
223
|
+
* @param adminViewSlug Normalized admin screen slug.
|
|
224
|
+
* @param inventory Parsed workspace inventory.
|
|
225
|
+
* @throws {Error} When the directory, PHP bootstrap, or inventory entry already exists.
|
|
226
|
+
*/
|
|
227
|
+
export declare function assertAdminViewDoesNotExist(projectDir: string, adminViewSlug: string, inventory: WorkspaceInventory): void;
|
|
160
228
|
/**
|
|
161
229
|
* Ensure a workflow ability scaffold does not already exist on disk or in the
|
|
162
230
|
* workspace inventory.
|
|
@@ -10,8 +10,11 @@ export { normalizeBlockSlug, } from "./scaffold-identifiers.js";
|
|
|
10
10
|
* Supported top-level `wp-typia add` kinds exposed by the canonical CLI.
|
|
11
11
|
*/
|
|
12
12
|
export const ADD_KIND_IDS = [
|
|
13
|
+
"admin-view",
|
|
13
14
|
"block",
|
|
14
15
|
"variation",
|
|
16
|
+
"style",
|
|
17
|
+
"transform",
|
|
15
18
|
"pattern",
|
|
16
19
|
"binding-source",
|
|
17
20
|
"rest-resource",
|
|
@@ -32,9 +35,23 @@ export const REST_RESOURCE_METHOD_IDS = [
|
|
|
32
35
|
"delete",
|
|
33
36
|
];
|
|
34
37
|
/**
|
|
35
|
-
* Supported editor-plugin shell
|
|
38
|
+
* Supported editor-plugin shell surfaces accepted by
|
|
39
|
+
* `wp-typia add editor-plugin --slot`.
|
|
36
40
|
*/
|
|
37
|
-
export const EDITOR_PLUGIN_SLOT_IDS = ["
|
|
41
|
+
export const EDITOR_PLUGIN_SLOT_IDS = ["sidebar", "document-setting-panel"];
|
|
42
|
+
export const EDITOR_PLUGIN_SLOT_ALIASES = {
|
|
43
|
+
PluginDocumentSettingPanel: "document-setting-panel",
|
|
44
|
+
PluginSidebar: "sidebar",
|
|
45
|
+
"document-setting-panel": "document-setting-panel",
|
|
46
|
+
sidebar: "sidebar",
|
|
47
|
+
};
|
|
48
|
+
export function resolveEditorPluginSlotAlias(slot) {
|
|
49
|
+
const trimmed = slot.trim();
|
|
50
|
+
if (!Object.prototype.hasOwnProperty.call(EDITOR_PLUGIN_SLOT_ALIASES, trimmed)) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
return EDITOR_PLUGIN_SLOT_ALIASES[trimmed];
|
|
54
|
+
}
|
|
38
55
|
/**
|
|
39
56
|
* Supported built-in block families accepted by `wp-typia add block --template`.
|
|
40
57
|
*/
|
|
@@ -175,15 +192,16 @@ export function assertValidHookAnchor(anchorBlockName) {
|
|
|
175
192
|
/**
|
|
176
193
|
* Validate and normalize the editor plugin shell slot.
|
|
177
194
|
*
|
|
178
|
-
* @param slot Optional shell slot. Defaults to `
|
|
195
|
+
* @param slot Optional shell slot. Defaults to `sidebar`.
|
|
179
196
|
* @returns The canonical editor plugin slot id.
|
|
180
197
|
* @throws {Error} When the slot is not supported by the workspace scaffold.
|
|
181
198
|
*/
|
|
182
|
-
export function assertValidEditorPluginSlot(slot = "
|
|
183
|
-
|
|
184
|
-
|
|
199
|
+
export function assertValidEditorPluginSlot(slot = "sidebar") {
|
|
200
|
+
const alias = resolveEditorPluginSlotAlias(slot);
|
|
201
|
+
if (alias) {
|
|
202
|
+
return alias;
|
|
185
203
|
}
|
|
186
|
-
throw new Error(`Editor plugin slot must be one of: ${EDITOR_PLUGIN_SLOT_IDS.join(", ")}.`);
|
|
204
|
+
throw new Error(`Editor plugin slot must be one of: ${EDITOR_PLUGIN_SLOT_IDS.join(", ")}. Legacy aliases: PluginSidebar, PluginDocumentSettingPanel.`);
|
|
187
205
|
}
|
|
188
206
|
export function readWorkspaceBlockJson(projectDir, blockSlug) {
|
|
189
207
|
const blockJsonPath = path.join(projectDir, "src", "blocks", blockSlug, "block.json");
|
|
@@ -256,6 +274,28 @@ export function assertRestResourceDoesNotExist(projectDir, restResourceSlug, inv
|
|
|
256
274
|
throw new Error(`A REST resource inventory entry already exists for ${restResourceSlug}. Choose a different name.`);
|
|
257
275
|
}
|
|
258
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* Ensure a DataViews admin screen scaffold does not already exist on disk or in
|
|
279
|
+
* the workspace inventory.
|
|
280
|
+
*
|
|
281
|
+
* @param projectDir Workspace root directory.
|
|
282
|
+
* @param adminViewSlug Normalized admin screen slug.
|
|
283
|
+
* @param inventory Parsed workspace inventory.
|
|
284
|
+
* @throws {Error} When the directory, PHP bootstrap, or inventory entry already exists.
|
|
285
|
+
*/
|
|
286
|
+
export function assertAdminViewDoesNotExist(projectDir, adminViewSlug, inventory) {
|
|
287
|
+
const adminViewDir = path.join(projectDir, "src", "admin-views", adminViewSlug);
|
|
288
|
+
const adminViewPhpPath = path.join(projectDir, "inc", "admin-views", `${adminViewSlug}.php`);
|
|
289
|
+
if (fs.existsSync(adminViewDir)) {
|
|
290
|
+
throw new Error(`An admin view already exists at ${path.relative(projectDir, adminViewDir)}. Choose a different name.`);
|
|
291
|
+
}
|
|
292
|
+
if (fs.existsSync(adminViewPhpPath)) {
|
|
293
|
+
throw new Error(`An admin view bootstrap already exists at ${path.relative(projectDir, adminViewPhpPath)}. Choose a different name.`);
|
|
294
|
+
}
|
|
295
|
+
if (inventory.adminViews.some((entry) => entry.slug === adminViewSlug)) {
|
|
296
|
+
throw new Error(`An admin view inventory entry already exists for ${adminViewSlug}. Choose a different name.`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
259
299
|
/**
|
|
260
300
|
* Ensure a workflow ability scaffold does not already exist on disk or in the
|
|
261
301
|
* workspace inventory.
|
|
@@ -317,10 +357,13 @@ export function assertEditorPluginDoesNotExist(projectDir, editorPluginSlug, inv
|
|
|
317
357
|
*/
|
|
318
358
|
export function formatAddHelpText() {
|
|
319
359
|
return `Usage:
|
|
320
|
-
wp-typia add
|
|
360
|
+
wp-typia add admin-view <name> [--source <rest-resource:slug>] [--dry-run]
|
|
361
|
+
wp-typia add block <name> [--template <${ADD_BLOCK_TEMPLATE_IDS.join("|")}>] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--inner-blocks-preset <freeform|ordered|horizontal|locked-structure>] [--alternate-render-targets <email,mjml,plain-text>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>] [--dry-run]
|
|
321
362
|
wp-typia add variation <name> --block <block-slug> [--dry-run]
|
|
363
|
+
wp-typia add style <name> --block <block-slug> [--dry-run]
|
|
364
|
+
wp-typia add transform <name> --from <namespace/block> --to <block-slug|namespace/block-slug> [--dry-run]
|
|
322
365
|
wp-typia add pattern <name> [--dry-run]
|
|
323
|
-
wp-typia add binding-source <name> [--dry-run]
|
|
366
|
+
wp-typia add binding-source <name> [--block <block-slug|namespace/block-slug> --attribute <attribute>] [--dry-run]
|
|
324
367
|
wp-typia add rest-resource <name> [--namespace <vendor/v1>] [--methods <list,read,create,update,delete>] [--dry-run]
|
|
325
368
|
wp-typia add ability <name> [--dry-run]
|
|
326
369
|
wp-typia add ai-feature <name> [--namespace <vendor/v1>] [--dry-run]
|
|
@@ -330,13 +373,17 @@ export function formatAddHelpText() {
|
|
|
330
373
|
Notes:
|
|
331
374
|
\`wp-typia add\` runs only inside official ${WORKSPACE_TEMPLATE_PACKAGE} workspaces scaffolded via \`wp-typia create <project-dir> --template workspace\`.
|
|
332
375
|
Pass \`--dry-run\` to preview the workspace files that would change without writing them.
|
|
376
|
+
Interactive add flows let you choose a template when \`--template\` is omitted; non-interactive runs default to \`basic\`.
|
|
377
|
+
\`add admin-view\` scaffolds an opt-in DataViews-powered WordPress admin screen under \`src/admin-views/\`; pass \`--source rest-resource:<slug>\` to reuse a list-capable REST resource.
|
|
333
378
|
\`query-loop\` is a create-time scaffold family. Use \`wp-typia create <project-dir> --template query-loop\` instead of \`wp-typia add block\`.
|
|
334
379
|
\`add variation\` targets an existing block slug from \`scripts/block-config.ts\`.
|
|
380
|
+
\`add style\` registers a Block Styles option for an existing generated block.
|
|
381
|
+
\`add transform\` adds a block-to-block transform into an existing generated block.
|
|
335
382
|
\`add pattern\` scaffolds a namespaced PHP pattern shell under \`src/patterns/\`.
|
|
336
|
-
\`add binding-source\` scaffolds shared PHP and editor registration under \`src/bindings
|
|
383
|
+
\`add binding-source\` scaffolds shared PHP and editor registration under \`src/bindings/\`; pass \`--block\` and \`--attribute\` together to declare an end-to-end bindable attribute on an existing generated block.
|
|
337
384
|
\`add rest-resource\` scaffolds plugin-level TypeScript REST contracts under \`src/rest/\` and PHP route glue under \`inc/rest/\`.
|
|
338
385
|
\`add ability\` scaffolds typed workflow abilities under \`src/abilities/\` and server registration under \`inc/abilities/\`.
|
|
339
386
|
\`add ai-feature\` scaffolds server-owned AI feature endpoints under \`src/ai-features/\` and PHP route glue under \`inc/ai-features/\`.
|
|
340
387
|
\`add hooked-block\` patches an existing workspace block's \`block.json\` \`blockHooks\` metadata.
|
|
341
|
-
\`add editor-plugin\` scaffolds a document-level editor extension under \`src/editor-plugins
|
|
388
|
+
\`add editor-plugin\` scaffolds a document-level editor extension under \`src/editor-plugins/\`; legacy aliases \`PluginSidebar\` and \`PluginDocumentSettingPanel\` resolve to \`sidebar\` and \`document-setting-panel\`.`;
|
|
342
389
|
}
|
|
@@ -5,7 +5,8 @@ import { syncTypeSchemas } from "@wp-typia/block-runtime/metadata-core";
|
|
|
5
5
|
import semver from "semver";
|
|
6
6
|
import { appendWorkspaceInventoryEntries, readWorkspaceInventory, } from "./workspace-inventory.js";
|
|
7
7
|
import { resolveWorkspaceProject } from "./workspace-project.js";
|
|
8
|
-
import { toTitleCase } from "./string-case.js";
|
|
8
|
+
import { toPascalCase, toTitleCase } from "./string-case.js";
|
|
9
|
+
import { escapeRegex, findPhpFunctionRange, hasPhpFunctionDefinition, quotePhpString, replacePhpFunctionDefinition, } from "./php-utils.js";
|
|
9
10
|
import { assertAbilityDoesNotExist, assertValidGeneratedSlug, getWorkspaceBootstrapPath, normalizeBlockSlug, patchFile, quoteTsString, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
|
|
10
11
|
import { REQUIRED_WORKSPACE_ABILITY_COMPATIBILITY, renderScaffoldCompatibilityConfig, resolveScaffoldCompatibilityPolicy, updatePluginHeaderCompatibility, } from "./scaffold-compatibility.js";
|
|
11
12
|
const ABILITY_SERVER_GLOB = "/inc/abilities/*.php";
|
|
@@ -17,46 +18,6 @@ const WP_ABILITIES_PACKAGE_VERSION = "^0.10.0";
|
|
|
17
18
|
const WP_CORE_ABILITIES_PACKAGE_VERSION = "^0.9.0";
|
|
18
19
|
const WP_ABILITIES_SCRIPT_MODULE_ID = "@wordpress/abilities";
|
|
19
20
|
const WP_CORE_ABILITIES_SCRIPT_MODULE_ID = "@wordpress/core-abilities";
|
|
20
|
-
function escapeRegex(value) {
|
|
21
|
-
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
22
|
-
}
|
|
23
|
-
function quotePhpString(value) {
|
|
24
|
-
return `'${value.replace(/\\/gu, "\\\\").replace(/'/gu, "\\'")}'`;
|
|
25
|
-
}
|
|
26
|
-
function findPhpFunctionRange(source, functionName) {
|
|
27
|
-
const functionPattern = new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\([^)]*\\)\\s*\\{`, "u");
|
|
28
|
-
const match = functionPattern.exec(source);
|
|
29
|
-
if (!match) {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
const openingBraceIndex = match.index + match[0].length - 1;
|
|
33
|
-
let depth = 0;
|
|
34
|
-
for (let index = openingBraceIndex; index < source.length; index += 1) {
|
|
35
|
-
const character = source[index];
|
|
36
|
-
if (character === "{") {
|
|
37
|
-
depth += 1;
|
|
38
|
-
}
|
|
39
|
-
else if (character === "}") {
|
|
40
|
-
depth -= 1;
|
|
41
|
-
if (depth === 0) {
|
|
42
|
-
const end = index + 1;
|
|
43
|
-
return {
|
|
44
|
-
end,
|
|
45
|
-
source: source.slice(match.index, end),
|
|
46
|
-
start: match.index,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
function replacePhpFunctionDefinition(source, functionName, replacement) {
|
|
54
|
-
const functionRange = findPhpFunctionRange(source, functionName);
|
|
55
|
-
if (!functionRange) {
|
|
56
|
-
return source;
|
|
57
|
-
}
|
|
58
|
-
return `${source.slice(0, functionRange.start)}${replacement.trimStart()}${source.slice(functionRange.end)}`;
|
|
59
|
-
}
|
|
60
21
|
function resolveManagedDependencyVersion(existingVersion, requiredVersion) {
|
|
61
22
|
if (!existingVersion) {
|
|
62
23
|
return requiredVersion;
|
|
@@ -70,13 +31,6 @@ function resolveManagedDependencyVersion(existingVersion, requiredVersion) {
|
|
|
70
31
|
? existingVersion
|
|
71
32
|
: requiredVersion;
|
|
72
33
|
}
|
|
73
|
-
function toPascalCaseFromAbilitySlug(abilitySlug) {
|
|
74
|
-
return normalizeBlockSlug(abilitySlug)
|
|
75
|
-
.split("-")
|
|
76
|
-
.filter(Boolean)
|
|
77
|
-
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
|
78
|
-
.join("");
|
|
79
|
-
}
|
|
80
34
|
function toAbilityCategorySlug(workspaceNamespace) {
|
|
81
35
|
const normalizedNamespace = workspaceNamespace
|
|
82
36
|
.replace(/[^a-z0-9-]+/gu, "-")
|
|
@@ -85,7 +39,7 @@ function toAbilityCategorySlug(workspaceNamespace) {
|
|
|
85
39
|
return `${normalizedNamespace || "workspace"}-workflows`;
|
|
86
40
|
}
|
|
87
41
|
function buildAbilityConfigEntry(abilitySlug, compatibilityPolicy) {
|
|
88
|
-
const pascalCase =
|
|
42
|
+
const pascalCase = toPascalCase(abilitySlug);
|
|
89
43
|
return [
|
|
90
44
|
"\t{",
|
|
91
45
|
`\t\tclientFile: ${quoteTsString(`src/abilities/${abilitySlug}/client.ts`)},`,
|
|
@@ -127,7 +81,7 @@ function buildAbilityConfigSource(abilitySlug, workspaceNamespace) {
|
|
|
127
81
|
}, null, 2)}\n`;
|
|
128
82
|
}
|
|
129
83
|
function buildAbilityTypesSource(abilitySlug) {
|
|
130
|
-
const pascalCase =
|
|
84
|
+
const pascalCase = toPascalCase(abilitySlug);
|
|
131
85
|
return `export interface ${pascalCase}AbilityInput {
|
|
132
86
|
\tcontextId: number;
|
|
133
87
|
\tnote?: string;
|
|
@@ -142,7 +96,7 @@ export interface ${pascalCase}AbilityOutput {
|
|
|
142
96
|
`;
|
|
143
97
|
}
|
|
144
98
|
function buildAbilityDataSource(abilitySlug) {
|
|
145
|
-
const pascalCase =
|
|
99
|
+
const pascalCase = toPascalCase(abilitySlug);
|
|
146
100
|
const abilityConstBase = abilitySlug
|
|
147
101
|
.toUpperCase()
|
|
148
102
|
.replace(/[^A-Z0-9]+/gu, "_")
|
|
@@ -242,7 +196,7 @@ export async function run${pascalCase}Ability(
|
|
|
242
196
|
`;
|
|
243
197
|
}
|
|
244
198
|
function buildAbilityClientSource(abilitySlug) {
|
|
245
|
-
const pascalCase =
|
|
199
|
+
const pascalCase = toPascalCase(abilitySlug);
|
|
246
200
|
return `/**
|
|
247
201
|
* Re-export the typed ${pascalCase} ability client helpers.
|
|
248
202
|
*
|
|
@@ -651,7 +605,6 @@ function ${enqueueFunctionName}() {
|
|
|
651
605
|
/add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
|
|
652
606
|
/\?>\s*$/u,
|
|
653
607
|
];
|
|
654
|
-
const hasPhpFunctionDefinition = (functionName) => new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\(`, "u").test(nextSource);
|
|
655
608
|
const insertPhpSnippet = (snippet) => {
|
|
656
609
|
for (const anchor of insertionAnchors) {
|
|
657
610
|
const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
|
|
@@ -670,14 +623,15 @@ function ${enqueueFunctionName}() {
|
|
|
670
623
|
}
|
|
671
624
|
nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
|
|
672
625
|
};
|
|
673
|
-
if (!hasPhpFunctionDefinition(loadFunctionName)) {
|
|
626
|
+
if (!hasPhpFunctionDefinition(nextSource, loadFunctionName)) {
|
|
674
627
|
insertPhpSnippet(loadFunction);
|
|
675
628
|
}
|
|
676
|
-
if (!hasPhpFunctionDefinition(enqueueFunctionName)) {
|
|
629
|
+
if (!hasPhpFunctionDefinition(nextSource, enqueueFunctionName)) {
|
|
677
630
|
insertPhpSnippet(enqueueFunction);
|
|
678
631
|
}
|
|
679
632
|
else if (!findPhpFunctionRange(nextSource, enqueueFunctionName)?.source.includes("wp_enqueue_script_module")) {
|
|
680
|
-
nextSource =
|
|
633
|
+
nextSource =
|
|
634
|
+
replacePhpFunctionDefinition(nextSource, enqueueFunctionName, enqueueFunction, { trimReplacementStart: true }) ?? nextSource;
|
|
681
635
|
}
|
|
682
636
|
if (!nextSource.includes(loadHook)) {
|
|
683
637
|
appendPhpSnippet(loadHook);
|
|
@@ -882,7 +836,7 @@ export async function runAddAbilityCommand({ abilityName, cwd = process.cwd(), }
|
|
|
882
836
|
await fsp.writeFile(dataFilePath, buildAbilityDataSource(abilitySlug), "utf8");
|
|
883
837
|
await fsp.writeFile(clientFilePath, buildAbilityClientSource(abilitySlug), "utf8");
|
|
884
838
|
await fsp.writeFile(phpFilePath, buildAbilityPhpSource(abilitySlug, workspace), "utf8");
|
|
885
|
-
const pascalCase =
|
|
839
|
+
const pascalCase = toPascalCase(abilitySlug);
|
|
886
840
|
await syncTypeSchemas({
|
|
887
841
|
jsonSchemaFile: `src/abilities/${abilitySlug}/input.schema.json`,
|
|
888
842
|
projectRoot: workspace.projectDir,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type RunAddAdminViewCommandOptions } from "./cli-add-shared.js";
|
|
2
|
+
/**
|
|
3
|
+
* Add one DataViews-powered WordPress admin screen scaffold to an official
|
|
4
|
+
* workspace project.
|
|
5
|
+
*
|
|
6
|
+
* @param options Command options for the admin-view scaffold workflow.
|
|
7
|
+
* @param options.adminViewName Human-entered admin screen name that will be
|
|
8
|
+
* normalized and validated before files are written.
|
|
9
|
+
* @param options.cwd Working directory used to resolve the nearest official workspace.
|
|
10
|
+
* Defaults to `process.cwd()`.
|
|
11
|
+
* @param options.source Optional data source locator. `rest-resource:<slug>`
|
|
12
|
+
* wires the screen to an existing list-capable REST resource.
|
|
13
|
+
* @returns A promise that resolves with the normalized `adminViewSlug`, optional
|
|
14
|
+
* `source`, and owning `projectDir` after scaffold files and inventory entries
|
|
15
|
+
* are written successfully.
|
|
16
|
+
* @throws {Error} When the command is run outside an official workspace, when
|
|
17
|
+
* the slug/source is invalid, or when a conflicting file or inventory entry exists.
|
|
18
|
+
*/
|
|
19
|
+
export declare function runAddAdminViewCommand({ adminViewName, cwd, source, }: RunAddAdminViewCommandOptions): Promise<{
|
|
20
|
+
adminViewSlug: string;
|
|
21
|
+
projectDir: string;
|
|
22
|
+
source?: string;
|
|
23
|
+
}>;
|