@wp-typia/project-tools 0.16.13 → 0.17.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 (38) hide show
  1. package/README.md +0 -1
  2. package/dist/runtime/block-generator-service-spec.d.ts +6 -0
  3. package/dist/runtime/block-generator-service-spec.js +27 -0
  4. package/dist/runtime/built-in-block-code-artifacts.js +14 -1
  5. package/dist/runtime/built-in-block-code-templates/query-loop.d.ts +1 -0
  6. package/dist/runtime/built-in-block-code-templates/query-loop.js +70 -0
  7. package/dist/runtime/built-in-block-code-templates.d.ts +1 -0
  8. package/dist/runtime/built-in-block-code-templates.js +1 -0
  9. package/dist/runtime/built-in-block-non-ts-artifacts.js +2 -0
  10. package/dist/runtime/cli-help.js +1 -0
  11. package/dist/runtime/cli-prompt.js +78 -19
  12. package/dist/runtime/cli-scaffold.d.ts +2 -1
  13. package/dist/runtime/cli-scaffold.js +2 -1
  14. package/dist/runtime/local-dev-presets.js +21 -11
  15. package/dist/runtime/scaffold-answer-resolution.d.ts +37 -0
  16. package/dist/runtime/scaffold-answer-resolution.js +163 -0
  17. package/dist/runtime/scaffold-apply-utils.d.ts +1 -16
  18. package/dist/runtime/scaffold-apply-utils.js +4 -128
  19. package/dist/runtime/scaffold-documents.d.ts +34 -0
  20. package/dist/runtime/scaffold-documents.js +143 -0
  21. package/dist/runtime/scaffold-onboarding.js +12 -0
  22. package/dist/runtime/scaffold-template-variables.d.ts +9 -0
  23. package/dist/runtime/scaffold-template-variables.js +117 -0
  24. package/dist/runtime/scaffold.d.ts +19 -9
  25. package/dist/runtime/scaffold.js +6 -202
  26. package/dist/runtime/template-defaults.d.ts +7 -0
  27. package/dist/runtime/template-defaults.js +4 -0
  28. package/dist/runtime/template-registry.d.ts +1 -1
  29. package/dist/runtime/template-registry.js +14 -1
  30. package/package.json +3 -3
  31. package/templates/query-loop/inc/query-runtime.php.mustache +85 -0
  32. package/templates/query-loop/package.json.mustache +32 -0
  33. package/templates/query-loop/src/patterns/grid.php.mustache +49 -0
  34. package/templates/query-loop/src/patterns/list.php.mustache +48 -0
  35. package/templates/query-loop/src/query-extension.ts.mustache +41 -0
  36. package/templates/query-loop/src/validator-toolkit.ts.mustache +1 -0
  37. package/templates/query-loop/webpack.config.js.mustache +16 -0
  38. package/templates/query-loop/{{slugKebabCase}}.php.mustache +84 -0
package/README.md CHANGED
@@ -12,7 +12,6 @@ Package roles:
12
12
  templates no longer ship structural, TS/TSX, style, or block-local `render.php`
13
13
  Mustache files.
14
14
  - `@wp-typia/block-runtime/*` owns generated-project runtime helpers.
15
- - `@wp-typia/create` is the deprecated legacy package shell.
16
15
 
17
16
  Supported public imports:
18
17
 
