@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.
Files changed (76) hide show
  1. package/dist/runtime/alternate-render-targets.d.ts +5 -0
  2. package/dist/runtime/alternate-render-targets.js +29 -0
  3. package/dist/runtime/block-generator-service-core.d.ts +2 -2
  4. package/dist/runtime/block-generator-service-core.js +13 -8
  5. package/dist/runtime/block-generator-service-spec.d.ts +10 -2
  6. package/dist/runtime/block-generator-service-spec.js +43 -1
  7. package/dist/runtime/built-in-block-artifacts.js +1 -0
  8. package/dist/runtime/built-in-block-code-templates/compound-child.d.ts +2 -2
  9. package/dist/runtime/built-in-block-code-templates/compound-child.js +35 -2
  10. package/dist/runtime/built-in-block-code-templates/compound-parent.d.ts +2 -2
  11. package/dist/runtime/built-in-block-code-templates/compound-parent.js +204 -27
  12. package/dist/runtime/built-in-block-code-templates/compound-persistence.d.ts +1 -1
  13. package/dist/runtime/built-in-block-code-templates/compound-persistence.js +11 -8
  14. package/dist/runtime/built-in-block-non-ts-artifacts.js +505 -2
  15. package/dist/runtime/cli-add-block.d.ts +6 -2
  16. package/dist/runtime/cli-add-block.js +71 -24
  17. package/dist/runtime/cli-add-shared.d.ts +58 -2
  18. package/dist/runtime/cli-add-shared.js +111 -12
  19. package/dist/runtime/cli-add-workspace-assets.d.ts +21 -1
  20. package/dist/runtime/cli-add-workspace-assets.js +417 -1
  21. package/dist/runtime/cli-add-workspace-rest.d.ts +14 -0
  22. package/dist/runtime/cli-add-workspace-rest.js +1060 -0
  23. package/dist/runtime/cli-add-workspace.d.ts +10 -1
  24. package/dist/runtime/cli-add-workspace.js +10 -1
  25. package/dist/runtime/cli-add.d.ts +3 -3
  26. package/dist/runtime/cli-add.js +2 -2
  27. package/dist/runtime/cli-core.d.ts +5 -1
  28. package/dist/runtime/cli-core.js +3 -1
  29. package/dist/runtime/cli-doctor-workspace.js +135 -1
  30. package/dist/runtime/cli-help.js +12 -7
  31. package/dist/runtime/cli-scaffold.d.ts +12 -2
  32. package/dist/runtime/cli-scaffold.js +222 -46
  33. package/dist/runtime/cli-templates.d.ts +4 -4
  34. package/dist/runtime/cli-templates.js +104 -39
  35. package/dist/runtime/cli-validation.d.ts +66 -0
  36. package/dist/runtime/cli-validation.js +92 -0
  37. package/dist/runtime/compound-inner-blocks.d.ts +78 -0
  38. package/dist/runtime/compound-inner-blocks.js +88 -0
  39. package/dist/runtime/index.d.ts +6 -3
  40. package/dist/runtime/index.js +4 -2
  41. package/dist/runtime/local-dev-presets.js +7 -2
  42. package/dist/runtime/migration-command-surface.js +2 -0
  43. package/dist/runtime/package-versions.d.ts +1 -0
  44. package/dist/runtime/package-versions.js +12 -0
  45. package/dist/runtime/rest-resource-artifacts.d.ts +35 -0
  46. package/dist/runtime/rest-resource-artifacts.js +158 -0
  47. package/dist/runtime/scaffold-answer-resolution.js +78 -8
  48. package/dist/runtime/scaffold-apply-utils.d.ts +4 -3
  49. package/dist/runtime/scaffold-apply-utils.js +34 -17
  50. package/dist/runtime/scaffold-bootstrap.d.ts +15 -0
  51. package/dist/runtime/scaffold-bootstrap.js +29 -7
  52. package/dist/runtime/scaffold-documents.js +24 -3
  53. package/dist/runtime/scaffold-identifiers.d.ts +17 -0
  54. package/dist/runtime/scaffold-identifiers.js +22 -0
  55. package/dist/runtime/scaffold-onboarding.js +25 -13
  56. package/dist/runtime/scaffold-package-manager-files.js +6 -1
  57. package/dist/runtime/scaffold-template-variables.js +22 -0
  58. package/dist/runtime/scaffold.d.ts +22 -1
  59. package/dist/runtime/scaffold.js +56 -11
  60. package/dist/runtime/template-render.d.ts +5 -2
  61. package/dist/runtime/template-render.js +9 -3
  62. package/dist/runtime/template-source-contracts.d.ts +11 -0
  63. package/dist/runtime/template-source-external.d.ts +1 -1
  64. package/dist/runtime/template-source-external.js +45 -13
  65. package/dist/runtime/template-source-normalization.d.ts +1 -1
  66. package/dist/runtime/template-source-normalization.js +5 -1
  67. package/dist/runtime/template-source-remote.d.ts +5 -0
  68. package/dist/runtime/template-source-remote.js +33 -0
  69. package/dist/runtime/template-source.js +35 -4
  70. package/dist/runtime/workspace-inventory.d.ts +43 -1
  71. package/dist/runtime/workspace-inventory.js +132 -1
  72. package/dist/runtime/workspace-project.d.ts +1 -1
  73. package/dist/runtime/workspace-project.js +3 -3
  74. package/package.json +9 -4
  75. package/templates/_shared/compound/core/scripts/add-compound-child.ts.mustache +728 -49
  76. package/templates/query-loop/src/validator-toolkit.ts.mustache +0 -1
