@wp-typia/project-tools 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +32 -0
  2. package/dist/runtime/cli-add.d.ts +38 -0
  3. package/dist/runtime/cli-add.js +561 -0
  4. package/dist/runtime/cli-core.d.ts +25 -0
  5. package/dist/runtime/cli-core.js +25 -0
  6. package/dist/runtime/cli-doctor.d.ts +34 -0
  7. package/dist/runtime/cli-doctor.js +131 -0
  8. package/dist/runtime/cli-help.d.ts +9 -0
  9. package/dist/runtime/cli-help.js +37 -0
  10. package/dist/runtime/cli-prompt.d.ts +21 -0
  11. package/dist/runtime/cli-prompt.js +53 -0
  12. package/dist/runtime/cli-scaffold.d.ts +79 -0
  13. package/dist/runtime/cli-scaffold.js +206 -0
  14. package/dist/runtime/cli-templates.d.ts +30 -0
  15. package/dist/runtime/cli-templates.js +61 -0
  16. package/dist/runtime/index.d.ts +9 -0
  17. package/dist/runtime/index.js +7 -0
  18. package/dist/runtime/json-utils.d.ts +10 -0
  19. package/dist/runtime/json-utils.js +12 -0
  20. package/dist/runtime/local-dev-presets.d.ts +26 -0
  21. package/dist/runtime/local-dev-presets.js +132 -0
  22. package/dist/runtime/metadata-analysis.d.ts +11 -0
  23. package/dist/runtime/metadata-analysis.js +285 -0
  24. package/dist/runtime/metadata-model.d.ts +84 -0
  25. package/dist/runtime/metadata-model.js +59 -0
  26. package/dist/runtime/metadata-parser.d.ts +53 -0
  27. package/dist/runtime/metadata-parser.js +794 -0
  28. package/dist/runtime/metadata-php-render.d.ts +29 -0
  29. package/dist/runtime/metadata-php-render.js +549 -0
  30. package/dist/runtime/metadata-projection.d.ts +7 -0
  31. package/dist/runtime/metadata-projection.js +233 -0
  32. package/dist/runtime/migration-constants.d.ts +15 -0
  33. package/dist/runtime/migration-constants.js +16 -0
  34. package/dist/runtime/migration-diff.d.ts +2 -0
  35. package/dist/runtime/migration-diff.js +537 -0
  36. package/dist/runtime/migration-fixtures.d.ts +8 -0
  37. package/dist/runtime/migration-fixtures.js +94 -0
  38. package/dist/runtime/migration-fuzz-plan.d.ts +2 -0
  39. package/dist/runtime/migration-fuzz-plan.js +50 -0
  40. package/dist/runtime/migration-manifest.d.ts +19 -0
  41. package/dist/runtime/migration-manifest.js +129 -0
  42. package/dist/runtime/migration-project.d.ts +94 -0
  43. package/dist/runtime/migration-project.js +1101 -0
  44. package/dist/runtime/migration-render.d.ts +11 -0
  45. package/dist/runtime/migration-render.js +741 -0
  46. package/dist/runtime/migration-risk.d.ts +4 -0
  47. package/dist/runtime/migration-risk.js +52 -0
  48. package/dist/runtime/migration-types.d.ts +249 -0
  49. package/dist/runtime/migration-types.js +1 -0
  50. package/dist/runtime/migration-ui-capability.d.ts +17 -0
  51. package/dist/runtime/migration-ui-capability.js +190 -0
  52. package/dist/runtime/migration-utils.d.ts +69 -0
  53. package/dist/runtime/migration-utils.js +246 -0
  54. package/dist/runtime/migrations.d.ts +249 -0
  55. package/dist/runtime/migrations.js +1061 -0
  56. package/dist/runtime/object-utils.d.ts +12 -0
  57. package/dist/runtime/object-utils.js +14 -0
  58. package/dist/runtime/package-managers.d.ts +28 -0
  59. package/dist/runtime/package-managers.js +156 -0
  60. package/dist/runtime/package-versions.d.ts +10 -0
  61. package/dist/runtime/package-versions.js +68 -0
  62. package/dist/runtime/scaffold-onboarding.d.ts +32 -0
  63. package/dist/runtime/scaffold-onboarding.js +99 -0
  64. package/dist/runtime/scaffold.d.ts +146 -0
  65. package/dist/runtime/scaffold.js +612 -0
  66. package/dist/runtime/schema-core.d.ts +267 -0
  67. package/dist/runtime/schema-core.js +597 -0
  68. package/dist/runtime/starter-manifests.d.ts +25 -0
  69. package/dist/runtime/starter-manifests.js +383 -0
  70. package/dist/runtime/string-case.d.ts +36 -0
  71. package/dist/runtime/string-case.js +69 -0
  72. package/dist/runtime/template-builtins.d.ts +38 -0
  73. package/dist/runtime/template-builtins.js +72 -0
  74. package/dist/runtime/template-defaults.d.ts +75 -0
  75. package/dist/runtime/template-defaults.js +65 -0
  76. package/dist/runtime/template-registry.d.ts +36 -0
  77. package/dist/runtime/template-registry.js +94 -0
  78. package/dist/runtime/template-render.d.ts +24 -0
  79. package/dist/runtime/template-render.js +113 -0
  80. package/dist/runtime/template-source.d.ts +71 -0
  81. package/dist/runtime/template-source.js +821 -0
  82. package/dist/runtime/typia-tags.d.ts +1 -0
  83. package/dist/runtime/typia-tags.js +1 -0
  84. package/package.json +79 -0
  85. package/templates/_shared/base/languages/.gitkeep +1 -0
  86. package/templates/_shared/base/package.json.mustache +41 -0
  87. package/templates/_shared/base/scripts/sync-types-to-block-json.ts.mustache +118 -0
  88. package/templates/_shared/base/src/hooks.ts.mustache +19 -0
  89. package/templates/_shared/base/src/validator-toolkit.ts.mustache +31 -0
  90. package/templates/_shared/base/tsconfig.json.mustache +21 -0
  91. package/templates/_shared/base/webpack.config.js.mustache +99 -0
  92. package/templates/_shared/base/{{slugKebabCase}}.php.mustache +53 -0
  93. package/templates/_shared/compound/core/package.json.mustache +45 -0
  94. package/templates/_shared/compound/core/scripts/add-compound-child.ts.mustache +559 -0
  95. package/templates/_shared/compound/core/scripts/block-config.ts.mustache +13 -0
  96. package/templates/_shared/compound/core/scripts/sync-types-to-block-json.ts.mustache +53 -0
  97. package/templates/_shared/compound/core/webpack.config.js.mustache +141 -0
  98. package/templates/_shared/compound/core/{{slugKebabCase}}.php.mustache +51 -0
  99. package/templates/_shared/compound/persistence/package.json.mustache +50 -0
  100. package/templates/_shared/compound/persistence/scripts/block-config.ts.mustache +59 -0
  101. package/templates/_shared/compound/persistence/scripts/sync-rest-contracts.ts.mustache +101 -0
  102. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api-types.ts.mustache +21 -0
  103. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api-validators.ts.mustache +32 -0
  104. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api.ts.mustache +68 -0
  105. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/block.json.mustache +52 -0
  106. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/data.ts.mustache +192 -0
  107. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/edit.tsx.mustache +123 -0
  108. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/hooks.ts.mustache +11 -0
  109. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/interactivity.ts.mustache +132 -0
  110. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/render.php.mustache +158 -0
  111. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/save.tsx.mustache +3 -0
  112. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/types.ts.mustache +56 -0
  113. package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/validators.ts.mustache +32 -0
  114. package/templates/_shared/compound/persistence-auth/{{slugKebabCase}}.php.mustache +294 -0
  115. package/templates/_shared/compound/persistence-public/{{slugKebabCase}}.php.mustache +312 -0
  116. package/templates/_shared/migration-ui/common/src/admin/migration-dashboard.tsx +394 -0
  117. package/templates/_shared/migration-ui/common/src/migration-detector.ts +9 -0
  118. package/templates/_shared/migration-ui/common/src/migrations/helpers.ts +490 -0
  119. package/templates/_shared/migration-ui/common/src/migrations/index.ts +886 -0
  120. package/templates/_shared/persistence/auth/{{slugKebabCase}}.php.mustache +290 -0
  121. package/templates/_shared/persistence/core/package.json.mustache +46 -0
  122. package/templates/_shared/persistence/core/scripts/sync-rest-contracts.ts.mustache +113 -0
  123. package/templates/_shared/persistence/core/scripts/sync-types-to-block-json.ts.mustache +125 -0
  124. package/templates/_shared/persistence/core/src/api-types.ts.mustache +21 -0
  125. package/templates/_shared/persistence/core/src/api-validators.ts.mustache +32 -0
  126. package/templates/_shared/persistence/core/src/api.ts.mustache +68 -0
  127. package/templates/_shared/persistence/core/src/data.ts.mustache +192 -0
  128. package/templates/_shared/persistence/core/src/index.tsx.mustache +25 -0
  129. package/templates/_shared/persistence/core/src/interactivity.ts.mustache +134 -0
  130. package/templates/_shared/persistence/core/src/save.tsx.mustache +5 -0
  131. package/templates/_shared/persistence/core/src/validators.ts.mustache +32 -0
  132. package/templates/_shared/persistence/core/{{slugKebabCase}}.php.mustache +336 -0
  133. package/templates/_shared/persistence/public/{{slugKebabCase}}.php.mustache +308 -0
  134. package/templates/_shared/presets/test-preset/.wp-env.test.json.mustache +16 -0
  135. package/templates/_shared/presets/test-preset/playwright.config.ts.mustache +22 -0
  136. package/templates/_shared/presets/test-preset/scripts/wait-for-wp-env.mjs.mustache +102 -0
  137. package/templates/_shared/presets/test-preset/scripts/wp-env-utils.cjs.mustache +32 -0
  138. package/templates/_shared/presets/test-preset/tests/e2e/smoke.spec.ts.mustache +34 -0
  139. package/templates/_shared/presets/wp-env/.wp-env.json.mustache +16 -0
  140. package/templates/_shared/rest-helpers/auth/inc/rest-auth.php.mustache +37 -0
  141. package/templates/_shared/rest-helpers/public/inc/rest-public.php.mustache +314 -0
  142. package/templates/_shared/rest-helpers/shared/inc/rest-shared.php.mustache +58 -0
  143. package/templates/_shared/workspace/persistence-auth/inc/rest-auth.php.mustache +36 -0
  144. package/templates/_shared/workspace/persistence-auth/inc/rest-shared.php.mustache +55 -0
  145. package/templates/_shared/workspace/persistence-auth/server.php.mustache +237 -0
  146. package/templates/_shared/workspace/persistence-public/inc/rest-public.php.mustache +273 -0
  147. package/templates/_shared/workspace/persistence-public/inc/rest-shared.php.mustache +55 -0
  148. package/templates/_shared/workspace/persistence-public/server.php.mustache +252 -0
  149. package/templates/basic/src/block.json.mustache +51 -0
  150. package/templates/basic/src/edit.tsx.mustache +128 -0
  151. package/templates/basic/src/editor.scss.mustache +8 -0
  152. package/templates/basic/src/hooks.ts.mustache +18 -0
  153. package/templates/basic/src/index.tsx.mustache +45 -0
  154. package/templates/basic/src/save.tsx.mustache +30 -0
  155. package/templates/basic/src/style.scss.mustache +40 -0
  156. package/templates/basic/src/types.ts.mustache +56 -0
  157. package/templates/basic/src/validators.ts.mustache +26 -0
  158. package/templates/compound/src/blocks/{{slugKebabCase}}/block.json.mustache +37 -0
  159. package/templates/compound/src/blocks/{{slugKebabCase}}/children.ts.mustache +25 -0
  160. package/templates/compound/src/blocks/{{slugKebabCase}}/edit.tsx.mustache +93 -0
  161. package/templates/compound/src/blocks/{{slugKebabCase}}/hooks.ts.mustache +11 -0
  162. package/templates/compound/src/blocks/{{slugKebabCase}}/index.tsx.mustache +25 -0
  163. package/templates/compound/src/blocks/{{slugKebabCase}}/save.tsx.mustache +32 -0
  164. package/templates/compound/src/blocks/{{slugKebabCase}}/style.scss.mustache +31 -0
  165. package/templates/compound/src/blocks/{{slugKebabCase}}/types.ts.mustache +13 -0
  166. package/templates/compound/src/blocks/{{slugKebabCase}}/validators.ts.mustache +17 -0
  167. package/templates/compound/src/blocks/{{slugKebabCase}}-item/block.json.mustache +35 -0
  168. package/templates/compound/src/blocks/{{slugKebabCase}}-item/edit.tsx.mustache +50 -0
  169. package/templates/compound/src/blocks/{{slugKebabCase}}-item/hooks.ts.mustache +11 -0
  170. package/templates/compound/src/blocks/{{slugKebabCase}}-item/index.tsx.mustache +25 -0
  171. package/templates/compound/src/blocks/{{slugKebabCase}}-item/save.tsx.mustache +24 -0
  172. package/templates/compound/src/blocks/{{slugKebabCase}}-item/types.ts.mustache +12 -0
  173. package/templates/compound/src/blocks/{{slugKebabCase}}-item/validators.ts.mustache +17 -0
  174. package/templates/interactivity/package.json.mustache +42 -0
  175. package/templates/interactivity/src/block.json.mustache +73 -0
  176. package/templates/interactivity/src/edit.tsx.mustache +270 -0
  177. package/templates/interactivity/src/index.tsx.mustache +32 -0
  178. package/templates/interactivity/src/interactivity.ts.mustache +152 -0
  179. package/templates/interactivity/src/save.tsx.mustache +101 -0
  180. package/templates/interactivity/src/style.scss.mustache +60 -0
  181. package/templates/interactivity/src/types.ts.mustache +32 -0
  182. package/templates/interactivity/src/validators.ts.mustache +36 -0
  183. package/templates/persistence/src/block.json.mustache +52 -0
  184. package/templates/persistence/src/edit.tsx.mustache +165 -0
  185. package/templates/persistence/src/render.php.mustache +126 -0
  186. package/templates/persistence/src/style.scss.mustache +46 -0
  187. package/templates/persistence/src/types.ts.mustache +55 -0
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Internal built-in template metadata defaults used by scaffold rendering.
3
+ */
4
+ export const BUILTIN_BLOCK_METADATA_VERSION = "0.1.0";
5
+ /**
6
+ * Built-in parent block metadata defaults keyed by template id.
7
+ */
8
+ export const BUILTIN_TEMPLATE_METADATA_DEFAULTS = Object.freeze({
9
+ basic: Object.freeze({
10
+ category: "text",
11
+ icon: "smiley",
12
+ }),
13
+ interactivity: Object.freeze({
14
+ category: "widgets",
15
+ icon: "smiley",
16
+ }),
17
+ persistence: Object.freeze({
18
+ category: "widgets",
19
+ icon: "database",
20
+ }),
21
+ compound: Object.freeze({
22
+ category: "widgets",
23
+ icon: "screenoptions",
24
+ }),
25
+ });
26
+ /**
27
+ * Shared hidden child block metadata defaults for compound scaffolds.
28
+ */
29
+ export const COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS = Object.freeze({
30
+ category: "widgets",
31
+ icon: "excerpt-view",
32
+ });
33
+ /**
34
+ * Legacy built-in template ids that were removed in favor of persistence modes.
35
+ */
36
+ export const REMOVED_BUILTIN_TEMPLATE_IDS = ["data", "persisted"];
37
+ /**
38
+ * Returns the metadata defaults for a built-in scaffold template id.
39
+ *
40
+ * @param templateId Built-in template id whose metadata defaults should be read.
41
+ * @returns The stable category/icon defaults used by scaffold rendering.
42
+ */
43
+ export function getBuiltInTemplateMetadataDefaults(templateId) {
44
+ return BUILTIN_TEMPLATE_METADATA_DEFAULTS[templateId];
45
+ }
46
+ /**
47
+ * Checks whether a template id points at a removed built-in scaffold.
48
+ *
49
+ * @param templateId Template id supplied to scaffold resolution.
50
+ * @returns True when the template id is one of the removed legacy built-ins.
51
+ */
52
+ export function isRemovedBuiltInTemplateId(templateId) {
53
+ return REMOVED_BUILTIN_TEMPLATE_IDS.includes(templateId);
54
+ }
55
+ /**
56
+ * Builds the stable recovery guidance shown for removed built-in template ids.
57
+ *
58
+ * @param templateId Removed template id, where `data` maps to the public policy and
59
+ * `persisted` maps to the authenticated policy.
60
+ * @returns A user-facing error string in the form
61
+ * `Built-in template "<id>" was removed. Use --template persistence --persistence-policy <policy> instead.`
62
+ */
63
+ export function getRemovedBuiltInTemplateMessage(templateId) {
64
+ return `Built-in template "${templateId}" was removed. Use --template persistence --persistence-policy ${templateId === "data" ? "public" : "authenticated"} instead.`;
65
+ }
@@ -0,0 +1,36 @@
1
+ export declare function resolvePackageRoot(startDir: string): string;
2
+ export declare const PROJECT_TOOLS_PACKAGE_ROOT: string;
3
+ export declare const TEMPLATE_ROOT: string;
4
+ export declare const SHARED_TEMPLATE_ROOT: string;
5
+ export declare const SHARED_BASE_TEMPLATE_ROOT: string;
6
+ export declare const SHARED_COMPOUND_TEMPLATE_ROOT: string;
7
+ export declare const SHARED_PERSISTENCE_TEMPLATE_ROOT: string;
8
+ export declare const SHARED_PRESET_TEMPLATE_ROOT: string;
9
+ export declare const SHARED_REST_HELPER_TEMPLATE_ROOT: string;
10
+ export declare const SHARED_MIGRATION_UI_TEMPLATE_ROOT: string;
11
+ /**
12
+ * Shared workspace template overlay root used by workspace scaffolding flows.
13
+ */
14
+ export declare const SHARED_WORKSPACE_TEMPLATE_ROOT: string;
15
+ export declare const SHARED_TEST_PRESET_TEMPLATE_ROOT: string;
16
+ export declare const SHARED_WP_ENV_PRESET_TEMPLATE_ROOT: string;
17
+ export declare const BUILTIN_TEMPLATE_IDS: readonly ["basic", "interactivity", "persistence", "compound"];
18
+ export type BuiltInTemplateId = (typeof BUILTIN_TEMPLATE_IDS)[number];
19
+ export type PersistencePolicy = "authenticated" | "public";
20
+ export interface TemplateDefinition {
21
+ id: BuiltInTemplateId;
22
+ description: string;
23
+ defaultCategory: string;
24
+ features: string[];
25
+ templateDir: string;
26
+ }
27
+ export declare const TEMPLATE_REGISTRY: readonly TemplateDefinition[];
28
+ export declare const TEMPLATE_IDS: BuiltInTemplateId[];
29
+ export declare function isBuiltInTemplateId(templateId: string): templateId is BuiltInTemplateId;
30
+ export declare function listTemplates(): readonly TemplateDefinition[];
31
+ export declare function getTemplateById(templateId: string): TemplateDefinition;
32
+ export declare function getTemplateSelectOptions(): Array<{
33
+ label: string;
34
+ value: TemplateDefinition["id"];
35
+ hint: string;
36
+ }>;
@@ -0,0 +1,94 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { getBuiltInTemplateMetadataDefaults } from "./template-defaults.js";
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ export function resolvePackageRoot(startDir) {
7
+ let currentDir = startDir;
8
+ while (true) {
9
+ const packageJsonPath = path.join(currentDir, "package.json");
10
+ if (fs.existsSync(packageJsonPath)) {
11
+ try {
12
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
13
+ if (packageJson.name === "@wp-typia/project-tools") {
14
+ return currentDir;
15
+ }
16
+ }
17
+ catch {
18
+ // Ignore malformed package.json while walking upward.
19
+ }
20
+ }
21
+ const parentDir = path.dirname(currentDir);
22
+ if (parentDir === currentDir) {
23
+ throw new Error("Unable to resolve the @wp-typia/project-tools package root.");
24
+ }
25
+ currentDir = parentDir;
26
+ }
27
+ }
28
+ export const PROJECT_TOOLS_PACKAGE_ROOT = resolvePackageRoot(__dirname);
29
+ export const TEMPLATE_ROOT = path.join(PROJECT_TOOLS_PACKAGE_ROOT, "templates");
30
+ export const SHARED_TEMPLATE_ROOT = path.join(TEMPLATE_ROOT, "_shared");
31
+ export const SHARED_BASE_TEMPLATE_ROOT = path.join(SHARED_TEMPLATE_ROOT, "base");
32
+ export const SHARED_COMPOUND_TEMPLATE_ROOT = path.join(SHARED_TEMPLATE_ROOT, "compound");
33
+ export const SHARED_PERSISTENCE_TEMPLATE_ROOT = path.join(SHARED_TEMPLATE_ROOT, "persistence");
34
+ export const SHARED_PRESET_TEMPLATE_ROOT = path.join(SHARED_TEMPLATE_ROOT, "presets");
35
+ export const SHARED_REST_HELPER_TEMPLATE_ROOT = path.join(SHARED_TEMPLATE_ROOT, "rest-helpers");
36
+ export const SHARED_MIGRATION_UI_TEMPLATE_ROOT = path.join(SHARED_TEMPLATE_ROOT, "migration-ui");
37
+ /**
38
+ * Shared workspace template overlay root used by workspace scaffolding flows.
39
+ */
40
+ export const SHARED_WORKSPACE_TEMPLATE_ROOT = path.join(SHARED_TEMPLATE_ROOT, "workspace");
41
+ export const SHARED_TEST_PRESET_TEMPLATE_ROOT = path.join(SHARED_PRESET_TEMPLATE_ROOT, "test-preset");
42
+ export const SHARED_WP_ENV_PRESET_TEMPLATE_ROOT = path.join(SHARED_PRESET_TEMPLATE_ROOT, "wp-env");
43
+ export const BUILTIN_TEMPLATE_IDS = ["basic", "interactivity", "persistence", "compound"];
44
+ export const TEMPLATE_REGISTRY = Object.freeze([
45
+ {
46
+ id: "basic",
47
+ description: "A lightweight WordPress block with Typia validation",
48
+ defaultCategory: getBuiltInTemplateMetadataDefaults("basic").category,
49
+ features: ["Type-safe attributes", "Runtime validation", "Minimal setup"],
50
+ templateDir: path.join(TEMPLATE_ROOT, "basic"),
51
+ },
52
+ {
53
+ id: "interactivity",
54
+ description: "An interactive WordPress block with Typia validation and Interactivity API",
55
+ defaultCategory: getBuiltInTemplateMetadataDefaults("interactivity").category,
56
+ features: ["Interactivity API", "Client-side state", "Event handling"],
57
+ templateDir: path.join(TEMPLATE_ROOT, "interactivity"),
58
+ },
59
+ {
60
+ id: "persistence",
61
+ description: "A persistence-aware WordPress block with Typia validation, typed REST contracts, and selectable public or authenticated write policies",
62
+ defaultCategory: getBuiltInTemplateMetadataDefaults("persistence").category,
63
+ features: ["Interactivity API", "Typed REST client", "Schema sync", "Persistence policies"],
64
+ templateDir: path.join(TEMPLATE_ROOT, "persistence"),
65
+ },
66
+ {
67
+ id: "compound",
68
+ description: "A parent-and-child WordPress block scaffold with InnerBlocks, optional persistence wiring, and hidden implementation child blocks",
69
+ defaultCategory: getBuiltInTemplateMetadataDefaults("compound").category,
70
+ features: ["InnerBlocks", "Hidden child blocks", "Optional persistence layer"],
71
+ templateDir: path.join(TEMPLATE_ROOT, "compound"),
72
+ },
73
+ ]);
74
+ export const TEMPLATE_IDS = TEMPLATE_REGISTRY.map((template) => template.id);
75
+ export function isBuiltInTemplateId(templateId) {
76
+ return BUILTIN_TEMPLATE_IDS.includes(templateId);
77
+ }
78
+ export function listTemplates() {
79
+ return TEMPLATE_REGISTRY;
80
+ }
81
+ export function getTemplateById(templateId) {
82
+ const template = TEMPLATE_REGISTRY.find((entry) => entry.id === templateId);
83
+ if (!template) {
84
+ throw new Error(`Unknown template "${templateId}". Expected one of: ${TEMPLATE_IDS.join(", ")}`);
85
+ }
86
+ return template;
87
+ }
88
+ export function getTemplateSelectOptions() {
89
+ return TEMPLATE_REGISTRY.map((template) => ({
90
+ label: template.id,
91
+ value: template.id,
92
+ hint: template.features.join(", "),
93
+ }));
94
+ }
@@ -0,0 +1,24 @@
1
+ import fs from "node:fs";
2
+ export type TemplateRenderView = Record<string, unknown>;
3
+ /**
4
+ * Optional controls for raw directory copies.
5
+ *
6
+ * The filter runs for each discovered entry before copying. Returning `false`
7
+ * skips only that entry; when the skipped entry is a directory, the subtree is
8
+ * skipped as well.
9
+ */
10
+ export interface CopyRawDirectoryOptions {
11
+ /**
12
+ * Predicate that decides whether a discovered entry should be copied.
13
+ *
14
+ * Throwing or rejecting aborts the copy and bubbles to the caller.
15
+ */
16
+ filter?: (sourcePath: string, targetPath: string, entry: fs.Dirent) => boolean | Promise<boolean>;
17
+ }
18
+ /**
19
+ * Recursively copies a directory tree without rendering template contents.
20
+ */
21
+ export declare function copyRawDirectory(sourceDir: string, targetDir: string, options?: CopyRawDirectoryOptions): Promise<void>;
22
+ export declare function copyRenderedDirectory(sourceDir: string, targetDir: string, view: TemplateRenderView): Promise<void>;
23
+ export declare function copyInterpolatedDirectory(sourceDir: string, targetDir: string, view: Record<string, string>): Promise<void>;
24
+ export declare function pathExists(targetPath: string): boolean;
@@ -0,0 +1,113 @@
1
+ /// <reference path="./external-template-modules.d.ts" />
2
+ import fs from "node:fs";
3
+ import { promises as fsp } from "node:fs";
4
+ import path from "node:path";
5
+ import Mustache from "mustache";
6
+ const BINARY_EXTENSIONS = new Set([
7
+ ".avif",
8
+ ".bmp",
9
+ ".gif",
10
+ ".ico",
11
+ ".jpeg",
12
+ ".jpg",
13
+ ".otf",
14
+ ".pdf",
15
+ ".png",
16
+ ".svg",
17
+ ".ttf",
18
+ ".webp",
19
+ ".woff",
20
+ ".woff2",
21
+ ]);
22
+ Mustache.escape = (value) => value;
23
+ function renderMustacheTemplateString(template, view) {
24
+ return Mustache.render(template, view);
25
+ }
26
+ function escapeRegExp(value) {
27
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
28
+ }
29
+ function renderInterpolatedString(template, view) {
30
+ return Object.entries(view).reduce((output, [key, value]) => output.replace(new RegExp(`{{${escapeRegExp(key)}}}`, "g"), value), template);
31
+ }
32
+ function isBinaryTemplateFile(filePath) {
33
+ return BINARY_EXTENSIONS.has(path.extname(filePath).toLowerCase());
34
+ }
35
+ function resolveRenderedPath(targetDir, destinationName) {
36
+ const resolvedTargetDir = path.resolve(targetDir);
37
+ const resolvedDestinationPath = path.resolve(resolvedTargetDir, destinationName);
38
+ const relativePath = path.relative(resolvedTargetDir, resolvedDestinationPath);
39
+ if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
40
+ throw new Error(`Rendered template path escapes target directory: ${destinationName}`);
41
+ }
42
+ return resolvedDestinationPath;
43
+ }
44
+ /**
45
+ * Recursively copies a directory tree without rendering template contents.
46
+ */
47
+ export async function copyRawDirectory(sourceDir, targetDir, options = {}) {
48
+ await fsp.mkdir(targetDir, { recursive: true });
49
+ for (const entry of await fsp.readdir(sourceDir, { withFileTypes: true })) {
50
+ const sourcePath = path.join(sourceDir, entry.name);
51
+ const targetPath = path.join(targetDir, entry.name);
52
+ if (options.filter && !(await options.filter(sourcePath, targetPath, entry))) {
53
+ continue;
54
+ }
55
+ if (entry.isDirectory()) {
56
+ await copyRawDirectory(sourcePath, targetPath, options);
57
+ continue;
58
+ }
59
+ await fsp.mkdir(path.dirname(targetPath), { recursive: true });
60
+ await fsp.copyFile(sourcePath, targetPath);
61
+ }
62
+ }
63
+ export async function copyRenderedDirectory(sourceDir, targetDir, view) {
64
+ const entries = await fsp.readdir(sourceDir, { withFileTypes: true });
65
+ for (const entry of entries) {
66
+ const sourcePath = path.join(sourceDir, entry.name);
67
+ const destinationNameTemplate = entry.name.endsWith(".mustache")
68
+ ? entry.name.slice(0, -".mustache".length)
69
+ : entry.name;
70
+ const destinationName = renderMustacheTemplateString(destinationNameTemplate, view);
71
+ const destinationPath = resolveRenderedPath(targetDir, destinationName);
72
+ if (entry.isDirectory()) {
73
+ await fsp.mkdir(destinationPath, { recursive: true });
74
+ await copyRenderedDirectory(sourcePath, destinationPath, view);
75
+ continue;
76
+ }
77
+ if (isBinaryTemplateFile(sourcePath)) {
78
+ await fsp.mkdir(path.dirname(destinationPath), { recursive: true });
79
+ await fsp.copyFile(sourcePath, destinationPath);
80
+ continue;
81
+ }
82
+ const content = await fsp.readFile(sourcePath, "utf8");
83
+ await fsp.mkdir(path.dirname(destinationPath), { recursive: true });
84
+ await fsp.writeFile(destinationPath, renderMustacheTemplateString(content, view), "utf8");
85
+ }
86
+ }
87
+ export async function copyInterpolatedDirectory(sourceDir, targetDir, view) {
88
+ const entries = await fsp.readdir(sourceDir, { withFileTypes: true });
89
+ for (const entry of entries) {
90
+ const sourcePath = path.join(sourceDir, entry.name);
91
+ const destinationNameTemplate = entry.name.endsWith(".mustache")
92
+ ? entry.name.slice(0, -".mustache".length)
93
+ : entry.name;
94
+ const destinationName = renderInterpolatedString(destinationNameTemplate, view);
95
+ const destinationPath = resolveRenderedPath(targetDir, destinationName);
96
+ if (entry.isDirectory()) {
97
+ await fsp.mkdir(destinationPath, { recursive: true });
98
+ await copyInterpolatedDirectory(sourcePath, destinationPath, view);
99
+ continue;
100
+ }
101
+ if (isBinaryTemplateFile(sourcePath)) {
102
+ await fsp.mkdir(path.dirname(destinationPath), { recursive: true });
103
+ await fsp.copyFile(sourcePath, destinationPath);
104
+ continue;
105
+ }
106
+ const content = await fsp.readFile(sourcePath, "utf8");
107
+ await fsp.mkdir(path.dirname(destinationPath), { recursive: true });
108
+ await fsp.writeFile(destinationPath, renderInterpolatedString(content, view), "utf8");
109
+ }
110
+ }
111
+ export function pathExists(targetPath) {
112
+ return fs.existsSync(targetPath);
113
+ }
@@ -0,0 +1,71 @@
1
+ type TemplateSourceFormat = "wp-typia" | "create-block-external" | "create-block-subset";
2
+ /**
3
+ * Public template variables exposed to external template seeds before wp-typia
4
+ * normalizes them into a scaffold project.
5
+ */
6
+ export interface TemplateVariableContext extends Record<string, unknown> {
7
+ /** Version string for `@wp-typia/api-client` used in generated dependencies. */
8
+ apiClientPackageVersion: string;
9
+ /** Version string for `@wp-typia/block-runtime` used in generated dependencies. */
10
+ blockRuntimePackageVersion: string;
11
+ /** Version string for `@wp-typia/block-types` used in generated dependencies. */
12
+ blockTypesPackageVersion: string;
13
+ /** PascalCase block type name derived from the scaffold slug. */
14
+ pascalCase: string;
15
+ /** Snake_case PHP symbol prefix used for generated functions, constants, and keys. */
16
+ phpPrefix: string;
17
+ /** Human-readable block title. */
18
+ title: string;
19
+ /** Human-readable project or block description. */
20
+ description: string;
21
+ /** Keyword string derived from the slug for generated block metadata. */
22
+ keyword: string;
23
+ /** Block namespace used in generated block names such as `namespace/slug`. */
24
+ namespace: string;
25
+ /** Kebab-case scaffold slug used for package names, paths, and block slugs. */
26
+ slug: string;
27
+ /** Kebab-case text domain used for generated i18n strings and plugin headers. */
28
+ textDomain: string;
29
+ }
30
+ export interface ResolvedTemplateSource {
31
+ id: string;
32
+ defaultCategory: string;
33
+ description: string;
34
+ features: string[];
35
+ format: TemplateSourceFormat;
36
+ isOfficialWorkspaceTemplate?: boolean;
37
+ templateDir: string;
38
+ cleanup?: () => Promise<void>;
39
+ selectedVariant?: string | null;
40
+ warnings?: string[];
41
+ }
42
+ interface GitHubTemplateLocator {
43
+ owner: string;
44
+ repo: string;
45
+ ref: string | null;
46
+ sourcePath: string;
47
+ }
48
+ interface NpmTemplateLocator {
49
+ fetchSpec: string;
50
+ name: string;
51
+ raw: string;
52
+ rawSpec: string;
53
+ type: string;
54
+ }
55
+ type RemoteTemplateLocator = {
56
+ kind: "github";
57
+ locator: GitHubTemplateLocator;
58
+ } | {
59
+ kind: "npm";
60
+ locator: NpmTemplateLocator;
61
+ } | {
62
+ kind: "path";
63
+ templatePath: string;
64
+ };
65
+ export declare function parseGitHubTemplateLocator(templateId: string): GitHubTemplateLocator | null;
66
+ export declare function parseNpmTemplateLocator(templateId: string): NpmTemplateLocator | null;
67
+ export declare function parseTemplateLocator(templateId: string): RemoteTemplateLocator;
68
+ export declare function resolveTemplateSource(templateId: string, cwd: string, variables: {
69
+ [key: string]: string;
70
+ }, variant?: string): Promise<ResolvedTemplateSource>;
71
+ export {};