@@ -27,6 +27,11 @@ export interface BlockSpec {
27
27
  project: {
28
28
  author: string;
29
29
  };
30
+ queryLoop: {
31
+ allowedControls: readonly string[];
32
+ enabled: boolean;
33
+ postType: string;
34
+ };
30
35
  runtime: {
31
36
  withMigrationUi: boolean;
32
37
  withTestPreset: boolean;
@@ -66,6 +71,7 @@ export interface PlanBlockInput {
66
71
  templateId: BuiltInTemplateId;
67
72
  variant?: string;
68
73
  withMigrationUi?: boolean;
74
+ queryPostType?: string;
69
75
  withTestPreset?: boolean;
70
76
  withWpEnv?: boolean;
71
77
  }
@@ -24,6 +24,15 @@ function getBuiltInPersistenceSpec({ templateId, dataStorageMode, persistencePol
24
24
  enabled: false,
25
25
  };
26
26
  }
27
+ const DEFAULT_QUERY_LOOP_ALLOWED_CONTROLS = [
28
+ "inherit",
29
+ "postType",
30
+ "order",
31
+ "sticky",
32
+ "taxQuery",
33
+ "author",
34
+ "search",
35
+ ];
27
36
  export function createBuiltInBlockSpec({ answers, dataStorageMode, persistencePolicy, templateId, withMigrationUi = false, withTestPreset = false, withWpEnv = false, }) {
28
37
  const template = getTemplateById(templateId);
29
38
  const metadataDefaults = getBuiltInTemplateMetadataDefaults(templateId);
@@ -52,6 +61,17 @@ export function createBuiltInBlockSpec({ answers, dataStorageMode, persistencePo
52
61
  project: {
53
62
  author: answers.author.trim(),
54
63
  },
64
+ queryLoop: templateId === "query-loop"
65
+ ? {
66
+ allowedControls: DEFAULT_QUERY_LOOP_ALLOWED_CONTROLS,
67
+ enabled: true,
68
+ postType: (answers.queryPostType ?? "post").trim() || "post",
69
+ }
70
+ : {
71
+ allowedControls: [],
72
+ enabled: false,
73
+ postType: "post",
74
+ },
55
75
  runtime: {
56
76
  withMigrationUi,
57
77
  withTestPreset,
@@ -82,6 +102,7 @@ export function buildTemplateVariablesFromBlockSpec(spec) {
82
102
  const persistencePolicy = persistenceEnabled
83
103
  ? spec.persistence.persistencePolicy
84
104
  : "authenticated";
105
+ const queryVariationNamespace = `${namespace}/${slug}`;
85
106
  return {
86
107
  apiClientPackageVersion,
87
108
  author: spec.project.author,
@@ -101,7 +122,13 @@ export function buildTemplateVariablesFromBlockSpec(spec) {
101
122
  dashCase: slug,
102
123
  dataStorageMode,
103
124
  description: spec.metadata.description,
125
+ descriptionJson: JSON.stringify(spec.metadata.description),
104
126
  frontendCssClassName: buildFrontendCssClassName(cssClassName),
127
+ queryAllowedControlsJson: JSON.stringify(spec.queryLoop.enabled ? spec.queryLoop.allowedControls : [], null, 2),
128
+ queryPostType: spec.queryLoop.enabled ? spec.queryLoop.postType : "post",
129
+ queryPostTypeJson: JSON.stringify(spec.queryLoop.enabled ? spec.queryLoop.postType : "post"),
130
+ queryVariationNamespace,
131
+ queryVariationNamespaceJson: JSON.stringify(queryVariationNamespace),
105
132
  isAuthenticatedPersistencePolicy: persistencePolicy === "authenticated" ? "true" : "false",
106
133
  isPublicPersistencePolicy: persistencePolicy === "public" ? "true" : "false",
107
134
  bootstrapCredentialDeclarations: persistencePolicy === "public"
@@ -1,5 +1,5 @@
1
1
  import { buildBuiltInNonTsArtifacts } from "./built-in-block-non-ts-artifacts.js";
2
- import { BASIC_EDIT_TEMPLATE, BASIC_INDEX_TEMPLATE, BASIC_SAVE_TEMPLATE, BASIC_VALIDATORS_TEMPLATE, BLOCK_METADATA_WRAPPER_TEMPLATE, COMPOUND_CHILD_EDIT_TEMPLATE, COMPOUND_CHILD_INDEX_TEMPLATE, COMPOUND_CHILD_SAVE_TEMPLATE, COMPOUND_CHILD_VALIDATORS_TEMPLATE, COMPOUND_CHILDREN_TEMPLATE, COMPOUND_LOCAL_HOOKS_TEMPLATE, COMPOUND_PARENT_EDIT_TEMPLATE, COMPOUND_PARENT_INDEX_TEMPLATE, COMPOUND_PARENT_SAVE_TEMPLATE, COMPOUND_PARENT_VALIDATORS_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_EDIT_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_INTERACTIVITY_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_SAVE_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_VALIDATORS_TEMPLATE, INTERACTIVITY_EDIT_TEMPLATE, INTERACTIVITY_INDEX_TEMPLATE, INTERACTIVITY_SAVE_TEMPLATE, INTERACTIVITY_SCRIPT_TEMPLATE, INTERACTIVITY_VALIDATORS_TEMPLATE, MANIFEST_DEFAULTS_DOCUMENT_WRAPPER_TEMPLATE, MANIFEST_DOCUMENT_WRAPPER_TEMPLATE, PERSISTENCE_EDIT_TEMPLATE, PERSISTENCE_INDEX_TEMPLATE, PERSISTENCE_INTERACTIVITY_TEMPLATE, PERSISTENCE_SAVE_TEMPLATE, PERSISTENCE_VALIDATORS_TEMPLATE, SHARED_HOOKS_TEMPLATE, } from "./built-in-block-code-templates.js";
2
+ import { BASIC_EDIT_TEMPLATE, BASIC_INDEX_TEMPLATE, BASIC_SAVE_TEMPLATE, BASIC_VALIDATORS_TEMPLATE, BLOCK_METADATA_WRAPPER_TEMPLATE, COMPOUND_CHILD_EDIT_TEMPLATE, COMPOUND_CHILD_INDEX_TEMPLATE, COMPOUND_CHILD_SAVE_TEMPLATE, COMPOUND_CHILD_VALIDATORS_TEMPLATE, COMPOUND_CHILDREN_TEMPLATE, COMPOUND_LOCAL_HOOKS_TEMPLATE, COMPOUND_PARENT_EDIT_TEMPLATE, COMPOUND_PARENT_INDEX_TEMPLATE, COMPOUND_PARENT_SAVE_TEMPLATE, COMPOUND_PARENT_VALIDATORS_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_EDIT_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_INTERACTIVITY_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_SAVE_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_VALIDATORS_TEMPLATE, INTERACTIVITY_EDIT_TEMPLATE, INTERACTIVITY_INDEX_TEMPLATE, INTERACTIVITY_SAVE_TEMPLATE, INTERACTIVITY_SCRIPT_TEMPLATE, INTERACTIVITY_VALIDATORS_TEMPLATE, MANIFEST_DEFAULTS_DOCUMENT_WRAPPER_TEMPLATE, MANIFEST_DOCUMENT_WRAPPER_TEMPLATE, PERSISTENCE_EDIT_TEMPLATE, PERSISTENCE_INDEX_TEMPLATE, PERSISTENCE_INTERACTIVITY_TEMPLATE, PERSISTENCE_SAVE_TEMPLATE, PERSISTENCE_VALIDATORS_TEMPLATE, QUERY_LOOP_INDEX_TEMPLATE, SHARED_HOOKS_TEMPLATE, } from "./built-in-block-code-templates.js";
3
3
  import { renderMustacheTemplateString } from "./template-render.js";
4
4
  function renderCodeTemplate(template, variables) {
5
5
  const rendered = renderMustacheTemplateString(template, variables);
@@ -162,6 +162,17 @@ function buildPersistenceCodeArtifacts(variables) {
162
162
  ...buildBuiltInNonTsArtifacts({ templateId: "persistence", variables }),
163
163
  ]);
164
164
  }
165
+ function buildQueryLoopCodeArtifacts(variables) {
166
+ return ensureUniqueArtifactPaths([
167
+ ...createCodeArtifacts([
168
+ {
169
+ relativePath: "src/index.ts",
170
+ template: QUERY_LOOP_INDEX_TEMPLATE,
171
+ },
172
+ ], variables),
173
+ ...buildBuiltInNonTsArtifacts({ templateId: "query-loop", variables }),
174
+ ]);
175
+ }
165
176
  /**
166
177
  * Build the emitter-owned scaffold files for a built-in template family.
167
178
  *
@@ -179,6 +190,8 @@ export function buildBuiltInCodeArtifacts({ templateId, variables, }) {
179
190
  return buildPersistenceCodeArtifacts(variables);
180
191
  case "compound":
181
192
  return buildCompoundCodeArtifacts(variables);
193
+ case "query-loop":
194
+ return buildQueryLoopCodeArtifacts(variables);
182
195
  default: {
183
196
  const unhandledTemplateId = templateId;
184
197
  throw new Error(`Unhandled built-in template id: ${unhandledTemplateId}`);
@@ -0,0 +1 @@
1
+ export declare const QUERY_LOOP_INDEX_TEMPLATE = "import { registerBlockVariation } from '@wordpress/blocks';\nimport type { BlockVariation } from '@wp-typia/block-types/blocks/registration';\nimport { __ } from '@wordpress/i18n';\nimport {\n getQueryLoopCustomAllowedControls,\n getQueryLoopCustomQuerySeed,\n registerQueryLoopEditorExtensions,\n} from './query-extension';\n\ntype QueryLoopVariationAttributes = {\n namespace?: string;\n query?: {\n inherit?: boolean;\n order?: 'asc' | 'desc';\n orderBy?: string;\n perPage?: number;\n postType?: string;\n wpTypiaVariation?: string;\n [key: string]: unknown;\n };\n};\n\ntype QueryLoopVariation = BlockVariation<QueryLoopVariationAttributes> & {\n allowedControls: string[];\n};\n\nconst VARIATION_NAME = {{queryVariationNamespaceJson}};\nconst DEFAULT_ALLOWED_CONTROLS = {{queryAllowedControlsJson}};\nconst customQuerySeed = getQueryLoopCustomQuerySeed();\nconst allowedControls = Array.from(\n new Set([...DEFAULT_ALLOWED_CONTROLS, ...getQueryLoopCustomAllowedControls()]),\n);\n\nconst queryLoopVariation = {\n name: VARIATION_NAME,\n title: __({{titleJson}}, '{{textDomain}}'),\n description: __({{descriptionJson}}, '{{textDomain}}'),\n scope: ['inserter'],\n isActive: ['namespace'],\n attributes: {\n namespace: VARIATION_NAME,\n query: {\n inherit: false,\n order: 'desc',\n orderBy: 'date',\n perPage: 6,\n postType: {{queryPostTypeJson}},\n ...customQuerySeed,\n wpTypiaVariation: VARIATION_NAME,\n },\n },\n allowedControls,\n innerBlocks: [\n [\n 'core/post-template',\n {},\n [\n ['core/post-featured-image'],\n ['core/post-title', { isLink: true }],\n ['core/post-excerpt'],\n ],\n ],\n ['core/query-pagination'],\n ['core/query-no-results'],\n ],\n} satisfies QueryLoopVariation;\n\nregisterBlockVariation('core/query', queryLoopVariation);\nregisterQueryLoopEditorExtensions({ variationName: VARIATION_NAME });\n";
@@ -0,0 +1,70 @@
1
+ export const QUERY_LOOP_INDEX_TEMPLATE = `import { registerBlockVariation } from '@wordpress/blocks';
2
+ import type { BlockVariation } from '@wp-typia/block-types/blocks/registration';
3
+ import { __ } from '@wordpress/i18n';
4
+ import {
5
+ getQueryLoopCustomAllowedControls,
6
+ getQueryLoopCustomQuerySeed,
7
+ registerQueryLoopEditorExtensions,
8
+ } from './query-extension';
9
+
10
+ type QueryLoopVariationAttributes = {
11
+ namespace?: string;
12
+ query?: {
13
+ inherit?: boolean;
14
+ order?: 'asc' | 'desc';
15
+ orderBy?: string;
16
+ perPage?: number;
17
+ postType?: string;
18
+ wpTypiaVariation?: string;
19
+ [key: string]: unknown;
20
+ };
21
+ };
22
+
23
+ type QueryLoopVariation = BlockVariation<QueryLoopVariationAttributes> & {
24
+ allowedControls: string[];
25
+ };
26
+
27
+ const VARIATION_NAME = {{queryVariationNamespaceJson}};
28
+ const DEFAULT_ALLOWED_CONTROLS = {{queryAllowedControlsJson}};
29
+ const customQuerySeed = getQueryLoopCustomQuerySeed();
30
+ const allowedControls = Array.from(
31
+ new Set([...DEFAULT_ALLOWED_CONTROLS, ...getQueryLoopCustomAllowedControls()]),
32
+ );
33
+
34
+ const queryLoopVariation = {
35
+ name: VARIATION_NAME,
36
+ title: __({{titleJson}}, '{{textDomain}}'),
37
+ description: __({{descriptionJson}}, '{{textDomain}}'),
38
+ scope: ['inserter'],
39
+ isActive: ['namespace'],
40
+ attributes: {
41
+ namespace: VARIATION_NAME,
42
+ query: {
43
+ inherit: false,
44
+ order: 'desc',
45
+ orderBy: 'date',
46
+ perPage: 6,
47
+ postType: {{queryPostTypeJson}},
48
+ ...customQuerySeed,
49
+ wpTypiaVariation: VARIATION_NAME,
50
+ },
51
+ },
52
+ allowedControls,
53
+ innerBlocks: [
54
+ [
55
+ 'core/post-template',
56
+ {},
57
+ [
58
+ ['core/post-featured-image'],
59
+ ['core/post-title', { isLink: true }],
60
+ ['core/post-excerpt'],
61
+ ],
62
+ ],
63
+ ['core/query-pagination'],
64
+ ['core/query-no-results'],
65
+ ],
66
+ } satisfies QueryLoopVariation;
67
+
68
+ registerBlockVariation('core/query', queryLoopVariation);
69
+ registerQueryLoopEditorExtensions({ variationName: VARIATION_NAME });
70
+ `;
@@ -6,4 +6,5 @@ export { BLOCK_METADATA_WRAPPER_TEMPLATE, MANIFEST_DEFAULTS_DOCUMENT_WRAPPER_TEM
6
6
  export { BASIC_EDIT_TEMPLATE, BASIC_INDEX_TEMPLATE, BASIC_SAVE_TEMPLATE, BASIC_VALIDATORS_TEMPLATE, } from "./built-in-block-code-templates/basic.js";
7
7
  export { INTERACTIVITY_EDIT_TEMPLATE, INTERACTIVITY_INDEX_TEMPLATE, INTERACTIVITY_SAVE_TEMPLATE, INTERACTIVITY_SCRIPT_TEMPLATE, INTERACTIVITY_VALIDATORS_TEMPLATE, } from "./built-in-block-code-templates/interactivity.js";
8
8
  export { PERSISTENCE_EDIT_TEMPLATE, PERSISTENCE_INDEX_TEMPLATE, PERSISTENCE_INTERACTIVITY_TEMPLATE, PERSISTENCE_SAVE_TEMPLATE, PERSISTENCE_VALIDATORS_TEMPLATE, } from "./built-in-block-code-templates/persistence.js";
9
+ export { QUERY_LOOP_INDEX_TEMPLATE, } from "./built-in-block-code-templates/query-loop.js";
9
10
  export { COMPOUND_CHILD_EDIT_TEMPLATE, COMPOUND_CHILD_INDEX_TEMPLATE, COMPOUND_CHILDREN_TEMPLATE, COMPOUND_CHILD_SAVE_TEMPLATE, COMPOUND_CHILD_VALIDATORS_TEMPLATE, COMPOUND_LOCAL_HOOKS_TEMPLATE, COMPOUND_PARENT_EDIT_TEMPLATE, COMPOUND_PARENT_INDEX_TEMPLATE, COMPOUND_PARENT_SAVE_TEMPLATE, COMPOUND_PARENT_VALIDATORS_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_EDIT_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_INTERACTIVITY_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_SAVE_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_VALIDATORS_TEMPLATE, } from "./built-in-block-code-templates/compound.js";
@@ -6,4 +6,5 @@ export { BLOCK_METADATA_WRAPPER_TEMPLATE, MANIFEST_DEFAULTS_DOCUMENT_WRAPPER_TEM
6
6
  export { BASIC_EDIT_TEMPLATE, BASIC_INDEX_TEMPLATE, BASIC_SAVE_TEMPLATE, BASIC_VALIDATORS_TEMPLATE, } from "./built-in-block-code-templates/basic.js";
7
7
  export { INTERACTIVITY_EDIT_TEMPLATE, INTERACTIVITY_INDEX_TEMPLATE, INTERACTIVITY_SAVE_TEMPLATE, INTERACTIVITY_SCRIPT_TEMPLATE, INTERACTIVITY_VALIDATORS_TEMPLATE, } from "./built-in-block-code-templates/interactivity.js";
8
8
  export { PERSISTENCE_EDIT_TEMPLATE, PERSISTENCE_INDEX_TEMPLATE, PERSISTENCE_INTERACTIVITY_TEMPLATE, PERSISTENCE_SAVE_TEMPLATE, PERSISTENCE_VALIDATORS_TEMPLATE, } from "./built-in-block-code-templates/persistence.js";
9
+ export { QUERY_LOOP_INDEX_TEMPLATE, } from "./built-in-block-code-templates/query-loop.js";
9
10
  export { COMPOUND_CHILD_EDIT_TEMPLATE, COMPOUND_CHILD_INDEX_TEMPLATE, COMPOUND_CHILDREN_TEMPLATE, COMPOUND_CHILD_SAVE_TEMPLATE, COMPOUND_CHILD_VALIDATORS_TEMPLATE, COMPOUND_LOCAL_HOOKS_TEMPLATE, COMPOUND_PARENT_EDIT_TEMPLATE, COMPOUND_PARENT_INDEX_TEMPLATE, COMPOUND_PARENT_SAVE_TEMPLATE, COMPOUND_PARENT_VALIDATORS_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_EDIT_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_INTERACTIVITY_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_SAVE_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_VALIDATORS_TEMPLATE, } from "./built-in-block-code-templates/compound.js";
@@ -555,6 +555,8 @@ export function buildBuiltInNonTsArtifacts({ templateId, variables, }) {
555
555
  return buildPersistenceArtifacts(variables);
556
556
  case "compound":
557
557
  return buildCompoundArtifacts(variables);
558
+ case "query-loop":
559
+ return [];
558
560
  default: {
559
561
  const unhandledTemplateId = templateId;
560
562
  throw new Error(`Unhandled built-in template id: ${unhandledTemplateId}`);
@@ -11,6 +11,7 @@ import { TEMPLATE_IDS } from "./template-registry.js";
11
11
  export function formatHelpText() {
12
12
  return `Usage:
13
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>]
14
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>]
15
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>]
16
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,18 +18,17 @@ export function createReadlinePrompt() {
18
18
  * retry behavior without stubbing global stdin/stdout.
19
19
  */
20
20
  export function createReadlinePromptWithInterface(rl) {
21
+ const askQuestion = (query) => new Promise((resolve) => {
22
+ rl.question(query, resolve);
23
+ });
21
24
  return {
22
25
  async text(message, defaultValue, validate) {
23
- const suffix = defaultValue ? ` (${defaultValue})` : "";
24
26
  while (true) {
25
- const answer = await new Promise((resolve) => {
26
- rl.question(`${message}${suffix}: `, resolve);
27
- });
28
- const value = String(answer).trim() || defaultValue;
27
+ const value = normalizePromptAnswer(await askQuestion(formatTextPrompt(message, defaultValue))) || defaultValue;
29
28
  if (validate) {
30
29
  const result = validate(value);
31
30
  if (result !== true) {
32
- console.error(`❌ ${typeof result === "string" ? result : "Invalid input"}`);
31
+ console.error(formatValidationError(message, result, defaultValue));
33
32
  continue;
34
33
  }
35
34
  }
@@ -40,22 +39,22 @@ export function createReadlinePromptWithInterface(rl) {
40
39
  if (options.length === 0) {
41
40
  throw new Error(`select() requires at least one option for prompt: ${message}`);
42
41
  }
43
- console.log(message);
44
- options.forEach((option, index) => {
45
- const hint = option.hint ? ` - ${option.hint}` : "";
46
- console.log(` ${index + 1}. ${option.label}${hint}`);
47
- });
42
+ const resolvedDefaultIndex = getResolvedDefaultIndex(options, defaultValue);
43
+ renderSelectPrompt(message, options, resolvedDefaultIndex);
48
44
  while (true) {
49
- const answer = await this.text("Choice", String(defaultValue));
50
- const numericChoice = Number(answer);
51
- if (!Number.isNaN(numericChoice) && options[numericChoice - 1]) {
52
- return options[numericChoice - 1].value;
45
+ const answer = normalizePromptAnswer(await askQuestion(formatChoicePrompt(resolvedDefaultIndex)));
46
+ if (answer.length === 0) {
47
+ return options[resolvedDefaultIndex].value;
48
+ }
49
+ const selection = resolvePromptSelection(options, answer);
50
+ if (selection) {
51
+ return selection.value;
53
52
  }
54
- const directChoice = options.find((option) => option.value === answer);
55
- if (directChoice) {
56
- return directChoice.value;
53
+ if (isPromptHelpToken(answer)) {
54
+ renderSelectPrompt(message, options, resolvedDefaultIndex);
55
+ continue;
57
56
  }
58
- console.error(`❌ Invalid selection: ${answer}`);
57
+ console.error(formatInvalidSelectionError(answer, options, resolvedDefaultIndex));
59
58
  }
60
59
  },
61
60
  close() {
@@ -63,3 +62,63 @@ export function createReadlinePromptWithInterface(rl) {
63
62
  },
64
63
  };
65
64
  }
65
+ function normalizePromptAnswer(value) {
66
+ return String(value).trim();
67
+ }
68
+ function normalizePromptToken(value) {
69
+ return value.trim().toLowerCase();
70
+ }
71
+ function getResolvedDefaultIndex(options, defaultValue) {
72
+ const candidateIndex = Number.isInteger(defaultValue) ? defaultValue - 1 : -1;
73
+ return options[candidateIndex] ? candidateIndex : 0;
74
+ }
75
+ function formatTextPrompt(message, defaultValue) {
76
+ const suffix = defaultValue.length > 0 ? ` [default: ${defaultValue}]` : "";
77
+ return `${message}${suffix}: `;
78
+ }
79
+ function formatValidationError(message, result, defaultValue) {
80
+ const detail = typeof result === "string" ? result : "Invalid input";
81
+ const retryHint = defaultValue.length > 0 ? ` Press Enter to keep "${defaultValue}".` : "";
82
+ return `❌ ${message}: ${detail}.${retryHint}`;
83
+ }
84
+ function formatChoicePrompt(defaultIndex) {
85
+ return `Choice [default: ${defaultIndex + 1}, ? for options]: `;
86
+ }
87
+ function renderSelectPrompt(message, options, defaultIndex) {
88
+ console.log(message);
89
+ console.log(" Enter a number, option label, or option value. Press Enter to keep the default, or type ? to list choices again.");
90
+ options.forEach((option, index) => {
91
+ const defaultMarker = index === defaultIndex ? " (default)" : "";
92
+ const valueHint = normalizePromptToken(option.label) === normalizePromptToken(option.value)
93
+ ? ""
94
+ : ` [${option.value}]`;
95
+ console.log(` ${index + 1}. ${option.label}${valueHint}${defaultMarker}`);
96
+ if (option.hint) {
97
+ console.log(` ${option.hint}`);
98
+ }
99
+ });
100
+ }
101
+ function isPromptHelpToken(answer) {
102
+ const normalized = normalizePromptToken(answer);
103
+ return normalized === "?" || normalized === "help" || normalized === "list";
104
+ }
105
+ function resolvePromptSelection(options, answer) {
106
+ const numericChoice = Number(answer);
107
+ if (!Number.isNaN(numericChoice) && options[numericChoice - 1]) {
108
+ return options[numericChoice - 1];
109
+ }
110
+ const normalizedAnswer = normalizePromptToken(answer);
111
+ return options.find((option) => {
112
+ const normalizedLabel = normalizePromptToken(option.label);
113
+ const normalizedValue = normalizePromptToken(option.value);
114
+ return normalizedAnswer === normalizedLabel || normalizedAnswer === normalizedValue;
115
+ });
116
+ }
117
+ function formatInvalidSelectionError(answer, options, defaultIndex) {
118
+ const optionValues = options.map((option) => option.value).join(", ");
119
+ return [
120
+ `❌ Invalid selection: ${answer}.`,
121
+ `Enter 1-${options.length}, one of: ${optionValues},`,
122
+ `or press Enter for "${options[defaultIndex].label}".`,
123
+ ].join(" ");
124
+ }
@@ -34,6 +34,7 @@ interface RunScaffoldFlowOptions {
34
34
  phpPrefix?: string;
35
35
  projectInput: string;
36
36
  promptText?: Parameters<typeof collectScaffoldAnswers>[0]["promptText"];
37
+ queryPostType?: string;
37
38
  selectDataStorage?: () => Promise<DataStorageMode>;
38
39
  selectExternalLayerId?: (options: ExternalLayerSelectionOption[]) => Promise<string>;
39
40
  selectPackageManager?: () => Promise<PackageManagerId>;
@@ -73,7 +74,7 @@ export declare function getOptionalOnboarding({ availableScripts, packageManager
73
74
  * project.
74
75
  * @returns The scaffold result together with next-step guidance.
75
76
  */
76
- export declare function runScaffoldFlow({ projectInput, cwd, templateId, dataStorageMode, externalLayerId, externalLayerSource, persistencePolicy, packageManager, namespace, textDomain, phpPrefix, yes, noInstall, isInteractive, allowExistingDir, selectTemplate, selectDataStorage, selectExternalLayerId, selectPersistencePolicy, selectPackageManager, promptText, installDependencies, variant, selectWithTestPreset, selectWithWpEnv, selectWithMigrationUi, withMigrationUi, withTestPreset, withWpEnv, }: RunScaffoldFlowOptions): Promise<{
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<{
77
78
  optionalOnboarding: OptionalOnboardingGuidance;
78
79
  projectDir: string;
79
80
  projectInput: string;
@@ -135,7 +135,7 @@ export function getOptionalOnboarding({ availableScripts, packageManager, templa
135
135
  * project.
136
136
  * @returns The scaffold result together with next-step guidance.
137
137
  */
138
- export async function runScaffoldFlow({ projectInput, cwd = process.cwd(), templateId, dataStorageMode, externalLayerId, externalLayerSource, persistencePolicy, packageManager, namespace, textDomain, phpPrefix, yes = false, noInstall = false, isInteractive = false, allowExistingDir = false, selectTemplate, selectDataStorage, selectExternalLayerId, selectPersistencePolicy, selectPackageManager, promptText, installDependencies = undefined, variant, selectWithTestPreset, selectWithWpEnv, selectWithMigrationUi, withMigrationUi, withTestPreset, withWpEnv, }) {
138
+ export async function runScaffoldFlow({ projectInput, cwd = process.cwd(), templateId, dataStorageMode, externalLayerId, externalLayerSource, persistencePolicy, packageManager, namespace, textDomain, phpPrefix, queryPostType, yes = false, noInstall = false, isInteractive = false, allowExistingDir = false, selectTemplate, selectDataStorage, selectExternalLayerId, selectPersistencePolicy, selectPackageManager, promptText, installDependencies = undefined, variant, selectWithTestPreset, selectWithWpEnv, selectWithMigrationUi, withMigrationUi, withTestPreset, withWpEnv, }) {
139
139
  const normalizedExternalLayerId = typeof externalLayerId === "string" && externalLayerId.trim().length > 0
140
140
  ? externalLayerId.trim()
141
141
  : undefined;
@@ -228,6 +228,7 @@ export async function runScaffoldFlow({ projectInput, cwd = process.cwd(), templ
228
228
  persistencePolicy: resolvedPersistencePolicy,
229
229
  phpPrefix,
230
230
  projectName,
231
+ queryPostType,
231
232
  templateId: resolvedTemplateId,
232
233
  textDomain,
233
234
  yes,
@@ -13,6 +13,12 @@ import { copyInterpolatedDirectory } from "./template-render.js";
13
13
  function templateHasPersistenceSync(templateId, compoundPersistenceEnabled) {
14
14
  return templateId === "persistence" || (templateId === "compound" && compoundPersistenceEnabled);
15
15
  }
16
+ function templateSupportsGeneratedSyncWatchers(templateId) {
17
+ return (templateId === "basic" ||
18
+ templateId === "interactivity" ||
19
+ templateId === "persistence" ||
20
+ templateId === "compound");
21
+ }
16
22
  function getWatchSyncTypesScript(packageManager, templateId) {
17
23
  if (templateId === "compound") {
18
24
  return `chokidar "src/blocks/**/types.ts" "scripts/block-config.ts" --debounce 200 -c "${formatRunScript(packageManager, "sync-types")}"`;
@@ -80,11 +86,16 @@ export async function applyLocalDevPresetFiles({ projectDir, variables, withTest
80
86
  */
81
87
  export async function applyGeneratedProjectDxPackageJson({ compoundPersistenceEnabled = false, packageManager, projectDir, templateId, withTestPreset = false, withWpEnv = false, }) {
82
88
  const hasPersistenceSync = templateHasPersistenceSync(templateId, compoundPersistenceEnabled);
89
+ const supportsGeneratedSyncWatchers = templateSupportsGeneratedSyncWatchers(templateId);
83
90
  await mutatePackageJson(projectDir, (packageJson) => {
84
91
  packageJson.devDependencies = {
85
92
  ...(packageJson.devDependencies ?? {}),
86
- "chokidar-cli": "^3.0.0",
87
- concurrently: "^9.0.1",
93
+ ...(supportsGeneratedSyncWatchers
94
+ ? {
95
+ "chokidar-cli": "^3.0.0",
96
+ concurrently: "^9.0.1",
97
+ }
98
+ : {}),
88
99
  };
89
100
  if (withWpEnv || withTestPreset) {
90
101
  packageJson.devDependencies["@wordpress/env"] = "^11.2.0";
@@ -95,12 +106,16 @@ export async function applyGeneratedProjectDxPackageJson({ compoundPersistenceEn
95
106
  const scripts = {
96
107
  ...(packageJson.scripts ?? {}),
97
108
  };
98
- scripts["start:editor"] = "wp-scripts start --experimental-modules";
99
- scripts["watch:sync-types"] = getWatchSyncTypesScript(packageManager, templateId);
109
+ if (supportsGeneratedSyncWatchers) {
110
+ scripts["start:editor"] = "wp-scripts start --experimental-modules";
111
+ scripts["watch:sync-types"] = getWatchSyncTypesScript(packageManager, templateId);
112
+ }
100
113
  if (hasPersistenceSync) {
101
114
  scripts["watch:sync-rest"] = getWatchSyncRestScript(packageManager, templateId);
102
115
  }
103
- scripts.dev = getDevScript(packageManager, compoundPersistenceEnabled, templateId);
116
+ if (supportsGeneratedSyncWatchers) {
117
+ scripts.dev = getDevScript(packageManager, compoundPersistenceEnabled, templateId);
118
+ }
104
119
  if (withWpEnv) {
105
120
  scripts["wp-env:start"] = "wp-env start";
106
121
  scripts["wp-env:stop"] = "wp-env stop";
@@ -123,10 +138,5 @@ export async function applyGeneratedProjectDxPackageJson({ compoundPersistenceEn
123
138
  * scaffolded template.
124
139
  */
125
140
  export function getPrimaryDevelopmentScript(templateId) {
126
- return templateId === "basic" ||
127
- templateId === "interactivity" ||
128
- templateId === "persistence" ||
129
- templateId === "compound"
130
- ? "dev"
131
- : "start";
141
+ return templateSupportsGeneratedSyncWatchers(templateId) ? "dev" : "start";
132
142
  }
@@ -0,0 +1,37 @@
1
+ import type { PackageManagerId } from './package-managers.js';
2
+ import type { CollectScaffoldAnswersOptions, ResolvePackageManagerOptions, ResolveTemplateOptions, ScaffoldAnswers } from './scaffold.js';
3
+ /**
4
+ * Detect the current author name from local Git config.
5
+ *
6
+ * @returns The configured Git author name, or `"Your Name"` when unavailable.
7
+ */
8
+ export declare function detectAuthor(): string;
9
+ /**
10
+ * Compute the default scaffold answers for one project and template pair.
11
+ *
12
+ * @param projectName User-supplied project directory or block name seed.
13
+ * @param templateId Selected scaffold template identifier.
14
+ * @returns Normalized default answers for scaffold prompts and non-interactive flows.
15
+ */
16
+ export declare function getDefaultAnswers(projectName: string, templateId: string): ScaffoldAnswers;
17
+ /**
18
+ * Resolve the scaffold template id from flags, defaults, and interactive selection.
19
+ *
20
+ * @param options Template resolution options for interactive and non-interactive flows.
21
+ * @returns The normalized template identifier to scaffold.
22
+ */
23
+ export declare function resolveTemplateId({ templateId, yes, isInteractive, selectTemplate, }: ResolveTemplateOptions): Promise<string>;
24
+ /**
25
+ * Resolve the package manager id from flags, defaults, and interactive selection.
26
+ *
27
+ * @param options Package manager resolution options for interactive and non-interactive flows.
28
+ * @returns The normalized package manager id.
29
+ */
30
+ export declare function resolvePackageManagerId({ packageManager, yes, isInteractive, selectPackageManager, }: ResolvePackageManagerOptions): Promise<PackageManagerId>;
31
+ /**
32
+ * Collect scaffold answers from defaults, CLI overrides, and optional prompts.
33
+ *
34
+ * @param options Answer collection inputs including prompt callbacks and explicit overrides.
35
+ * @returns The normalized scaffold answers used for rendering and file generation.
36
+ */
37
+ export declare function collectScaffoldAnswers({ projectName, templateId, yes, dataStorageMode, namespace, persistencePolicy, phpPrefix, promptText, queryPostType, textDomain, }: CollectScaffoldAnswersOptions): Promise<ScaffoldAnswers>;