@@ -1,6 +1,15 @@
1
1
  import type { HookedBlockPositionId } from "./hooked-blocks.js";
2
2
  import { type RunAddHookedBlockCommandOptions, type RunAddVariationCommandOptions } from "./cli-add-shared.js";
3
- export { runAddBindingSourceCommand, runAddPatternCommand, } from "./cli-add-workspace-assets.js";
3
+ /**
4
+ * Re-export focused workspace asset scaffold commands from the companion
5
+ * `cli-add-workspace-assets` module.
6
+ */
7
+ export { runAddEditorPluginCommand, runAddBindingSourceCommand, runAddPatternCommand, } from "./cli-add-workspace-assets.js";
8
+ /**
9
+ * Re-export the plugin-level REST resource scaffold workflow from the focused
10
+ * rest-resource runtime helper module.
11
+ */
12
+ export { runAddRestResourceCommand } from "./cli-add-workspace-rest.js";
4
13
  /**
5
14
  * Add one variation entry to an existing workspace block.
6
15
  *
@@ -119,7 +119,16 @@ async function writeVariationRegistry(projectDir, blockSlug, variationSlug) {
119
119
  const nextVariationSlugs = Array.from(new Set([...existingVariationSlugs, variationSlug])).sort();
120
120
  await fsp.writeFile(variationsIndexPath, buildVariationIndexSource(nextVariationSlugs), "utf8");
121
121
  }
122
- export { runAddBindingSourceCommand, runAddPatternCommand, } from "./cli-add-workspace-assets.js";
122
+ /**
123
+ * Re-export focused workspace asset scaffold commands from the companion
124
+ * `cli-add-workspace-assets` module.
125
+ */
126
+ export { runAddEditorPluginCommand, runAddBindingSourceCommand, runAddPatternCommand, } from "./cli-add-workspace-assets.js";
127
+ /**
128
+ * Re-export the plugin-level REST resource scaffold workflow from the focused
129
+ * rest-resource runtime helper module.
130
+ */
131
+ export { runAddRestResourceCommand } from "./cli-add-workspace-rest.js";
123
132
  /**
124
133
  * Add one variation entry to an existing workspace block.
125
134
  *
@@ -7,8 +7,8 @@
7
7
  * - `cli-add-block` for built-in block scaffolding
8
8
  * - `cli-add-workspace` for workspace mutation commands
9
9
  */
10
- export { ADD_BLOCK_TEMPLATE_IDS, ADD_KIND_IDS, formatAddHelpText, } from "./cli-add-shared.js";
11
- export type { AddBlockTemplateId, AddKindId, } from "./cli-add-shared.js";
10
+ export { ADD_BLOCK_TEMPLATE_IDS, ADD_KIND_IDS, EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, } from "./cli-add-shared.js";
11
+ export type { AddBlockTemplateId, AddKindId, EditorPluginSlotId, } from "./cli-add-shared.js";
12
12
  export { runAddBlockCommand, seedWorkspaceMigrationProject, } from "./cli-add-block.js";
13
- export { runAddBindingSourceCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddVariationCommand, } from "./cli-add-workspace.js";
13
+ export { runAddBindingSourceCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, } from "./cli-add-workspace.js";
14
14
  export { getWorkspaceBlockSelectOptions } from "./workspace-inventory.js";
