@wp-typia/project-tools 0.17.0 → 0.19.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/alternate-render-targets.d.ts +5 -0
- package/dist/runtime/alternate-render-targets.js +29 -0
- package/dist/runtime/block-generator-service-core.d.ts +2 -2
- package/dist/runtime/block-generator-service-core.js +13 -8
- package/dist/runtime/block-generator-service-spec.d.ts +10 -2
- package/dist/runtime/block-generator-service-spec.js +43 -1
- package/dist/runtime/built-in-block-artifacts.js +1 -0
- package/dist/runtime/built-in-block-code-templates/compound-child.d.ts +2 -2
- package/dist/runtime/built-in-block-code-templates/compound-child.js +35 -2
- package/dist/runtime/built-in-block-code-templates/compound-parent.d.ts +2 -2
- package/dist/runtime/built-in-block-code-templates/compound-parent.js +204 -27
- package/dist/runtime/built-in-block-code-templates/compound-persistence.d.ts +1 -1
- package/dist/runtime/built-in-block-code-templates/compound-persistence.js +11 -8
- package/dist/runtime/built-in-block-non-ts-artifacts.js +505 -2
- package/dist/runtime/cli-add-block.d.ts +6 -2
- package/dist/runtime/cli-add-block.js +71 -24
- package/dist/runtime/cli-add-shared.d.ts +58 -2
- package/dist/runtime/cli-add-shared.js +111 -12
- package/dist/runtime/cli-add-workspace-assets.d.ts +21 -1
- package/dist/runtime/cli-add-workspace-assets.js +417 -1
- package/dist/runtime/cli-add-workspace-rest.d.ts +14 -0
- package/dist/runtime/cli-add-workspace-rest.js +1060 -0
- package/dist/runtime/cli-add-workspace.d.ts +10 -1
- package/dist/runtime/cli-add-workspace.js +10 -1
- package/dist/runtime/cli-add.d.ts +3 -3
- package/dist/runtime/cli-add.js +2 -2
- package/dist/runtime/cli-core.d.ts +5 -1
- package/dist/runtime/cli-core.js +3 -1
- package/dist/runtime/cli-doctor-workspace.js +135 -1
- package/dist/runtime/cli-help.js +12 -7
- package/dist/runtime/cli-scaffold.d.ts +12 -2
- package/dist/runtime/cli-scaffold.js +222 -46
- package/dist/runtime/cli-templates.d.ts +4 -4
- package/dist/runtime/cli-templates.js +104 -39
- package/dist/runtime/cli-validation.d.ts +66 -0
- package/dist/runtime/cli-validation.js +92 -0
- package/dist/runtime/compound-inner-blocks.d.ts +78 -0
- package/dist/runtime/compound-inner-blocks.js +88 -0
- package/dist/runtime/index.d.ts +6 -3
- package/dist/runtime/index.js +4 -2
- package/dist/runtime/local-dev-presets.js +7 -2
- package/dist/runtime/migration-command-surface.js +2 -0
- package/dist/runtime/package-versions.d.ts +1 -0
- package/dist/runtime/package-versions.js +12 -0
- package/dist/runtime/rest-resource-artifacts.d.ts +35 -0
- package/dist/runtime/rest-resource-artifacts.js +158 -0
- package/dist/runtime/scaffold-answer-resolution.js +78 -8
- package/dist/runtime/scaffold-apply-utils.d.ts +4 -3
- package/dist/runtime/scaffold-apply-utils.js +34 -17
- package/dist/runtime/scaffold-bootstrap.d.ts +15 -0
- package/dist/runtime/scaffold-bootstrap.js +29 -7
- package/dist/runtime/scaffold-documents.js +24 -3
- package/dist/runtime/scaffold-identifiers.d.ts +17 -0
- package/dist/runtime/scaffold-identifiers.js +22 -0
- package/dist/runtime/scaffold-onboarding.js +25 -13
- package/dist/runtime/scaffold-package-manager-files.js +6 -1
- package/dist/runtime/scaffold-template-variables.js +22 -0
- package/dist/runtime/scaffold.d.ts +22 -1
- package/dist/runtime/scaffold.js +56 -11
- package/dist/runtime/template-render.d.ts +5 -2
- package/dist/runtime/template-render.js +9 -3
- package/dist/runtime/template-source-contracts.d.ts +11 -0
- package/dist/runtime/template-source-external.d.ts +1 -1
- package/dist/runtime/template-source-external.js +45 -13
- package/dist/runtime/template-source-normalization.d.ts +1 -1
- package/dist/runtime/template-source-normalization.js +5 -1
- package/dist/runtime/template-source-remote.d.ts +5 -0
- package/dist/runtime/template-source-remote.js +33 -0
- package/dist/runtime/template-source.js +35 -4
- package/dist/runtime/workspace-inventory.d.ts +43 -1
- package/dist/runtime/workspace-inventory.js +132 -1
- package/dist/runtime/workspace-project.d.ts +1 -1
- package/dist/runtime/workspace-project.js +3 -3
- package/package.json +9 -4
- package/templates/_shared/compound/core/scripts/add-compound-child.ts.mustache +728 -49
- package/templates/query-loop/src/validator-toolkit.ts.mustache +0 -1
|
@@ -10,13 +10,24 @@ import { getDefaultAnswers, scaffoldProject } from "./scaffold.js";
|
|
|
10
10
|
import { copyInterpolatedDirectory, listInterpolatedDirectoryOutputs, } from "./template-render.js";
|
|
11
11
|
import { appendWorkspaceInventoryEntries, } from "./workspace-inventory.js";
|
|
12
12
|
import { resolveWorkspaceProject, } from "./workspace-project.js";
|
|
13
|
-
import { ADD_BLOCK_TEMPLATE_IDS, buildWorkspacePhpPrefix, isAddBlockTemplateId,
|
|
13
|
+
import { ADD_BLOCK_TEMPLATE_IDS, buildWorkspacePhpPrefix, isAddBlockTemplateId, patchFile, readOptionalFile, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
|
|
14
|
+
import { resolveNonEmptyNormalizedBlockSlug, } from "./scaffold-identifiers.js";
|
|
14
15
|
import { buildConfigEntries, buildMigrationBlocks, buildServerTemplateRoot, } from "./cli-add-block-config.js";
|
|
15
16
|
import { COMPOUND_SHARED_SUPPORT_FILES, collectLegacyCompoundValidatorPaths, ensureBlockConfigCanAddRestManifests, ensureCompoundWorkspaceSupportFiles, } from "./cli-add-block-legacy-validator.js";
|
|
16
17
|
import { parseTemplateLocator, resolveTemplateSeed, } from "./template-source.js";
|
|
17
18
|
import { resolveExternalTemplateLayers, } from "./template-layers.js";
|
|
19
|
+
import { formatInstallCommand, } from "./package-managers.js";
|
|
20
|
+
import { parseCompoundInnerBlocksPreset } from "./compound-inner-blocks.js";
|
|
18
21
|
import { resolveOptionalInteractiveExternalLayerId, } from "./external-layer-selection.js";
|
|
22
|
+
import { parseAlternateRenderTargets } from "./alternate-render-targets.js";
|
|
23
|
+
import { assertExternalLayerCompositionOptions, normalizeOptionalCliString, resolveLocalCliPathOption, } from "./cli-validation.js";
|
|
19
24
|
const COLLECTION_IMPORT_LINE = "import '../../collection';";
|
|
25
|
+
// This is a lightweight preflight heuristic for the common install layouts:
|
|
26
|
+
// node_modules for npm/pnpm/bun/yarn-classic and Yarn PnP marker files.
|
|
27
|
+
// It intentionally favors fast user guidance over exhaustive detection, so
|
|
28
|
+
// stale node_modules or hoisted installs outside the workspace root can still
|
|
29
|
+
// fall through to deeper resolver errors.
|
|
30
|
+
const WORKSPACE_INSTALL_MARKERS = ["node_modules", ".pnp.cjs", ".pnp.loader.mjs"];
|
|
20
31
|
async function ensureCollectionImport(filePath) {
|
|
21
32
|
await patchFile(filePath, (source) => {
|
|
22
33
|
if (source.includes(COLLECTION_IMPORT_LINE)) {
|
|
@@ -54,20 +65,14 @@ async function renderWorkspacePersistenceServerModule(projectDir, variables) {
|
|
|
54
65
|
const templateDir = buildServerTemplateRoot(variables.persistencePolicy);
|
|
55
66
|
await copyInterpolatedDirectory(templateDir, targetDir, variables);
|
|
56
67
|
}
|
|
57
|
-
function
|
|
58
|
-
|
|
59
|
-
return undefined;
|
|
60
|
-
}
|
|
61
|
-
const trimmed = value.trim();
|
|
62
|
-
return trimmed.length > 0 ? trimmed : undefined;
|
|
68
|
+
function hasInstalledWorkspaceDependencies(projectDir) {
|
|
69
|
+
return WORKSPACE_INSTALL_MARKERS.some((marker) => fs.existsSync(path.join(projectDir, marker)));
|
|
63
70
|
}
|
|
64
|
-
function
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
!(path.isAbsolute(source) || source.startsWith("./") || source.startsWith("../"))) {
|
|
68
|
-
return source;
|
|
71
|
+
function assertWorkspaceDependenciesInstalled(workspace) {
|
|
72
|
+
if (hasInstalledWorkspaceDependencies(workspace.projectDir)) {
|
|
73
|
+
return;
|
|
69
74
|
}
|
|
70
|
-
|
|
75
|
+
throw new Error(`Workspace dependencies have not been installed yet. Run \`${formatInstallCommand(workspace.packageManager)}\` from the workspace root before using \`wp-typia add block ...\`.`);
|
|
71
76
|
}
|
|
72
77
|
async function copyScaffoldedBlockSlice(projectDir, templateId, tempProjectDir, variables, legacyValidatorPaths = []) {
|
|
73
78
|
if (templateId === "compound") {
|
|
@@ -191,12 +196,14 @@ async function syncWorkspaceAddedBlockArtifacts(projectDir, templateId, variable
|
|
|
191
196
|
}
|
|
192
197
|
}
|
|
193
198
|
function assertPersistenceFlagsAllowed(templateId, options) {
|
|
194
|
-
const hasPersistenceFlags = typeof options.
|
|
199
|
+
const hasPersistenceFlags = typeof options.alternateRenderTargets === "string" ||
|
|
200
|
+
typeof options.dataStorageMode === "string" ||
|
|
195
201
|
typeof options.persistencePolicy === "string";
|
|
196
202
|
if (!hasPersistenceFlags) {
|
|
197
203
|
return;
|
|
198
204
|
}
|
|
199
205
|
if (templateId === "persistence" || templateId === "compound") {
|
|
206
|
+
parseAlternateRenderTargets(options.alternateRenderTargets);
|
|
200
207
|
if (typeof options.dataStorageMode === "string" &&
|
|
201
208
|
options.dataStorageMode !== "custom-table" &&
|
|
202
209
|
options.dataStorageMode !== "post-meta") {
|
|
@@ -207,9 +214,24 @@ function assertPersistenceFlagsAllowed(templateId, options) {
|
|
|
207
214
|
options.persistencePolicy !== "public") {
|
|
208
215
|
throw new Error(`Unsupported persistence policy "${options.persistencePolicy}". Expected one of: authenticated, public.`);
|
|
209
216
|
}
|
|
217
|
+
if (templateId === "compound" &&
|
|
218
|
+
typeof options.alternateRenderTargets === "string" &&
|
|
219
|
+
!options.dataStorageMode &&
|
|
220
|
+
!options.persistencePolicy) {
|
|
221
|
+
throw new Error("`--alternate-render-targets` on `wp-typia add block --template compound` requires the persistence-enabled server render path. Add `--data-storage <post-meta|custom-table>` or `--persistence-policy <authenticated|public>` first.");
|
|
222
|
+
}
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
throw new Error(`--data-storage, --persistence-policy, and --alternate-render-targets are supported only for \`wp-typia add block --template persistence\` or persistence-enabled \`--template compound\`.`);
|
|
226
|
+
}
|
|
227
|
+
function assertCompoundInnerBlocksPresetAllowed(templateId, innerBlocksPreset) {
|
|
228
|
+
if (!innerBlocksPreset) {
|
|
210
229
|
return;
|
|
211
230
|
}
|
|
212
|
-
|
|
231
|
+
if (templateId !== "compound") {
|
|
232
|
+
throw new Error("`--inner-blocks-preset` is supported only for `wp-typia add block --template compound`.");
|
|
233
|
+
}
|
|
234
|
+
parseCompoundInnerBlocksPreset(innerBlocksPreset);
|
|
213
235
|
}
|
|
214
236
|
/**
|
|
215
237
|
* Seeds an empty official workspace migration project before any blocks are added.
|
|
@@ -239,6 +261,9 @@ export async function seedWorkspaceMigrationProject(projectDir, currentMigration
|
|
|
239
261
|
* workspace. Defaults to `process.cwd()`.
|
|
240
262
|
* @param options.dataStorageMode Optional storage mode for persistence-capable
|
|
241
263
|
* templates.
|
|
264
|
+
* @param options.innerBlocksPreset Optional compound-only InnerBlocks preset
|
|
265
|
+
* (`freeform`, `ordered`, `horizontal`, or `locked-structure`) that controls
|
|
266
|
+
* the generated authoring defaults for nested compound containers.
|
|
242
267
|
* @param options.persistencePolicy Optional persistence policy for
|
|
243
268
|
* persistence-capable templates.
|
|
244
269
|
* @param options.templateId Built-in block family to scaffold. Defaults to
|
|
@@ -248,17 +273,36 @@ export async function seedWorkspaceMigrationProject(projectDir, currentMigration
|
|
|
248
273
|
* succeeds.
|
|
249
274
|
* @throws {Error} When the template id is unknown, persistence flags are used
|
|
250
275
|
* with unsupported templates, the command runs outside an official workspace,
|
|
251
|
-
* or target block paths
|
|
276
|
+
* workspace dependencies have not been installed yet, or target block paths
|
|
277
|
+
* already exist.
|
|
252
278
|
*/
|
|
253
|
-
export async function runAddBlockCommand({ blockName, cwd = process.cwd(), dataStorageMode, externalLayerId, externalLayerSource, persistencePolicy, selectExternalLayerId, templateId = "basic", }) {
|
|
279
|
+
export async function runAddBlockCommand({ alternateRenderTargets, blockName, cwd = process.cwd(), dataStorageMode, externalLayerId, externalLayerSource, innerBlocksPreset, persistencePolicy, selectExternalLayerId, templateId = "basic", }) {
|
|
280
|
+
if (templateId === "query-loop") {
|
|
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
|
+
}
|
|
254
283
|
if (!isAddBlockTemplateId(templateId)) {
|
|
255
284
|
throw new Error(`Unknown add-block template "${templateId}". Expected one of: ${ADD_BLOCK_TEMPLATE_IDS.join(", ")}`);
|
|
256
285
|
}
|
|
257
286
|
const resolvedTemplateId = templateId;
|
|
258
|
-
assertPersistenceFlagsAllowed(resolvedTemplateId, {
|
|
287
|
+
assertPersistenceFlagsAllowed(resolvedTemplateId, {
|
|
288
|
+
alternateRenderTargets,
|
|
289
|
+
dataStorageMode,
|
|
290
|
+
persistencePolicy,
|
|
291
|
+
});
|
|
292
|
+
assertCompoundInnerBlocksPresetAllowed(resolvedTemplateId, innerBlocksPreset);
|
|
293
|
+
const resolvedInnerBlocksPreset = parseCompoundInnerBlocksPreset(innerBlocksPreset);
|
|
259
294
|
const workspace = resolveWorkspaceProject(cwd);
|
|
260
|
-
|
|
261
|
-
const
|
|
295
|
+
assertWorkspaceDependenciesInstalled(workspace);
|
|
296
|
+
const normalizedExternalLayerId = normalizeOptionalCliString(externalLayerId);
|
|
297
|
+
const normalizedExternalLayerSource = resolveLocalCliPathOption({
|
|
298
|
+
cwd,
|
|
299
|
+
label: "--external-layer-source",
|
|
300
|
+
value: externalLayerSource,
|
|
301
|
+
});
|
|
302
|
+
assertExternalLayerCompositionOptions({
|
|
303
|
+
externalLayerId: normalizedExternalLayerId,
|
|
304
|
+
externalLayerSource: normalizedExternalLayerSource,
|
|
305
|
+
});
|
|
262
306
|
const resolvedExternalLayerSelection = await resolveOptionalInteractiveExternalLayerId({
|
|
263
307
|
callerCwd: cwd,
|
|
264
308
|
externalLayerId: normalizedExternalLayerId,
|
|
@@ -267,10 +311,11 @@ export async function runAddBlockCommand({ blockName, cwd = process.cwd(), dataS
|
|
|
267
311
|
});
|
|
268
312
|
let tempRoot = "";
|
|
269
313
|
try {
|
|
270
|
-
const normalizedSlug =
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
314
|
+
const normalizedSlug = resolveNonEmptyNormalizedBlockSlug({
|
|
315
|
+
input: blockName,
|
|
316
|
+
label: "Block name",
|
|
317
|
+
usage: "wp-typia add block <name> --template <family>",
|
|
318
|
+
});
|
|
274
319
|
const defaults = getDefaultAnswers(normalizedSlug, resolvedTemplateId);
|
|
275
320
|
tempRoot = await fsp.mkdtemp(path.join(os.tmpdir(), "wp-typia-add-block-"));
|
|
276
321
|
const tempProjectDir = path.join(tempRoot, normalizedSlug);
|
|
@@ -287,9 +332,11 @@ export async function runAddBlockCommand({ blockName, cwd = process.cwd(), dataS
|
|
|
287
332
|
: [];
|
|
288
333
|
const result = await (async () => {
|
|
289
334
|
const scaffoldResult = await scaffoldProject({
|
|
335
|
+
alternateRenderTargets,
|
|
290
336
|
answers: {
|
|
291
337
|
...defaults,
|
|
292
338
|
author: workspace.author,
|
|
339
|
+
compoundInnerBlocksPreset: resolvedInnerBlocksPreset,
|
|
293
340
|
namespace: workspace.workspace.namespace,
|
|
294
341
|
phpPrefix: blockPhpPrefix,
|
|
295
342
|
slug: normalizedSlug,
|
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
import { type HookedBlockPositionId } from "./hooked-blocks.js";
|
|
2
2
|
import { type WorkspaceInventory } from "./workspace-inventory.js";
|
|
3
3
|
import { type WorkspaceProject } from "./workspace-project.js";
|
|
4
|
+
export { normalizeBlockSlug, } from "./scaffold-identifiers.js";
|
|
4
5
|
/**
|
|
5
6
|
* Supported top-level `wp-typia add` kinds exposed by the canonical CLI.
|
|
6
7
|
*/
|
|
7
|
-
export declare const ADD_KIND_IDS: readonly ["block", "variation", "pattern", "binding-source", "hooked-block"];
|
|
8
|
+
export declare const ADD_KIND_IDS: readonly ["block", "variation", "pattern", "binding-source", "rest-resource", "hooked-block", "editor-plugin"];
|
|
8
9
|
export type AddKindId = (typeof ADD_KIND_IDS)[number];
|
|
10
|
+
/**
|
|
11
|
+
* Supported plugin-level REST resource methods accepted by
|
|
12
|
+
* `wp-typia add rest-resource --methods`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const REST_RESOURCE_METHOD_IDS: readonly ["list", "read", "create", "update", "delete"];
|
|
15
|
+
export type RestResourceMethodId = (typeof REST_RESOURCE_METHOD_IDS)[number];
|
|
16
|
+
/**
|
|
17
|
+
* Supported editor-plugin shell slots accepted by `wp-typia add editor-plugin --slot`.
|
|
18
|
+
*/
|
|
19
|
+
export declare const EDITOR_PLUGIN_SLOT_IDS: readonly ["PluginSidebar"];
|
|
20
|
+
export type EditorPluginSlotId = (typeof EDITOR_PLUGIN_SLOT_IDS)[number];
|
|
9
21
|
/**
|
|
10
22
|
* Supported built-in block families accepted by `wp-typia add block --template`.
|
|
11
23
|
*/
|
|
12
24
|
export declare const ADD_BLOCK_TEMPLATE_IDS: readonly ["basic", "interactivity", "persistence", "compound"];
|
|
13
25
|
export type AddBlockTemplateId = (typeof ADD_BLOCK_TEMPLATE_IDS)[number];
|
|
26
|
+
export declare const REST_RESOURCE_NAMESPACE_PATTERN: RegExp;
|
|
14
27
|
export interface RunAddVariationCommandOptions {
|
|
15
28
|
blockName: string;
|
|
16
29
|
cwd?: string;
|
|
@@ -24,18 +37,40 @@ export interface RunAddBindingSourceCommandOptions {
|
|
|
24
37
|
bindingSourceName: string;
|
|
25
38
|
cwd?: string;
|
|
26
39
|
}
|
|
40
|
+
export interface RunAddRestResourceCommandOptions {
|
|
41
|
+
cwd?: string;
|
|
42
|
+
methods?: string;
|
|
43
|
+
namespace?: string;
|
|
44
|
+
restResourceName: string;
|
|
45
|
+
}
|
|
27
46
|
export interface RunAddHookedBlockCommandOptions {
|
|
28
47
|
anchorBlockName: string;
|
|
29
48
|
blockName: string;
|
|
30
49
|
cwd?: string;
|
|
31
50
|
position: string;
|
|
32
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Options for `wp-typia add editor-plugin`.
|
|
54
|
+
*
|
|
55
|
+
* @property cwd Working directory used to resolve the nearest official workspace.
|
|
56
|
+
* Defaults to `process.cwd()`.
|
|
57
|
+
* @property editorPluginName Human-entered editor plugin name that will be
|
|
58
|
+
* normalized into the generated slug.
|
|
59
|
+
* @property slot Optional editor shell slot. Defaults to `PluginSidebar`.
|
|
60
|
+
*/
|
|
61
|
+
export interface RunAddEditorPluginCommandOptions {
|
|
62
|
+
cwd?: string;
|
|
63
|
+
editorPluginName: string;
|
|
64
|
+
slot?: string;
|
|
65
|
+
}
|
|
33
66
|
export interface RunAddBlockCommandOptions {
|
|
67
|
+
alternateRenderTargets?: string;
|
|
34
68
|
blockName: string;
|
|
35
69
|
cwd?: string;
|
|
36
70
|
dataStorageMode?: string;
|
|
37
71
|
externalLayerId?: string;
|
|
38
72
|
externalLayerSource?: string;
|
|
73
|
+
innerBlocksPreset?: string;
|
|
39
74
|
persistencePolicy?: string;
|
|
40
75
|
selectExternalLayerId?: (options: Array<{
|
|
41
76
|
description?: string;
|
|
@@ -57,8 +92,10 @@ export interface WorkspaceMutationSnapshot {
|
|
|
57
92
|
/** Files or directories created by the mutation that should be removed on rollback. */
|
|
58
93
|
targetPaths: string[];
|
|
59
94
|
}
|
|
60
|
-
export declare function normalizeBlockSlug(input: string): string;
|
|
61
95
|
export declare function assertValidGeneratedSlug(label: string, slug: string, usage: string): string;
|
|
96
|
+
export declare function assertValidRestResourceNamespace(namespace: string): string;
|
|
97
|
+
export declare function resolveRestResourceNamespace(workspaceNamespace: string, namespace?: string): string;
|
|
98
|
+
export declare function assertValidRestResourceMethods(methods?: string): RestResourceMethodId[];
|
|
62
99
|
export declare function assertValidHookedBlockPosition(position: string): HookedBlockPositionId;
|
|
63
100
|
export declare function getWorkspaceBootstrapPath(workspace: WorkspaceProject): string;
|
|
64
101
|
export declare function buildWorkspacePhpPrefix(workspacePhpPrefix: string, slug: string): string;
|
|
@@ -86,6 +123,14 @@ export declare function snapshotWorkspaceFiles(filePaths: string[]): Promise<Wor
|
|
|
86
123
|
export declare function rollbackWorkspaceMutation(snapshot: WorkspaceMutationSnapshot): Promise<void>;
|
|
87
124
|
export declare function resolveWorkspaceBlock(inventory: WorkspaceInventory, blockSlug: string): WorkspaceInventory["blocks"][number];
|
|
88
125
|
export declare function assertValidHookAnchor(anchorBlockName: string): string;
|
|
126
|
+
/**
|
|
127
|
+
* Validate and normalize the editor plugin shell slot.
|
|
128
|
+
*
|
|
129
|
+
* @param slot Optional shell slot. Defaults to `PluginSidebar`.
|
|
130
|
+
* @returns The canonical editor plugin slot id.
|
|
131
|
+
* @throws {Error} When the slot is not supported by the workspace scaffold.
|
|
132
|
+
*/
|
|
133
|
+
export declare function assertValidEditorPluginSlot(slot?: string): EditorPluginSlotId;
|
|
89
134
|
export declare function readWorkspaceBlockJson(projectDir: string, blockSlug: string): {
|
|
90
135
|
blockJson: Record<string, unknown>;
|
|
91
136
|
blockJsonPath: string;
|
|
@@ -94,6 +139,17 @@ export declare function getMutableBlockHooks(blockJson: Record<string, unknown>,
|
|
|
94
139
|
export declare function assertVariationDoesNotExist(projectDir: string, blockSlug: string, variationSlug: string, inventory: WorkspaceInventory): void;
|
|
95
140
|
export declare function assertPatternDoesNotExist(projectDir: string, patternSlug: string, inventory: WorkspaceInventory): void;
|
|
96
141
|
export declare function assertBindingSourceDoesNotExist(projectDir: string, bindingSourceSlug: string, inventory: WorkspaceInventory): void;
|
|
142
|
+
export declare function assertRestResourceDoesNotExist(projectDir: string, restResourceSlug: string, inventory: WorkspaceInventory): void;
|
|
143
|
+
/**
|
|
144
|
+
* Ensure an editor plugin scaffold does not already exist on disk or in the
|
|
145
|
+
* workspace inventory.
|
|
146
|
+
*
|
|
147
|
+
* @param projectDir Workspace root directory.
|
|
148
|
+
* @param editorPluginSlug Normalized editor plugin slug.
|
|
149
|
+
* @param inventory Parsed workspace inventory.
|
|
150
|
+
* @throws {Error} When the directory or inventory entry already exists.
|
|
151
|
+
*/
|
|
152
|
+
export declare function assertEditorPluginDoesNotExist(projectDir: string, editorPluginSlug: string, inventory: WorkspaceInventory): void;
|
|
97
153
|
/**
|
|
98
154
|
* Returns help text for the canonical `wp-typia add` subcommands.
|
|
99
155
|
*/
|
|
@@ -3,12 +3,36 @@ import { promises as fsp } from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { parseScaffoldBlockMetadata } from "@wp-typia/block-runtime/blocks";
|
|
5
5
|
import { HOOKED_BLOCK_ANCHOR_PATTERN, HOOKED_BLOCK_POSITION_IDS, } from "./hooked-blocks.js";
|
|
6
|
-
import {
|
|
6
|
+
import { toSnakeCase, } from "./string-case.js";
|
|
7
7
|
import { WORKSPACE_TEMPLATE_PACKAGE, } from "./workspace-project.js";
|
|
8
|
+
export { normalizeBlockSlug, } from "./scaffold-identifiers.js";
|
|
8
9
|
/**
|
|
9
10
|
* Supported top-level `wp-typia add` kinds exposed by the canonical CLI.
|
|
10
11
|
*/
|
|
11
|
-
export const ADD_KIND_IDS = [
|
|
12
|
+
export const ADD_KIND_IDS = [
|
|
13
|
+
"block",
|
|
14
|
+
"variation",
|
|
15
|
+
"pattern",
|
|
16
|
+
"binding-source",
|
|
17
|
+
"rest-resource",
|
|
18
|
+
"hooked-block",
|
|
19
|
+
"editor-plugin",
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* Supported plugin-level REST resource methods accepted by
|
|
23
|
+
* `wp-typia add rest-resource --methods`.
|
|
24
|
+
*/
|
|
25
|
+
export const REST_RESOURCE_METHOD_IDS = [
|
|
26
|
+
"list",
|
|
27
|
+
"read",
|
|
28
|
+
"create",
|
|
29
|
+
"update",
|
|
30
|
+
"delete",
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Supported editor-plugin shell slots accepted by `wp-typia add editor-plugin --slot`.
|
|
34
|
+
*/
|
|
35
|
+
export const EDITOR_PLUGIN_SLOT_IDS = ["PluginSidebar"];
|
|
12
36
|
/**
|
|
13
37
|
* Supported built-in block families accepted by `wp-typia add block --template`.
|
|
14
38
|
*/
|
|
@@ -19,9 +43,7 @@ export const ADD_BLOCK_TEMPLATE_IDS = [
|
|
|
19
43
|
"compound",
|
|
20
44
|
];
|
|
21
45
|
const WORKSPACE_GENERATED_SLUG_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
22
|
-
export
|
|
23
|
-
return toKebabCase(input);
|
|
24
|
-
}
|
|
46
|
+
export const REST_RESOURCE_NAMESPACE_PATTERN = /^[a-z][a-z0-9-]*(?:\/[a-z0-9-]+)+$/u;
|
|
25
47
|
export function assertValidGeneratedSlug(label, slug, usage) {
|
|
26
48
|
if (!slug) {
|
|
27
49
|
throw new Error(`${label} is required. Use \`${usage}\`.`);
|
|
@@ -31,6 +53,33 @@ export function assertValidGeneratedSlug(label, slug, usage) {
|
|
|
31
53
|
}
|
|
32
54
|
return slug;
|
|
33
55
|
}
|
|
56
|
+
export function assertValidRestResourceNamespace(namespace) {
|
|
57
|
+
const trimmed = namespace.trim();
|
|
58
|
+
if (!trimmed) {
|
|
59
|
+
throw new Error("REST resource namespace is required. Use `--namespace <vendor/v1>` or let the workspace default apply.");
|
|
60
|
+
}
|
|
61
|
+
if (!REST_RESOURCE_NAMESPACE_PATTERN.test(trimmed)) {
|
|
62
|
+
throw new Error("REST resource namespace must use lowercase slash-separated segments like `demo-space/v1`.");
|
|
63
|
+
}
|
|
64
|
+
return trimmed;
|
|
65
|
+
}
|
|
66
|
+
export function resolveRestResourceNamespace(workspaceNamespace, namespace) {
|
|
67
|
+
return assertValidRestResourceNamespace(namespace ?? `${workspaceNamespace}/v1`);
|
|
68
|
+
}
|
|
69
|
+
export function assertValidRestResourceMethods(methods) {
|
|
70
|
+
const rawMethods = typeof methods === "string" && methods.trim().length > 0
|
|
71
|
+
? methods.split(",").map((value) => value.trim()).filter(Boolean)
|
|
72
|
+
: ["list", "read", "create"];
|
|
73
|
+
const normalizedMethods = Array.from(new Set(rawMethods));
|
|
74
|
+
const invalidMethods = normalizedMethods.filter((method) => !REST_RESOURCE_METHOD_IDS.includes(method));
|
|
75
|
+
if (invalidMethods.length > 0) {
|
|
76
|
+
throw new Error(`REST resource methods must be a comma-separated list of: ${REST_RESOURCE_METHOD_IDS.join(", ")}.`);
|
|
77
|
+
}
|
|
78
|
+
if (normalizedMethods.length === 0) {
|
|
79
|
+
throw new Error("REST resource methods must include at least one of: list, read, create, update, delete.");
|
|
80
|
+
}
|
|
81
|
+
return normalizedMethods;
|
|
82
|
+
}
|
|
34
83
|
export function assertValidHookedBlockPosition(position) {
|
|
35
84
|
if (HOOKED_BLOCK_POSITION_IDS.includes(position)) {
|
|
36
85
|
return position;
|
|
@@ -121,6 +170,19 @@ export function assertValidHookAnchor(anchorBlockName) {
|
|
|
121
170
|
}
|
|
122
171
|
return trimmed;
|
|
123
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Validate and normalize the editor plugin shell slot.
|
|
175
|
+
*
|
|
176
|
+
* @param slot Optional shell slot. Defaults to `PluginSidebar`.
|
|
177
|
+
* @returns The canonical editor plugin slot id.
|
|
178
|
+
* @throws {Error} When the slot is not supported by the workspace scaffold.
|
|
179
|
+
*/
|
|
180
|
+
export function assertValidEditorPluginSlot(slot = "PluginSidebar") {
|
|
181
|
+
if (EDITOR_PLUGIN_SLOT_IDS.includes(slot)) {
|
|
182
|
+
return slot;
|
|
183
|
+
}
|
|
184
|
+
throw new Error(`Editor plugin slot must be one of: ${EDITOR_PLUGIN_SLOT_IDS.join(", ")}.`);
|
|
185
|
+
}
|
|
124
186
|
export function readWorkspaceBlockJson(projectDir, blockSlug) {
|
|
125
187
|
const blockJsonPath = path.join(projectDir, "src", "blocks", blockSlug, "block.json");
|
|
126
188
|
if (!fs.existsSync(blockJsonPath)) {
|
|
@@ -179,21 +241,58 @@ export function assertBindingSourceDoesNotExist(projectDir, bindingSourceSlug, i
|
|
|
179
241
|
throw new Error(`A binding source inventory entry already exists for ${bindingSourceSlug}. Choose a different name.`);
|
|
180
242
|
}
|
|
181
243
|
}
|
|
244
|
+
export function assertRestResourceDoesNotExist(projectDir, restResourceSlug, inventory) {
|
|
245
|
+
const restResourceDir = path.join(projectDir, "src", "rest", restResourceSlug);
|
|
246
|
+
const restResourcePhpPath = path.join(projectDir, "inc", "rest", `${restResourceSlug}.php`);
|
|
247
|
+
if (fs.existsSync(restResourceDir)) {
|
|
248
|
+
throw new Error(`A REST resource already exists at ${path.relative(projectDir, restResourceDir)}. Choose a different name.`);
|
|
249
|
+
}
|
|
250
|
+
if (fs.existsSync(restResourcePhpPath)) {
|
|
251
|
+
throw new Error(`A REST resource bootstrap already exists at ${path.relative(projectDir, restResourcePhpPath)}. Choose a different name.`);
|
|
252
|
+
}
|
|
253
|
+
if (inventory.restResources.some((entry) => entry.slug === restResourceSlug)) {
|
|
254
|
+
throw new Error(`A REST resource inventory entry already exists for ${restResourceSlug}. Choose a different name.`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Ensure an editor plugin scaffold does not already exist on disk or in the
|
|
259
|
+
* workspace inventory.
|
|
260
|
+
*
|
|
261
|
+
* @param projectDir Workspace root directory.
|
|
262
|
+
* @param editorPluginSlug Normalized editor plugin slug.
|
|
263
|
+
* @param inventory Parsed workspace inventory.
|
|
264
|
+
* @throws {Error} When the directory or inventory entry already exists.
|
|
265
|
+
*/
|
|
266
|
+
export function assertEditorPluginDoesNotExist(projectDir, editorPluginSlug, inventory) {
|
|
267
|
+
const editorPluginDir = path.join(projectDir, "src", "editor-plugins", editorPluginSlug);
|
|
268
|
+
if (fs.existsSync(editorPluginDir)) {
|
|
269
|
+
throw new Error(`An editor plugin already exists at ${path.relative(projectDir, editorPluginDir)}. Choose a different name.`);
|
|
270
|
+
}
|
|
271
|
+
if (inventory.editorPlugins.some((entry) => entry.slug === editorPluginSlug)) {
|
|
272
|
+
throw new Error(`An editor plugin inventory entry already exists for ${editorPluginSlug}. Choose a different name.`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
182
275
|
/**
|
|
183
276
|
* Returns help text for the canonical `wp-typia add` subcommands.
|
|
184
277
|
*/
|
|
185
278
|
export function formatAddHelpText() {
|
|
186
279
|
return `Usage:
|
|
187
|
-
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>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>]
|
|
188
|
-
wp-typia add variation <name> --block <block-slug>
|
|
189
|
-
wp-typia add pattern <name>
|
|
190
|
-
wp-typia add binding-source <name>
|
|
191
|
-
wp-typia add
|
|
280
|
+
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]
|
|
281
|
+
wp-typia add variation <name> --block <block-slug> [--dry-run]
|
|
282
|
+
wp-typia add pattern <name> [--dry-run]
|
|
283
|
+
wp-typia add binding-source <name> [--dry-run]
|
|
284
|
+
wp-typia add rest-resource <name> [--namespace <vendor/v1>] [--methods <list,read,create,update,delete>] [--dry-run]
|
|
285
|
+
wp-typia add hooked-block <block-slug> --anchor <anchor-block-name> --position <${HOOKED_BLOCK_POSITION_IDS.join("|")}> [--dry-run]
|
|
286
|
+
wp-typia add editor-plugin <name> [--slot <${EDITOR_PLUGIN_SLOT_IDS.join("|")}>] [--dry-run]
|
|
192
287
|
|
|
193
288
|
Notes:
|
|
194
|
-
\`wp-typia add\` runs only inside official ${WORKSPACE_TEMPLATE_PACKAGE} workspaces
|
|
289
|
+
\`wp-typia add\` runs only inside official ${WORKSPACE_TEMPLATE_PACKAGE} workspaces scaffolded via \`wp-typia create <project-dir> --template workspace\`.
|
|
290
|
+
Pass \`--dry-run\` to preview the workspace files that would change without writing them.
|
|
291
|
+
\`query-loop\` is a create-time scaffold family. Use \`wp-typia create <project-dir> --template query-loop\` instead of \`wp-typia add block\`.
|
|
195
292
|
\`add variation\` targets an existing block slug from \`scripts/block-config.ts\`.
|
|
196
293
|
\`add pattern\` scaffolds a namespaced PHP pattern shell under \`src/patterns/\`.
|
|
197
294
|
\`add binding-source\` scaffolds shared PHP and editor registration under \`src/bindings/\`.
|
|
198
|
-
\`add
|
|
295
|
+
\`add rest-resource\` scaffolds plugin-level TypeScript REST contracts under \`src/rest/\` and PHP route glue under \`inc/rest/\`.
|
|
296
|
+
\`add hooked-block\` patches an existing workspace block's \`block.json\` \`blockHooks\` metadata.
|
|
297
|
+
\`add editor-plugin\` scaffolds a document-level editor extension under \`src/editor-plugins/\`.`;
|
|
199
298
|
}
|
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
import { type RunAddBindingSourceCommandOptions, type RunAddPatternCommandOptions } from "./cli-add-shared.js";
|
|
1
|
+
import { type RunAddBindingSourceCommandOptions, type RunAddEditorPluginCommandOptions, type RunAddPatternCommandOptions } from "./cli-add-shared.js";
|
|
2
|
+
/**
|
|
3
|
+
* Add one document-level editor plugin scaffold to an official workspace project.
|
|
4
|
+
*
|
|
5
|
+
* @param options Command options for the editor-plugin scaffold workflow.
|
|
6
|
+
* @param options.cwd Working directory used to resolve the nearest official workspace.
|
|
7
|
+
* Defaults to `process.cwd()`.
|
|
8
|
+
* @param options.editorPluginName Human-entered editor-plugin name that will be
|
|
9
|
+
* normalized and validated before files are written.
|
|
10
|
+
* @param options.slot Optional editor plugin shell slot. Defaults to `PluginSidebar`.
|
|
11
|
+
* @returns A promise that resolves with the normalized `editorPluginSlug`, chosen
|
|
12
|
+
* `slot`, and owning `projectDir` after the scaffold files and inventory entry
|
|
13
|
+
* are written successfully.
|
|
14
|
+
* @throws {Error} When the command is run outside an official workspace, when the
|
|
15
|
+
* slug or slot is invalid, or when a conflicting file or inventory entry exists.
|
|
16
|
+
*/
|
|
17
|
+
export declare function runAddEditorPluginCommand({ cwd, editorPluginName, slot, }: RunAddEditorPluginCommandOptions): Promise<{
|
|
18
|
+
editorPluginSlug: string;
|
|
19
|
+
projectDir: string;
|
|
20
|
+
slot: string;
|
|
21
|
+
}>;
|
|
2
22
|
/**
|
|
3
23
|
* Add one PHP block pattern shell to an official workspace project.
|
|
4
24
|
*
|