@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,61 @@
1
+ import { getBuiltInTemplateLayerDirs } from "./template-builtins.js";
2
+ import { getTemplateById, getTemplateSelectOptions, isBuiltInTemplateId, listTemplates, } from "./template-registry.js";
3
+ /**
4
+ * Format one line of template list output for a built-in template.
5
+ *
6
+ * @param template Template metadata including `id` and `description`.
7
+ * @returns One-line summary text for `templates list`.
8
+ */
9
+ export function formatTemplateSummary(template) {
10
+ return `${template.id.padEnd(14)} ${template.description}`;
11
+ }
12
+ /**
13
+ * Format the feature hint line shown under a template summary.
14
+ *
15
+ * @param template Template metadata including the `features` list.
16
+ * @returns Indented feature text for CLI list output.
17
+ */
18
+ export function formatTemplateFeatures(template) {
19
+ return ` ${template.features.join(" • ")}`;
20
+ }
21
+ /**
22
+ * Format the detailed template description for `templates inspect`.
23
+ *
24
+ * This expands special layer combinations for the `persistence` and `compound`
25
+ * templates and returns a multi-line block including category, overlay path,
26
+ * resolved layers, and feature labels.
27
+ *
28
+ * @param template Template metadata including `id`, `defaultCategory`,
29
+ * `templateDir`, and `features`.
30
+ * @returns Multi-line template details text for CLI output.
31
+ */
32
+ export function formatTemplateDetails(template) {
33
+ const layers = template.id === "persistence"
34
+ ? [
35
+ `authenticated: ${getBuiltInTemplateLayerDirs(template.id, { persistencePolicy: "authenticated" }).join(" -> ")}`,
36
+ `public: ${getBuiltInTemplateLayerDirs(template.id, { persistencePolicy: "public" }).join(" -> ")}`,
37
+ ]
38
+ : template.id === "compound"
39
+ ? [
40
+ `pure: ${getBuiltInTemplateLayerDirs(template.id).join(" -> ")}`,
41
+ `authenticated+persistence: ${getBuiltInTemplateLayerDirs(template.id, {
42
+ persistenceEnabled: true,
43
+ persistencePolicy: "authenticated",
44
+ }).join(" -> ")}`,
45
+ `public+persistence: ${getBuiltInTemplateLayerDirs(template.id, {
46
+ persistenceEnabled: true,
47
+ persistencePolicy: "public",
48
+ }).join(" -> ")}`,
49
+ ]
50
+ : [getBuiltInTemplateLayerDirs(template.id).join(" -> ")];
51
+ return [
52
+ template.id,
53
+ template.description,
54
+ `Category: ${template.defaultCategory}`,
55
+ `Overlay path: ${template.templateDir}`,
56
+ `Layers: ${layers.join("\n")}`,
57
+ `Features: ${template.features.join(", ")}`,
58
+ ].join("\n");
59
+ }
60
+ export { getTemplateById, getTemplateSelectOptions, listTemplates };
61
+ export { isBuiltInTemplateId };
@@ -0,0 +1,9 @@
1
+ export { scaffoldProject, collectScaffoldAnswers, getDefaultAnswers, getTemplateVariables, resolvePackageManagerId, resolveTemplateId, } from "./scaffold.js";
2
+ export { formatMigrationHelpText, parseMigrationArgs, runMigrationCommand, } from "./migrations.js";
3
+ export { manifestAttributeToJsonSchema, projectJsonSchemaDocument, manifestToJsonSchema, manifestToOpenApi, normalizeEndpointAuthDefinition, } from "./schema-core.js";
4
+ export { buildCompoundChildStarterManifestDocument, getStarterManifestFiles, stringifyStarterManifest, } from "./starter-manifests.js";
5
+ export type { EndpointAuthIntent, EndpointOpenApiAuthMode, EndpointOpenApiContractDocument, EndpointOpenApiDocumentOptions, EndpointOpenApiEndpointDefinition, EndpointOpenApiMethod, EndpointWordPressAuthDefinition, EndpointWordPressAuthMechanism, JsonSchemaDocument, JsonSchemaProjectionProfile, JsonSchemaObject, NormalizedEndpointAuthDefinition, OpenApiDocument, OpenApiInfo, OpenApiOperation, OpenApiParameter, OpenApiPathItem, OpenApiSecurityScheme, } from "./schema-core.js";
6
+ export { PACKAGE_MANAGER_IDS, PACKAGE_MANAGERS, formatPackageExecCommand, formatInstallCommand, formatRunScript, getPackageManager, getPackageManagerSelectOptions, transformPackageManagerText, } from "./package-managers.js";
7
+ export { TEMPLATE_IDS, TEMPLATE_REGISTRY, getTemplateById, getTemplateSelectOptions, listTemplates, } from "./template-registry.js";
8
+ export { createAddPlaceholderMessage, formatAddHelpText, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getNextSteps, getOptionalOnboarding, runAddBlockCommand, runDoctor, runScaffoldFlow, } from "./cli-core.js";
9
+ export type { DoctorCheck, ReadlinePrompt } from "./cli-core.js";
@@ -0,0 +1,7 @@
1
+ export { scaffoldProject, collectScaffoldAnswers, getDefaultAnswers, getTemplateVariables, resolvePackageManagerId, resolveTemplateId, } from "./scaffold.js";
2
+ export { formatMigrationHelpText, parseMigrationArgs, runMigrationCommand, } from "./migrations.js";
3
+ export { manifestAttributeToJsonSchema, projectJsonSchemaDocument, manifestToJsonSchema, manifestToOpenApi, normalizeEndpointAuthDefinition, } from "./schema-core.js";
4
+ export { buildCompoundChildStarterManifestDocument, getStarterManifestFiles, stringifyStarterManifest, } from "./starter-manifests.js";
5
+ export { PACKAGE_MANAGER_IDS, PACKAGE_MANAGERS, formatPackageExecCommand, formatInstallCommand, formatRunScript, getPackageManager, getPackageManagerSelectOptions, transformPackageManagerText, } from "./package-managers.js";
6
+ export { TEMPLATE_IDS, TEMPLATE_REGISTRY, getTemplateById, getTemplateSelectOptions, listTemplates, } from "./template-registry.js";
7
+ export { createAddPlaceholderMessage, formatAddHelpText, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getNextSteps, getOptionalOnboarding, runAddBlockCommand, runDoctor, runScaffoldFlow, } from "./cli-core.js";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Create a deep clone of a JSON-serializable value.
3
+ *
4
+ * @param value JSON-compatible data to clone.
5
+ * @returns A deep-cloned copy created with `JSON.parse(JSON.stringify(...))`.
6
+ *
7
+ * Values that are not JSON-serializable, such as functions, `undefined`,
8
+ * `BigInt`, class instances, and `Date` objects, are not preserved faithfully.
9
+ */
10
+ export declare function cloneJsonValue<T>(value: T): T;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Create a deep clone of a JSON-serializable value.
3
+ *
4
+ * @param value JSON-compatible data to clone.
5
+ * @returns A deep-cloned copy created with `JSON.parse(JSON.stringify(...))`.
6
+ *
7
+ * Values that are not JSON-serializable, such as functions, `undefined`,
8
+ * `BigInt`, class instances, and `Date` objects, are not preserved faithfully.
9
+ */
10
+ export function cloneJsonValue(value) {
11
+ return JSON.parse(JSON.stringify(value));
12
+ }
@@ -0,0 +1,26 @@
1
+ import { type PackageManagerId } from "./package-managers.js";
2
+ import type { ScaffoldTemplateVariables } from "./scaffold.js";
3
+ interface LocalDevPresetOptions {
4
+ compoundPersistenceEnabled?: boolean;
5
+ packageManager: PackageManagerId;
6
+ projectDir: string;
7
+ templateId: string;
8
+ variables: ScaffoldTemplateVariables;
9
+ withTestPreset?: boolean;
10
+ withWpEnv?: boolean;
11
+ }
12
+ /**
13
+ * Copies opt-in local development preset files into a generated project.
14
+ */
15
+ export declare function applyLocalDevPresetFiles({ projectDir, variables, withTestPreset, withWpEnv, }: Pick<LocalDevPresetOptions, "projectDir" | "variables" | "withTestPreset" | "withWpEnv">): Promise<void>;
16
+ /**
17
+ * Adds generated-project watch scripts and preset-specific dependencies to
18
+ * `package.json`.
19
+ */
20
+ export declare function applyGeneratedProjectDxPackageJson({ compoundPersistenceEnabled, packageManager, projectDir, templateId, withTestPreset, withWpEnv, }: Omit<LocalDevPresetOptions, "variables">): Promise<void>;
21
+ /**
22
+ * Returns the recommended first-run development command for the given
23
+ * scaffolded template.
24
+ */
25
+ export declare function getPrimaryDevelopmentScript(templateId: string): "dev" | "start";
26
+ export {};
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Helpers for the generated-project local development experience.
3
+ *
4
+ * These utilities apply optional preset files and watch-oriented package
5
+ * scripts after a scaffold has been rendered.
6
+ */
7
+ import fs from "node:fs";
8
+ import { promises as fsp } from "node:fs";
9
+ import path from "node:path";
10
+ import { formatRunScript, } from "./package-managers.js";
11
+ import { SHARED_TEST_PRESET_TEMPLATE_ROOT, SHARED_WP_ENV_PRESET_TEMPLATE_ROOT, } from "./template-registry.js";
12
+ import { copyInterpolatedDirectory } from "./template-render.js";
13
+ function templateHasPersistenceSync(templateId, compoundPersistenceEnabled) {
14
+ return templateId === "persistence" || (templateId === "compound" && compoundPersistenceEnabled);
15
+ }
16
+ function getWatchSyncTypesScript(packageManager, templateId) {
17
+ if (templateId === "compound") {
18
+ return `chokidar "src/blocks/**/types.ts" "scripts/block-config.ts" --debounce 200 -c "${formatRunScript(packageManager, "sync-types")}"`;
19
+ }
20
+ return `chokidar "src/types.ts" --debounce 200 -c "${formatRunScript(packageManager, "sync-types")}"`;
21
+ }
22
+ function getWatchSyncRestScript(packageManager, templateId) {
23
+ if (templateId === "compound") {
24
+ return `chokidar "src/blocks/**/api-types.ts" "scripts/block-config.ts" --debounce 200 -c "${formatRunScript(packageManager, "sync-rest")}"`;
25
+ }
26
+ return `chokidar "src/api-types.ts" --debounce 200 -c "${formatRunScript(packageManager, "sync-rest")}"`;
27
+ }
28
+ function getDevScript(packageManager, compoundPersistenceEnabled, templateId) {
29
+ const syncProcesses = [
30
+ `"${formatRunScript(packageManager, "watch:sync-types")}"`,
31
+ ];
32
+ const names = ["sync-types"];
33
+ const colors = ["yellow"];
34
+ if (templateHasPersistenceSync(templateId, compoundPersistenceEnabled)) {
35
+ syncProcesses.push(`"${formatRunScript(packageManager, "watch:sync-rest")}"`);
36
+ names.push("sync-rest");
37
+ colors.push("magenta");
38
+ }
39
+ syncProcesses.push(`"${formatRunScript(packageManager, "start:editor")}"`);
40
+ names.push("editor");
41
+ colors.push("blue");
42
+ return `concurrently -k -n ${names.join(",")} -c ${colors.join(",")} ${syncProcesses.join(" ")}`;
43
+ }
44
+ async function mutatePackageJson(projectDir, mutate) {
45
+ const packageJsonPath = path.join(projectDir, "package.json");
46
+ const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, "utf8"));
47
+ mutate(packageJson);
48
+ await fsp.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, "\t")}\n`, "utf8");
49
+ }
50
+ async function appendGitignoreLines(projectDir, lines) {
51
+ if (lines.length === 0) {
52
+ return;
53
+ }
54
+ const gitignorePath = path.join(projectDir, ".gitignore");
55
+ const current = fs.existsSync(gitignorePath)
56
+ ? await fsp.readFile(gitignorePath, "utf8")
57
+ : "";
58
+ const missingLines = lines.filter((line) => !current.includes(`${line}\n`) && !current.endsWith(line));
59
+ if (missingLines.length === 0) {
60
+ return;
61
+ }
62
+ const suffix = current.endsWith("\n") ? "" : "\n";
63
+ await fsp.writeFile(gitignorePath, `${current}${suffix}${missingLines.join("\n")}\n`, "utf8");
64
+ }
65
+ /**
66
+ * Copies opt-in local development preset files into a generated project.
67
+ */
68
+ export async function applyLocalDevPresetFiles({ projectDir, variables, withTestPreset = false, withWpEnv = false, }) {
69
+ if (withWpEnv) {
70
+ await copyInterpolatedDirectory(SHARED_WP_ENV_PRESET_TEMPLATE_ROOT, projectDir, variables);
71
+ }
72
+ if (withTestPreset) {
73
+ await copyInterpolatedDirectory(SHARED_TEST_PRESET_TEMPLATE_ROOT, projectDir, variables);
74
+ await appendGitignoreLines(projectDir, ["playwright-report/", "test-results/"]);
75
+ }
76
+ }
77
+ /**
78
+ * Adds generated-project watch scripts and preset-specific dependencies to
79
+ * `package.json`.
80
+ */
81
+ export async function applyGeneratedProjectDxPackageJson({ compoundPersistenceEnabled = false, packageManager, projectDir, templateId, withTestPreset = false, withWpEnv = false, }) {
82
+ const hasPersistenceSync = templateHasPersistenceSync(templateId, compoundPersistenceEnabled);
83
+ await mutatePackageJson(projectDir, (packageJson) => {
84
+ packageJson.devDependencies = {
85
+ ...(packageJson.devDependencies ?? {}),
86
+ "chokidar-cli": "^3.0.0",
87
+ concurrently: "^9.0.1",
88
+ };
89
+ if (withWpEnv || withTestPreset) {
90
+ packageJson.devDependencies["@wordpress/env"] = "^11.2.0";
91
+ }
92
+ if (withTestPreset) {
93
+ packageJson.devDependencies["@playwright/test"] = "^1.54.2";
94
+ }
95
+ const scripts = {
96
+ ...(packageJson.scripts ?? {}),
97
+ };
98
+ scripts["start:editor"] = "wp-scripts start --experimental-modules";
99
+ scripts["watch:sync-types"] = getWatchSyncTypesScript(packageManager, templateId);
100
+ if (hasPersistenceSync) {
101
+ scripts["watch:sync-rest"] = getWatchSyncRestScript(packageManager, templateId);
102
+ }
103
+ scripts.dev = getDevScript(packageManager, compoundPersistenceEnabled, templateId);
104
+ if (withWpEnv) {
105
+ scripts["wp-env:start"] = "wp-env start";
106
+ scripts["wp-env:stop"] = "wp-env stop";
107
+ scripts["wp-env:reset"] = "wp-env destroy all && wp-env start";
108
+ }
109
+ if (withTestPreset) {
110
+ scripts["wp-env:start:test"] = "wp-env start --config=.wp-env.test.json";
111
+ scripts["wp-env:stop:test"] = "wp-env stop --config=.wp-env.test.json";
112
+ scripts["wp-env:reset:test"] =
113
+ "wp-env destroy all --config=.wp-env.test.json && wp-env start --config=.wp-env.test.json";
114
+ scripts["wp-env:wait:test"] =
115
+ "node scripts/wait-for-wp-env.mjs http://localhost:8889 180000 .wp-env.test.json";
116
+ scripts["test:e2e"] = `${formatRunScript(packageManager, "wp-env:start:test")} && ${formatRunScript(packageManager, "wp-env:wait:test")} && playwright test`;
117
+ }
118
+ packageJson.scripts = scripts;
119
+ });
120
+ }
121
+ /**
122
+ * Returns the recommended first-run development command for the given
123
+ * scaffolded template.
124
+ */
125
+ export function getPrimaryDevelopmentScript(templateId) {
126
+ return templateId === "basic" ||
127
+ templateId === "interactivity" ||
128
+ templateId === "persistence" ||
129
+ templateId === "compound"
130
+ ? "dev"
131
+ : "start";
132
+ }
@@ -0,0 +1,11 @@
1
+ import ts from "typescript";
2
+ export interface AnalysisContext {
3
+ allowedExternalPackages: Set<string>;
4
+ checker: ts.TypeChecker;
5
+ packageNameCache: Map<string, string | null>;
6
+ projectRoot: string;
7
+ program: ts.Program;
8
+ recursionGuard: Set<string>;
9
+ }
10
+ export declare function getTaggedSyncBlockMetadataFailureCode(error: Error): "typescript-diagnostic" | undefined;
11
+ export declare function createAnalysisContext(projectRoot: string, typesFilePath: string): AnalysisContext;
@@ -0,0 +1,285 @@
1
+ import { createHash } from "node:crypto";
2
+ import * as path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import ts from "typescript";
5
+ class LruCache {
6
+ constructor(maxEntries) {
7
+ this.maxEntries = maxEntries;
8
+ this.entries = new Map();
9
+ }
10
+ get(key) {
11
+ const value = this.entries.get(key);
12
+ if (value === undefined) {
13
+ return undefined;
14
+ }
15
+ this.entries.delete(key);
16
+ this.entries.set(key, value);
17
+ return value;
18
+ }
19
+ set(key, value) {
20
+ if (this.entries.has(key)) {
21
+ this.entries.delete(key);
22
+ }
23
+ this.entries.set(key, value);
24
+ if (this.entries.size <= this.maxEntries) {
25
+ return;
26
+ }
27
+ const oldestKey = this.entries.keys().next().value;
28
+ if (oldestKey !== undefined) {
29
+ this.entries.delete(oldestKey);
30
+ }
31
+ }
32
+ }
33
+ const DEFAULT_ALLOWED_EXTERNAL_PACKAGES = ["@wp-typia/block-types"];
34
+ const ANALYSIS_PROGRAM_CACHE_MAX_ENTRIES = 20;
35
+ const TYPESCRIPT_LIB_DIRECTORY = path.dirname(ts.getDefaultLibFilePath({}));
36
+ const RUNTIME_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
37
+ const SYNC_BLOCK_METADATA_FAILURE_CODE = Symbol("sync-block-metadata-failure-code");
38
+ const analysisProgramCache = new LruCache(ANALYSIS_PROGRAM_CACHE_MAX_ENTRIES);
39
+ function tagMetadataDiagnosticError(error) {
40
+ error[SYNC_BLOCK_METADATA_FAILURE_CODE] = "typescript-diagnostic";
41
+ return error;
42
+ }
43
+ export function getTaggedSyncBlockMetadataFailureCode(error) {
44
+ return error[SYNC_BLOCK_METADATA_FAILURE_CODE];
45
+ }
46
+ function isProjectLocalSourceFile(filePath, projectRoot) {
47
+ if (filePath.startsWith(TYPESCRIPT_LIB_DIRECTORY)) {
48
+ return false;
49
+ }
50
+ if (filePath.includes(`${path.sep}node_modules${path.sep}`)) {
51
+ return false;
52
+ }
53
+ return !path.relative(projectRoot, filePath).startsWith("..");
54
+ }
55
+ function collectSourceFileModuleSpecifiers(sourceFile) {
56
+ const moduleSpecifiers = [];
57
+ for (const statement of sourceFile.statements) {
58
+ if ((ts.isImportDeclaration(statement) ||
59
+ ts.isExportDeclaration(statement)) &&
60
+ statement.moduleSpecifier &&
61
+ ts.isStringLiteralLike(statement.moduleSpecifier)) {
62
+ moduleSpecifiers.push(statement.moduleSpecifier.text);
63
+ }
64
+ }
65
+ ts.forEachChild(sourceFile, (node) => {
66
+ if (ts.isImportTypeNode(node) &&
67
+ ts.isLiteralTypeNode(node.argument) &&
68
+ ts.isStringLiteral(node.argument.literal)) {
69
+ moduleSpecifiers.push(node.argument.literal.text);
70
+ }
71
+ });
72
+ return moduleSpecifiers;
73
+ }
74
+ function collectReferencedLocalSourceFiles(program, entryFilePath, compilerOptions, projectRoot) {
75
+ const visited = new Set();
76
+ const queue = [entryFilePath];
77
+ while (queue.length > 0) {
78
+ const filePath = queue.pop();
79
+ if (filePath === undefined ||
80
+ visited.has(filePath) ||
81
+ !isProjectLocalSourceFile(filePath, projectRoot)) {
82
+ continue;
83
+ }
84
+ visited.add(filePath);
85
+ const sourceFile = program.getSourceFile(filePath);
86
+ if (sourceFile === undefined) {
87
+ continue;
88
+ }
89
+ for (const moduleSpecifier of collectSourceFileModuleSpecifiers(sourceFile)) {
90
+ const resolved = ts.resolveModuleName(moduleSpecifier, filePath, compilerOptions, ts.sys).resolvedModule;
91
+ const resolvedFileName = resolved?.resolvedFileName;
92
+ if (resolvedFileName &&
93
+ isProjectLocalSourceFile(resolvedFileName, projectRoot)) {
94
+ queue.push(resolvedFileName);
95
+ }
96
+ }
97
+ }
98
+ return visited;
99
+ }
100
+ function stableSerializeAnalysisValue(value) {
101
+ if (value === undefined) {
102
+ return '"__undefined__"';
103
+ }
104
+ if (value === null || typeof value !== "object") {
105
+ return JSON.stringify(value);
106
+ }
107
+ if (Array.isArray(value)) {
108
+ return `[${value.map((entry) => stableSerializeAnalysisValue(entry)).join(",")}]`;
109
+ }
110
+ return `{${Object.entries(value)
111
+ .sort(([left], [right]) => left.localeCompare(right))
112
+ .map(([key, entry]) => `${JSON.stringify(key)}:${stableSerializeAnalysisValue(entry)}`)
113
+ .join(",")}}`;
114
+ }
115
+ function buildAnalysisProgramStructureKey(projectRoot, typesFilePath, { compilerOptions, configPath, rootNames, typiaTagsAugmentationPath, }) {
116
+ return stableSerializeAnalysisValue({
117
+ compilerOptions,
118
+ configPath,
119
+ projectRoot,
120
+ rootNames: [...rootNames].sort(),
121
+ typiaTagsAugmentationPath,
122
+ typesFilePath,
123
+ });
124
+ }
125
+ function createAnalysisProgramContentFingerprint(filePaths, onMissingFile = "throw") {
126
+ const hash = createHash("sha1");
127
+ const fingerprintPaths = [...new Set(filePaths)].sort();
128
+ for (const filePath of fingerprintPaths) {
129
+ const fileContents = ts.sys.readFile(filePath);
130
+ if (fileContents === undefined) {
131
+ if (onMissingFile === "return-null") {
132
+ return null;
133
+ }
134
+ if (onMissingFile === "hash-missing") {
135
+ hash.update(filePath);
136
+ hash.update("\0");
137
+ hash.update("__missing__");
138
+ hash.update("\0");
139
+ continue;
140
+ }
141
+ throw new Error(`Unable to read metadata analysis dependency: ${filePath}`);
142
+ }
143
+ hash.update(filePath);
144
+ hash.update("\0");
145
+ hash.update(fileContents);
146
+ hash.update("\0");
147
+ }
148
+ return hash.digest("hex");
149
+ }
150
+ function getAnalysisProgramDependencyPaths(program, configPath) {
151
+ const sourceFilePaths = program
152
+ .getSourceFiles()
153
+ .map((sourceFile) => sourceFile.fileName)
154
+ .filter((filePath) => !filePath.startsWith(TYPESCRIPT_LIB_DIRECTORY));
155
+ const dependencyPaths = new Set(sourceFilePaths);
156
+ for (const filePath of sourceFilePaths) {
157
+ let currentDir = path.dirname(filePath);
158
+ while (true) {
159
+ dependencyPaths.add(path.join(currentDir, "package.json"));
160
+ const parentDir = path.dirname(currentDir);
161
+ if (parentDir === currentDir) {
162
+ break;
163
+ }
164
+ currentDir = parentDir;
165
+ }
166
+ }
167
+ if (configPath) {
168
+ dependencyPaths.add(configPath);
169
+ }
170
+ return [...dependencyPaths].sort();
171
+ }
172
+ function resolveAnalysisProgramInputs(projectRoot, typesFilePath) {
173
+ const configPath = ts.findConfigFile(projectRoot, ts.sys.fileExists, "tsconfig.json");
174
+ const compilerOptions = {
175
+ allowJs: false,
176
+ esModuleInterop: true,
177
+ module: ts.ModuleKind.NodeNext,
178
+ moduleResolution: ts.ModuleResolutionKind.NodeNext,
179
+ resolveJsonModule: true,
180
+ skipLibCheck: true,
181
+ target: ts.ScriptTarget.ES2022,
182
+ };
183
+ let rootNames = [typesFilePath];
184
+ const typiaTagsAugmentationPath = resolveTypiaTagsAugmentationPath();
185
+ if (configPath !== undefined) {
186
+ const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
187
+ if (configFile.error) {
188
+ throw formatDiagnosticError(configFile.error);
189
+ }
190
+ const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configPath), compilerOptions, configPath);
191
+ if (parsed.errors.length > 0) {
192
+ throw formatDiagnosticError(parsed.errors[0]);
193
+ }
194
+ rootNames = parsed.fileNames.includes(typesFilePath)
195
+ ? parsed.fileNames
196
+ : [...parsed.fileNames, typesFilePath];
197
+ if (typiaTagsAugmentationPath &&
198
+ !rootNames.includes(typiaTagsAugmentationPath)) {
199
+ rootNames = [...rootNames, typiaTagsAugmentationPath];
200
+ }
201
+ Object.assign(compilerOptions, parsed.options);
202
+ }
203
+ else if (typiaTagsAugmentationPath) {
204
+ rootNames = [...rootNames, typiaTagsAugmentationPath];
205
+ }
206
+ const structureKey = buildAnalysisProgramStructureKey(projectRoot, typesFilePath, {
207
+ compilerOptions,
208
+ configPath: configPath ?? null,
209
+ rootNames,
210
+ typiaTagsAugmentationPath,
211
+ });
212
+ return {
213
+ compilerOptions,
214
+ configPath: configPath ?? null,
215
+ rootNames,
216
+ structureKey,
217
+ typiaTagsAugmentationPath,
218
+ };
219
+ }
220
+ export function createAnalysisContext(projectRoot, typesFilePath) {
221
+ const analysisInputs = resolveAnalysisProgramInputs(projectRoot, typesFilePath);
222
+ const cachedAnalysis = analysisProgramCache.get(analysisInputs.structureKey);
223
+ if (cachedAnalysis) {
224
+ const currentDependencyFingerprint = createAnalysisProgramContentFingerprint(cachedAnalysis.dependencyPaths, "hash-missing");
225
+ if (currentDependencyFingerprint !== null &&
226
+ currentDependencyFingerprint === cachedAnalysis.dependencyFingerprint) {
227
+ return {
228
+ allowedExternalPackages: new Set(DEFAULT_ALLOWED_EXTERNAL_PACKAGES),
229
+ checker: cachedAnalysis.checker,
230
+ packageNameCache: new Map(),
231
+ projectRoot,
232
+ program: cachedAnalysis.program,
233
+ recursionGuard: new Set(),
234
+ };
235
+ }
236
+ }
237
+ const program = ts.createProgram({
238
+ oldProgram: cachedAnalysis?.program,
239
+ options: analysisInputs.compilerOptions,
240
+ rootNames: analysisInputs.rootNames,
241
+ });
242
+ const diagnostics = ts.getPreEmitDiagnostics(program);
243
+ const localSourceFiles = collectReferencedLocalSourceFiles(program, typesFilePath, analysisInputs.compilerOptions, projectRoot);
244
+ const blockingDiagnostic = diagnostics.find((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error &&
245
+ diagnostic.file !== undefined &&
246
+ localSourceFiles.has(diagnostic.file.fileName));
247
+ if (blockingDiagnostic) {
248
+ throw formatDiagnosticError(blockingDiagnostic);
249
+ }
250
+ const checker = program.getTypeChecker();
251
+ const dependencyPaths = getAnalysisProgramDependencyPaths(program, analysisInputs.configPath);
252
+ const dependencyFingerprint = createAnalysisProgramContentFingerprint(dependencyPaths, "hash-missing");
253
+ if (dependencyFingerprint === null) {
254
+ throw new Error("Unable to fingerprint metadata analysis dependencies.");
255
+ }
256
+ analysisProgramCache.set(analysisInputs.structureKey, {
257
+ checker,
258
+ dependencyFingerprint,
259
+ dependencyPaths,
260
+ program,
261
+ });
262
+ return {
263
+ allowedExternalPackages: new Set(DEFAULT_ALLOWED_EXTERNAL_PACKAGES),
264
+ checker,
265
+ packageNameCache: new Map(),
266
+ projectRoot,
267
+ program,
268
+ recursionGuard: new Set(),
269
+ };
270
+ }
271
+ function resolveTypiaTagsAugmentationPath() {
272
+ const candidates = [
273
+ path.join(RUNTIME_DIRECTORY, "typia-tags.d.ts"),
274
+ path.join(RUNTIME_DIRECTORY, "typia-tags.ts"),
275
+ ];
276
+ for (const candidate of candidates) {
277
+ if (ts.sys.fileExists(candidate)) {
278
+ return candidate;
279
+ }
280
+ }
281
+ return null;
282
+ }
283
+ function formatDiagnosticError(diagnostic) {
284
+ return tagMetadataDiagnosticError(new Error(ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")));
285
+ }
@@ -0,0 +1,84 @@
1
+ export type JsonPrimitive = string | number | boolean | null;
2
+ export type JsonValue = JsonPrimitive | JsonValue[] | {
3
+ [key: string]: JsonValue;
4
+ };
5
+ export type AttributeKind = "string" | "number" | "boolean" | "array" | "object" | "union";
6
+ export type WordPressAttributeKind = "string" | "number" | "boolean" | "array" | "object";
7
+ export type WordPressAttributeSource = "html" | "text" | "rich-text";
8
+ export interface AttributeConstraints {
9
+ exclusiveMaximum: number | null;
10
+ exclusiveMinimum: number | null;
11
+ format: string | null;
12
+ maxLength: number | null;
13
+ maxItems: number | null;
14
+ maximum: number | null;
15
+ minLength: number | null;
16
+ minItems: number | null;
17
+ minimum: number | null;
18
+ multipleOf: number | null;
19
+ pattern: string | null;
20
+ typeTag: string | null;
21
+ }
22
+ export interface AttributeNode {
23
+ constraints: AttributeConstraints;
24
+ defaultValue?: JsonValue;
25
+ enumValues: Array<string | number | boolean> | null;
26
+ items?: AttributeNode;
27
+ kind: AttributeKind;
28
+ path: string;
29
+ properties?: Record<string, AttributeNode>;
30
+ required: boolean;
31
+ union?: AttributeUnion | null;
32
+ wp: {
33
+ selector: string | null;
34
+ source: WordPressAttributeSource | null;
35
+ };
36
+ }
37
+ export interface AttributeUnion {
38
+ branches: Record<string, AttributeNode>;
39
+ discriminator: string;
40
+ }
41
+ export interface BlockJsonAttribute {
42
+ default?: JsonValue;
43
+ enum?: Array<string | number | boolean>;
44
+ selector?: string;
45
+ source?: WordPressAttributeSource;
46
+ type: WordPressAttributeKind;
47
+ }
48
+ export interface ManifestAttribute {
49
+ typia: {
50
+ constraints: AttributeConstraints;
51
+ defaultValue: JsonValue | null;
52
+ hasDefault: boolean;
53
+ };
54
+ ts: {
55
+ items: ManifestAttribute | null;
56
+ kind: AttributeKind;
57
+ properties: Record<string, ManifestAttribute> | null;
58
+ required: boolean;
59
+ union: ManifestUnion | null;
60
+ };
61
+ wp: {
62
+ defaultValue: JsonValue | null;
63
+ enum: Array<string | number | boolean> | null;
64
+ hasDefault: boolean;
65
+ selector?: string | null;
66
+ source?: WordPressAttributeSource | null;
67
+ type: WordPressAttributeKind;
68
+ };
69
+ }
70
+ export interface ManifestUnion {
71
+ branches: Record<string, ManifestAttribute>;
72
+ discriminator: string;
73
+ }
74
+ export interface ManifestDocument {
75
+ attributes: Record<string, ManifestAttribute>;
76
+ manifestVersion: 2;
77
+ sourceType: string;
78
+ }
79
+ export declare function defaultAttributeConstraints(): AttributeConstraints;
80
+ export declare function getWordPressKind(node: AttributeNode): WordPressAttributeKind;
81
+ export declare function baseNode(kind: AttributeKind, pathLabel: string): AttributeNode;
82
+ export declare function withRequired(node: AttributeNode, required: boolean): AttributeNode;
83
+ export declare function cloneUnion(union: AttributeUnion): AttributeUnion;
84
+ export declare function cloneProperties(properties: Record<string, AttributeNode>): Record<string, AttributeNode>;