@@ -7,7 +7,7 @@
7
7
  * - `cli-add-block` for built-in block scaffolding
8
8
  * - `cli-add-workspace` for workspace mutation commands
9
9
  */
10
- export { ADD_BLOCK_TEMPLATE_IDS, ADD_KIND_IDS, formatAddHelpText, } from "./cli-add-shared.js";
10
+ export { ADD_BLOCK_TEMPLATE_IDS, ADD_KIND_IDS, EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, } from "./cli-add-shared.js";
11
11
  export { runAddBlockCommand, seedWorkspaceMigrationProject, } from "./cli-add-block.js";
12
- export { runAddBindingSourceCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddVariationCommand, } from "./cli-add-workspace.js";
12
+ export { runAddBindingSourceCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, } from "./cli-add-workspace.js";
13
13
  export { getWorkspaceBlockSelectOptions } from "./workspace-inventory.js";
@@ -11,6 +11,7 @@
11
11
  * and `HOOKED_BLOCK_POSITION_IDS`,
12
12
  * `getWorkspaceBlockSelectOptions`, and `seedWorkspaceMigrationProject` for
13
13
  * explicit `wp-typia add` flows,
14
+ * `runAddRestResourceCommand` for plugin-level REST resource scaffolds,
14
15
  * `getDoctorChecks`, `runDoctor`, and `DoctorCheck` for diagnostics,
15
16
  * `createCliCommandError` and `formatCliDiagnosticError` for shared
16
17
  * non-interactive failure rendering,
@@ -25,8 +26,11 @@
25
26
  export { getDoctorChecks, runDoctor, type DoctorCheck } from "./cli-doctor.js";
26
27
  export { createCliCommandError, CliDiagnosticError, formatCliDiagnosticError, formatDoctorCheckLine, formatDoctorSummaryLine, getDoctorFailureDetailLines, getFailingDoctorChecks, isCliDiagnosticError, } from "./cli-diagnostics.js";
27
28
  export type { CliDiagnosticMessage } from "./cli-diagnostics.js";
28
- export { formatAddHelpText, getWorkspaceBlockSelectOptions, runAddBindingSourceCommand, runAddBlockCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddVariationCommand, seedWorkspaceMigrationProject, } from "./cli-add.js";
29
+ export { EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, getWorkspaceBlockSelectOptions, runAddBindingSourceCommand, runAddBlockCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, seedWorkspaceMigrationProject, } from "./cli-add.js";
30
+ export { COMPOUND_INNER_BLOCKS_PRESET_IDS, getCompoundInnerBlocksPresetDefinition, } from "./compound-inner-blocks.js";
31
+ export type { CompoundInnerBlocksPresetId } from "./compound-inner-blocks.js";
29
32
  export { HOOKED_BLOCK_POSITION_IDS } from "./hooked-blocks.js";
33
+ export type { EditorPluginSlotId } from "./cli-add.js";
30
34
  export type { HookedBlockPositionId } from "./hooked-blocks.js";
31
35
  export { formatHelpText } from "./cli-help.js";
32
36
  export { getNextSteps, getOptionalOnboarding, runScaffoldFlow, } from "./cli-scaffold.js";
@@ -11,6 +11,7 @@
11
11
  * and `HOOKED_BLOCK_POSITION_IDS`,
12
12
  * `getWorkspaceBlockSelectOptions`, and `seedWorkspaceMigrationProject` for
13
13
  * explicit `wp-typia add` flows,
14
+ * `runAddRestResourceCommand` for plugin-level REST resource scaffolds,
14
15
  * `getDoctorChecks`, `runDoctor`, and `DoctorCheck` for diagnostics,
15
16
  * `createCliCommandError` and `formatCliDiagnosticError` for shared
16
17
  * non-interactive failure rendering,
@@ -24,7 +25,8 @@
24
25
  */
25
26
  export { getDoctorChecks, runDoctor } from "./cli-doctor.js";
26
27
  export { createCliCommandError, CliDiagnosticError, formatCliDiagnosticError, formatDoctorCheckLine, formatDoctorSummaryLine, getDoctorFailureDetailLines, getFailingDoctorChecks, isCliDiagnosticError, } from "./cli-diagnostics.js";
27
- export { formatAddHelpText, getWorkspaceBlockSelectOptions, runAddBindingSourceCommand, runAddBlockCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddVariationCommand, seedWorkspaceMigrationProject, } from "./cli-add.js";
28
+ export { EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, getWorkspaceBlockSelectOptions, runAddBindingSourceCommand, runAddBlockCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, seedWorkspaceMigrationProject, } from "./cli-add.js";
29
+ export { COMPOUND_INNER_BLOCKS_PRESET_IDS, getCompoundInnerBlocksPresetDefinition, } from "./compound-inner-blocks.js";
28
30
  export { HOOKED_BLOCK_POSITION_IDS } from "./hooked-blocks.js";
29
31
  export { formatHelpText } from "./cli-help.js";
30
32
  export { getNextSteps, getOptionalOnboarding, runScaffoldFlow, } from "./cli-scaffold.js";
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { parseScaffoldBlockMetadata } from "@wp-typia/block-runtime/blocks";
4
+ import { EDITOR_PLUGIN_SLOT_IDS, REST_RESOURCE_METHOD_IDS, REST_RESOURCE_NAMESPACE_PATTERN, } from "./cli-add-shared.js";
4
5
  import { HOOKED_BLOCK_ANCHOR_PATTERN, HOOKED_BLOCK_POSITION_SET, } from "./hooked-blocks.js";
5
6
  import { readWorkspaceInventory } from "./workspace-inventory.js";
6
7
  import { getInvalidWorkspaceProjectReason, parseWorkspacePackageJson, WORKSPACE_TEMPLATE_PACKAGE, tryResolveWorkspaceProject, } from "./workspace-project.js";
@@ -9,6 +10,10 @@ const WORKSPACE_COLLECTION_IMPORT_PATTERN = /^\s*import\s+["']\.\.\/\.\.\/collec
9
10
  const WORKSPACE_BINDING_SERVER_GLOB = "/src/bindings/*/server.php";
10
11
  const WORKSPACE_BINDING_EDITOR_SCRIPT = "build/bindings/index.js";
11
12
  const WORKSPACE_BINDING_EDITOR_ASSET = "build/bindings/index.asset.php";
13
+ const WORKSPACE_REST_RESOURCE_GLOB = "/inc/rest/*.php";
14
+ const WORKSPACE_EDITOR_PLUGIN_EDITOR_SCRIPT = "build/editor-plugins/index.js";
15
+ const WORKSPACE_EDITOR_PLUGIN_EDITOR_ASSET = "build/editor-plugins/index.asset.php";
16
+ const WORKSPACE_EDITOR_PLUGIN_EDITOR_STYLE = "build/editor-plugins/style-index.css";
12
17
  const WORKSPACE_GENERATED_BLOCK_ARTIFACTS = [
13
18
  "block.json",
14
19
  "typia.manifest.json",
@@ -22,6 +27,9 @@ function createDoctorCheck(label, status, detail) {
22
27
  function createDoctorScopeCheck(status, detail) {
23
28
  return createDoctorCheck("Doctor scope", status, detail);
24
29
  }
30
+ function escapeRegex(value) {
31
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
32
+ }
25
33
  function getWorkspaceBootstrapRelativePath(packageName) {
26
34
  const packageBaseName = packageName.split("/").pop() ?? packageName;
27
35
  return `${packageBaseName}.php`;
@@ -184,6 +192,117 @@ function checkWorkspaceBindingSourcesIndex(projectDir, bindingSources) {
184
192
  ? "Binding source editor registrations are aggregated"
185
193
  : `Missing editor imports for: ${missingImports.map((entry) => entry.slug).join(", ")}`);
186
194
  }
195
+ function getWorkspaceRestResourceRequiredFiles(restResource) {
196
+ const schemaNames = new Set();
197
+ if (restResource.methods.includes("list")) {
198
+ schemaNames.add("list-query");
199
+ schemaNames.add("list-response");
200
+ }
201
+ if (restResource.methods.includes("read")) {
202
+ schemaNames.add("read-query");
203
+ schemaNames.add("read-response");
204
+ }
205
+ if (restResource.methods.includes("create")) {
206
+ schemaNames.add("create-request");
207
+ schemaNames.add("create-response");
208
+ }
209
+ if (restResource.methods.includes("update")) {
210
+ schemaNames.add("update-query");
211
+ schemaNames.add("update-request");
212
+ schemaNames.add("update-response");
213
+ }
214
+ if (restResource.methods.includes("delete")) {
215
+ schemaNames.add("delete-query");
216
+ schemaNames.add("delete-response");
217
+ }
218
+ return Array.from(new Set([
219
+ restResource.apiFile,
220
+ ...Array.from(schemaNames, (schemaName) => path.join(path.dirname(restResource.typesFile), "api-schemas", `${schemaName}.schema.json`)),
221
+ restResource.clientFile,
222
+ restResource.dataFile,
223
+ restResource.openApiFile,
224
+ restResource.phpFile,
225
+ restResource.typesFile,
226
+ restResource.validatorsFile,
227
+ ]));
228
+ }
229
+ function checkWorkspaceRestResourceConfig(restResource) {
230
+ const hasNamespace = REST_RESOURCE_NAMESPACE_PATTERN.test(restResource.namespace);
231
+ const hasMethods = restResource.methods.length > 0 &&
232
+ restResource.methods.every((method) => REST_RESOURCE_METHOD_IDS.includes(method));
233
+ return createDoctorCheck(`REST resource config ${restResource.slug}`, hasNamespace && hasMethods ? "pass" : "fail", hasNamespace && hasMethods
234
+ ? `REST resource namespace ${restResource.namespace} with methods ${restResource.methods.join(", ")}`
235
+ : "REST resource namespace or methods are invalid");
236
+ }
237
+ function checkWorkspaceRestResourceBootstrap(projectDir, packageName, phpPrefix) {
238
+ const packageBaseName = packageName.split("/").pop() ?? packageName;
239
+ const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
240
+ if (!fs.existsSync(bootstrapPath)) {
241
+ return createDoctorCheck("REST resource bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
242
+ }
243
+ const source = fs.readFileSync(bootstrapPath, "utf8");
244
+ const registerFunctionName = `${phpPrefix}_register_rest_resources`;
245
+ const registerHook = `add_action( 'init', '${registerFunctionName}', 20 );`;
246
+ const hasServerGlob = source.includes(WORKSPACE_REST_RESOURCE_GLOB);
247
+ const hasRegisterHook = source.includes(registerHook);
248
+ return createDoctorCheck("REST resource bootstrap", hasServerGlob && hasRegisterHook ? "pass" : "fail", hasServerGlob && hasRegisterHook
249
+ ? "REST resource PHP loader hook is present"
250
+ : "Missing REST resource PHP require glob or init hook");
251
+ }
252
+ function getWorkspaceEditorPluginRequiredFiles(editorPlugin) {
253
+ const editorPluginDir = path.join("src", "editor-plugins", editorPlugin.slug);
254
+ return Array.from(new Set([
255
+ editorPlugin.file,
256
+ path.join(editorPluginDir, "Sidebar.tsx"),
257
+ path.join(editorPluginDir, "data.ts"),
258
+ path.join(editorPluginDir, "types.ts"),
259
+ path.join(editorPluginDir, "style.scss"),
260
+ ]));
261
+ }
262
+ function checkWorkspaceEditorPluginConfig(editorPlugin) {
263
+ const validSlots = new Set(EDITOR_PLUGIN_SLOT_IDS);
264
+ const isValidSlot = validSlots.has(editorPlugin.slot);
265
+ return createDoctorCheck(`Editor plugin config ${editorPlugin.slug}`, isValidSlot ? "pass" : "fail", isValidSlot
266
+ ? `Editor plugin slot ${editorPlugin.slot} is supported`
267
+ : `Unsupported editor plugin slot "${editorPlugin.slot}". Expected one of: ${EDITOR_PLUGIN_SLOT_IDS.join(", ")}`);
268
+ }
269
+ function checkWorkspaceEditorPluginBootstrap(projectDir, packageName, phpPrefix) {
270
+ const packageBaseName = packageName.split("/").pop() ?? packageName;
271
+ const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
272
+ if (!fs.existsSync(bootstrapPath)) {
273
+ return createDoctorCheck("Editor plugin bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
274
+ }
275
+ const source = fs.readFileSync(bootstrapPath, "utf8");
276
+ const enqueueFunctionName = `${phpPrefix}_enqueue_editor_plugins_editor`;
277
+ const enqueueHook = `add_action( 'enqueue_block_editor_assets', '${enqueueFunctionName}' );`;
278
+ const hasEditorEnqueueHook = source.includes(enqueueHook);
279
+ const hasEditorScript = source.includes(WORKSPACE_EDITOR_PLUGIN_EDITOR_SCRIPT);
280
+ const hasEditorAsset = source.includes(WORKSPACE_EDITOR_PLUGIN_EDITOR_ASSET);
281
+ const hasEditorStyle = source.includes(WORKSPACE_EDITOR_PLUGIN_EDITOR_STYLE);
282
+ return createDoctorCheck("Editor plugin bootstrap", hasEditorEnqueueHook && hasEditorScript && hasEditorAsset && hasEditorStyle ? "pass" : "fail", hasEditorEnqueueHook && hasEditorScript && hasEditorAsset && hasEditorStyle
283
+ ? "Editor plugin enqueue hook is present"
284
+ : "Missing editor plugin enqueue hook or build/editor-plugins script/style asset references");
285
+ }
286
+ function checkWorkspaceEditorPluginIndex(projectDir, editorPlugins) {
287
+ const indexRelativePath = [
288
+ path.join("src", "editor-plugins", "index.ts"),
289
+ path.join("src", "editor-plugins", "index.js"),
290
+ ].find((relativePath) => fs.existsSync(path.join(projectDir, relativePath)));
291
+ if (!indexRelativePath) {
292
+ return createDoctorCheck("Editor plugins index", "fail", "Missing src/editor-plugins/index.ts or src/editor-plugins/index.js");
293
+ }
294
+ const indexPath = path.join(projectDir, indexRelativePath);
295
+ const source = fs.readFileSync(indexPath, "utf8");
296
+ const missingImports = editorPlugins.filter((editorPlugin) => {
297
+ const importPattern = new RegExp(`['"\`]\\./${escapeRegex(editorPlugin.slug)}(?:/[^'"\`]*)?['"\`]`, "u");
298
+ return !importPattern.test(source);
299
+ });
300
+ return createDoctorCheck("Editor plugins index", missingImports.length === 0 ? "pass" : "fail", missingImports.length === 0
301
+ ? "Editor plugin registrations are aggregated"
302
+ : `Missing editor plugin imports for: ${missingImports
303
+ .map((entry) => entry.slug)
304
+ .join(", ")}`);
305
+ }
187
306
  function checkVariationEntrypoint(projectDir, blockSlug) {
188
307
  const entryPath = path.join(projectDir, "src", "blocks", blockSlug, "index.tsx");
189
308
  if (!fs.existsSync(entryPath)) {
@@ -258,7 +377,7 @@ export function getWorkspaceDoctorChecks(cwd) {
258
377
  checks.push(checkWorkspacePackageMetadata(workspace, workspacePackageJson));
259
378
  try {
260
379
  const inventory = readWorkspaceInventory(workspace.projectDir);
261
- checks.push(createDoctorCheck("Workspace inventory", "pass", `${inventory.blocks.length} block(s), ${inventory.variations.length} variation(s), ${inventory.patterns.length} pattern(s), ${inventory.bindingSources.length} binding source(s)`));
380
+ checks.push(createDoctorCheck("Workspace inventory", "pass", `${inventory.blocks.length} block(s), ${inventory.variations.length} variation(s), ${inventory.patterns.length} pattern(s), ${inventory.bindingSources.length} binding source(s), ${inventory.restResources.length} REST resource(s), ${inventory.editorPlugins.length} editor plugin(s)`));
262
381
  for (const block of inventory.blocks) {
263
382
  checks.push(checkExistingFiles(workspace.projectDir, `Block ${block.slug}`, getWorkspaceBlockRequiredFiles(block)));
264
383
  checks.push(checkWorkspaceBlockMetadata(workspace.projectDir, workspace, block));
@@ -296,6 +415,21 @@ export function getWorkspaceDoctorChecks(cwd) {
296
415
  bindingSource.editorFile,
297
416
  ]));
298
417
  }
418
+ if (inventory.restResources.length > 0) {
419
+ checks.push(checkWorkspaceRestResourceBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
420
+ }
421
+ for (const restResource of inventory.restResources) {
422
+ checks.push(checkWorkspaceRestResourceConfig(restResource));
423
+ checks.push(checkExistingFiles(workspace.projectDir, `REST resource ${restResource.slug}`, getWorkspaceRestResourceRequiredFiles(restResource)));
424
+ }
425
+ if (inventory.editorPlugins.length > 0) {
426
+ checks.push(checkWorkspaceEditorPluginBootstrap(workspace.projectDir, workspace.packageName, workspace.workspace.phpPrefix));
427
+ checks.push(checkWorkspaceEditorPluginIndex(workspace.projectDir, inventory.editorPlugins));
428
+ }
429
+ for (const editorPlugin of inventory.editorPlugins) {
430
+ checks.push(checkExistingFiles(workspace.projectDir, `Editor plugin ${editorPlugin.slug}`, getWorkspaceEditorPluginRequiredFiles(editorPlugin)));
431
+ checks.push(checkWorkspaceEditorPluginConfig(editorPlugin));
432
+ }
299
433
  const migrationWorkspaceCheck = checkMigrationWorkspaceHint(workspace, workspacePackageJson);
300
434
  if (migrationWorkspaceCheck) {
301
435
  checks.push(migrationWorkspaceCheck);
@@ -10,17 +10,19 @@ import { TEMPLATE_IDS } from "./template-registry.js";
10
10
  */
11
11
  export function formatHelpText() {
12
12
  return `Usage:
13
- wp-typia create <project-dir> [--template <basic|interactivity>] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--no-install] [--package-manager <id>]
14
- wp-typia create <project-dir> [--template query-loop] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--query-post-type <post-type>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--no-install] [--package-manager <id>]
15
- wp-typia create <project-dir> [--template <./path|github:owner/repo/path[#ref]>] [--variant <name>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-wp-env] [--with-test-preset] [--yes] [--no-install] [--package-manager <id>]
16
- wp-typia create <project-dir> [--template <npm-package>] [--variant <name>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-wp-env] [--with-test-preset] [--yes] [--no-install] [--package-manager <id>]
17
- wp-typia create <project-dir> [--template persistence] [--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>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--no-install] [--package-manager <id>]
18
- wp-typia create <project-dir> [--template compound] [--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>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--no-install] [--package-manager <id>]
13
+ wp-typia create <project-dir> [--template <basic|interactivity>] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
14
+ wp-typia create <project-dir> [--template query-loop] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--query-post-type <post-type>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
15
+ wp-typia create <project-dir> [--template <./path|github:owner/repo/path[#ref]>] [--variant <name>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
16
+ wp-typia create <project-dir> [--template <npm-package>] [--variant <name>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
17
+ wp-typia create <project-dir> [--template persistence] [--external-layer-source <./path|github:owner/repo/path[#ref]|npm-package>] [--external-layer-id <layer-id>] [--alternate-render-targets <email,mjml,plain-text>] [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
18
+ wp-typia create <project-dir> [--template compound] [--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>] [--namespace <value>] [--text-domain <value>] [--php-prefix <value>] [--with-migration-ui] [--with-wp-env] [--with-test-preset] [--yes] [--dry-run] [--no-install] [--package-manager <id>]
19
19
  wp-typia <project-dir> [create flags...]
20
- wp-typia add block <name> --template <basic|interactivity|persistence|compound> [--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>]
20
+ wp-typia add block <name> --template <basic|interactivity|persistence|compound> [--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>]
21
21
  wp-typia add variation <name> --block <block-slug>
22
22
  wp-typia add pattern <name>
23
23
  wp-typia add binding-source <name>
24
+ wp-typia add rest-resource <name> [--namespace <vendor/v1>] [--methods <method[,method...]>]
25
+ wp-typia add editor-plugin <name> [--slot <PluginSidebar>]
24
26
  wp-typia add hooked-block <block-slug> --anchor <anchor-block-name> --position <before|after|firstChild|lastChild>
25
27
  wp-typia migrate <init|snapshot|diff|scaffold|verify|doctor|fixtures|fuzz> [...]
26
28
  wp-typia templates list
@@ -36,9 +38,12 @@ Notes:
36
38
  \`wp-typia create\` is the canonical scaffold command.
37
39
  \`wp-typia <project-dir>\` remains a backward-compatible alias to \`create\` when \`<project-dir>\` is the only positional argument.
38
40
  Use \`--template workspace\` as shorthand for \`@wp-typia/create-workspace-template\`, the official empty workspace scaffold behind \`wp-typia add ...\`.
41
+ \`query-loop\` is create-only. Use \`wp-typia create <project-dir> --template query-loop\`; \`wp-typia add block\` accepts only basic, interactivity, persistence, and compound families.
39
42
  \`add variation\` uses an existing workspace block from \`scripts/block-config.ts\`.
40
43
  \`add pattern\` scaffolds a namespaced PHP pattern shell under \`src/patterns/\`.
41
44
  \`add binding-source\` scaffolds shared PHP and editor registration under \`src/bindings/\`.
45
+ \`add rest-resource\` scaffolds plugin-level TypeScript REST contracts under \`src/rest/\` and PHP route glue under \`inc/rest/\`.
46
+ \`add editor-plugin\` scaffolds a document-level editor extension under \`src/editor-plugins/\`.
42
47
  \`add hooked-block\` patches an existing workspace block's \`block.json\` \`blockHooks\` metadata.
43
48
  \`wp-typia doctor\` always checks environment readiness and reports when it only ran environment-level diagnostics; official workspace roots also get inventory and source-tree drift checks.
44
49
  \`wp-typia migrate doctor --all\` checks migration target alignment, snapshots, fixtures, and generated migration artifacts.
@@ -1,5 +1,5 @@
1
1
  import { collectScaffoldAnswers, scaffoldProject } from "./scaffold.js";
2
- import type { DataStorageMode, PersistencePolicy } from "./scaffold.js";
2
+ import type { DataStorageMode, PersistencePolicy, ScaffoldProgressEvent } from "./scaffold.js";
3
3
  import type { PackageManagerId } from "./package-managers.js";
4
4
  import { type ExternalLayerSelectionOption } from "./external-layer-selection.js";
5
5
  import type { TemplateDefinition } from "./template-registry.js";
@@ -20,16 +20,24 @@ interface OptionalOnboardingGuidance {
20
20
  note: string;
21
21
  steps: string[];
22
22
  }
23
+ export interface ScaffoldDryRunPlan {
24
+ dependencyInstall: "skipped-by-flag" | "would-install";
25
+ files: string[];
26
+ }
23
27
  interface RunScaffoldFlowOptions {
24
28
  allowExistingDir?: boolean;
29
+ alternateRenderTargets?: string;
25
30
  cwd?: string;
26
31
  dataStorageMode?: string;
32
+ dryRun?: boolean;
27
33
  externalLayerId?: string;
28
34
  externalLayerSource?: string;
29
35
  installDependencies?: Parameters<typeof scaffoldProject>[0]["installDependencies"];
36
+ innerBlocksPreset?: string;
30
37
  isInteractive?: boolean;
31
38
  namespace?: string;
32
39
  noInstall?: boolean;
40
+ onProgress?: ((event: ScaffoldProgressEvent) => void | Promise<void>) | undefined;
33
41
  packageManager?: string;
34
42
  phpPrefix?: string;
35
43
  projectInput: string;
@@ -74,8 +82,10 @@ export declare function getOptionalOnboarding({ availableScripts, packageManager
74
82
  * project.
75
83
  * @returns The scaffold result together with next-step guidance.
76
84
  */
77
- export declare function runScaffoldFlow({ projectInput, cwd, templateId, dataStorageMode, externalLayerId, externalLayerSource, persistencePolicy, packageManager, namespace, textDomain, phpPrefix, queryPostType, yes, noInstall, isInteractive, allowExistingDir, selectTemplate, selectDataStorage, selectExternalLayerId, selectPersistencePolicy, selectPackageManager, promptText, installDependencies, variant, selectWithTestPreset, selectWithWpEnv, selectWithMigrationUi, withMigrationUi, withTestPreset, withWpEnv, }: RunScaffoldFlowOptions): Promise<{
85
+ export declare function runScaffoldFlow({ projectInput, cwd, templateId, alternateRenderTargets, dataStorageMode, dryRun, externalLayerId, externalLayerSource, innerBlocksPreset, persistencePolicy, packageManager, namespace, textDomain, phpPrefix, queryPostType, yes, noInstall, onProgress, isInteractive, allowExistingDir, selectTemplate, selectDataStorage, selectExternalLayerId, selectPersistencePolicy, selectPackageManager, promptText, installDependencies, variant, selectWithTestPreset, selectWithWpEnv, selectWithMigrationUi, withMigrationUi, withTestPreset, withWpEnv, }: RunScaffoldFlowOptions): Promise<{
86
+ dryRun: boolean;
78
87
  optionalOnboarding: OptionalOnboardingGuidance;
88
+ plan: ScaffoldDryRunPlan | undefined;
79
89
  projectDir: string;
80
90
  projectInput: string;
81
91
  packageManager: PackageManagerId;