@wp-typia/project-tools 0.23.1 → 0.24.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 (152) hide show
  1. package/dist/runtime/built-in-block-non-ts-basic-artifacts.d.ts +9 -0
  2. package/dist/runtime/built-in-block-non-ts-basic-artifacts.js +84 -0
  3. package/dist/runtime/built-in-block-non-ts-compound-artifacts.d.ts +9 -0
  4. package/dist/runtime/built-in-block-non-ts-compound-artifacts.js +36 -0
  5. package/dist/runtime/built-in-block-non-ts-compound-templates.d.ts +23 -0
  6. package/dist/runtime/built-in-block-non-ts-compound-templates.js +453 -0
  7. package/dist/runtime/built-in-block-non-ts-family-artifacts.d.ts +8 -26
  8. package/dist/runtime/built-in-block-non-ts-family-artifacts.js +8 -1034
  9. package/dist/runtime/built-in-block-non-ts-interactivity-artifacts.d.ts +9 -0
  10. package/dist/runtime/built-in-block-non-ts-interactivity-artifacts.js +83 -0
  11. package/dist/runtime/built-in-block-non-ts-persistence-artifacts.d.ts +9 -0
  12. package/dist/runtime/built-in-block-non-ts-persistence-artifacts.js +33 -0
  13. package/dist/runtime/built-in-block-non-ts-persistence-templates.d.ts +23 -0
  14. package/dist/runtime/built-in-block-non-ts-persistence-templates.js +395 -0
  15. package/dist/runtime/cli-add-collision.js +8 -0
  16. package/dist/runtime/cli-add-help.js +10 -7
  17. package/dist/runtime/cli-add-kind-ids.d.ts +1 -1
  18. package/dist/runtime/cli-add-kind-ids.js +1 -0
  19. package/dist/runtime/cli-add-types.d.ts +28 -1
  20. package/dist/runtime/cli-add-types.js +2 -0
  21. package/dist/runtime/cli-add-workspace-ability-anchors.d.ts +24 -0
  22. package/dist/runtime/cli-add-workspace-ability-anchors.js +294 -0
  23. package/dist/runtime/cli-add-workspace-ability-registry.d.ts +10 -0
  24. package/dist/runtime/cli-add-workspace-ability-registry.js +51 -0
  25. package/dist/runtime/cli-add-workspace-ability-scaffold.d.ts +1 -1
  26. package/dist/runtime/cli-add-workspace-ability-scaffold.js +5 -311
  27. package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +1 -1
  28. package/dist/runtime/cli-add-workspace-ai-anchors.d.ts +4 -4
  29. package/dist/runtime/cli-add-workspace-ai-anchors.js +4 -232
  30. package/dist/runtime/cli-add-workspace-ai-scaffold.js +4 -2
  31. package/dist/runtime/cli-add-workspace-ai-source-emitters.d.ts +1 -4
  32. package/dist/runtime/cli-add-workspace-ai-source-emitters.js +1 -145
  33. package/dist/runtime/cli-add-workspace-ai-sync-rest-anchors.d.ts +5 -0
  34. package/dist/runtime/cli-add-workspace-ai-sync-rest-anchors.js +236 -0
  35. package/dist/runtime/cli-add-workspace-ai-sync-script-source.d.ts +4 -0
  36. package/dist/runtime/cli-add-workspace-ai-sync-script-source.js +145 -0
  37. package/dist/runtime/cli-add-workspace-assets.d.ts +6 -63
  38. package/dist/runtime/cli-add-workspace-assets.js +6 -950
  39. package/dist/runtime/cli-add-workspace-binding-source-anchors.d.ts +23 -0
  40. package/dist/runtime/cli-add-workspace-binding-source-anchors.js +112 -0
  41. package/dist/runtime/cli-add-workspace-binding-source-source-emitters.d.ts +33 -0
  42. package/dist/runtime/cli-add-workspace-binding-source-source-emitters.js +436 -0
  43. package/dist/runtime/cli-add-workspace-binding-source-types.d.ts +20 -0
  44. package/dist/runtime/cli-add-workspace-binding-source-types.js +1 -0
  45. package/dist/runtime/cli-add-workspace-binding-source.d.ts +40 -0
  46. package/dist/runtime/cli-add-workspace-binding-source.js +275 -0
  47. package/dist/runtime/cli-add-workspace-block-style.d.ts +22 -0
  48. package/dist/runtime/cli-add-workspace-block-style.js +148 -0
  49. package/dist/runtime/cli-add-workspace-block-transform.d.ts +32 -0
  50. package/dist/runtime/cli-add-workspace-block-transform.js +197 -0
  51. package/dist/runtime/cli-add-workspace-contract.js +1 -1
  52. package/dist/runtime/cli-add-workspace-core-variation.d.ts +20 -0
  53. package/dist/runtime/cli-add-workspace-core-variation.js +322 -0
  54. package/dist/runtime/cli-add-workspace-editor-plugin-anchors.d.ts +37 -0
  55. package/dist/runtime/cli-add-workspace-editor-plugin-anchors.js +206 -0
  56. package/dist/runtime/cli-add-workspace-editor-plugin-source-emitters.d.ts +47 -0
  57. package/dist/runtime/cli-add-workspace-editor-plugin-source-emitters.js +219 -0
  58. package/dist/runtime/cli-add-workspace-editor-plugin.d.ts +22 -0
  59. package/dist/runtime/cli-add-workspace-editor-plugin.js +78 -0
  60. package/dist/runtime/cli-add-workspace-hooked-block.d.ts +23 -0
  61. package/dist/runtime/cli-add-workspace-hooked-block.js +57 -0
  62. package/dist/runtime/cli-add-workspace-integration-env-files.d.ts +33 -0
  63. package/dist/runtime/cli-add-workspace-integration-env-files.js +65 -0
  64. package/dist/runtime/cli-add-workspace-integration-env-package-json.d.ts +38 -0
  65. package/dist/runtime/cli-add-workspace-integration-env-package-json.js +122 -0
  66. package/dist/runtime/cli-add-workspace-integration-env-source-emitters.d.ts +44 -0
  67. package/dist/runtime/cli-add-workspace-integration-env-source-emitters.js +262 -0
  68. package/dist/runtime/cli-add-workspace-integration-env.js +5 -345
  69. package/dist/runtime/cli-add-workspace-pattern-anchors.d.ts +10 -0
  70. package/dist/runtime/cli-add-workspace-pattern-anchors.js +95 -0
  71. package/dist/runtime/cli-add-workspace-pattern-options.d.ts +20 -0
  72. package/dist/runtime/cli-add-workspace-pattern-options.js +113 -0
  73. package/dist/runtime/cli-add-workspace-pattern-source-emitters.d.ts +20 -0
  74. package/dist/runtime/cli-add-workspace-pattern-source-emitters.js +57 -0
  75. package/dist/runtime/cli-add-workspace-pattern.d.ts +42 -0
  76. package/dist/runtime/cli-add-workspace-pattern.js +99 -0
  77. package/dist/runtime/cli-add-workspace-post-meta.js +1 -1
  78. package/dist/runtime/cli-add-workspace-registration-hooks.d.ts +50 -0
  79. package/dist/runtime/cli-add-workspace-registration-hooks.js +162 -0
  80. package/dist/runtime/cli-add-workspace-rest-anchors.d.ts +6 -9
  81. package/dist/runtime/cli-add-workspace-rest-anchors.js +6 -466
  82. package/dist/runtime/cli-add-workspace-rest-bootstrap-anchors.d.ts +17 -0
  83. package/dist/runtime/cli-add-workspace-rest-bootstrap-anchors.js +108 -0
  84. package/dist/runtime/cli-add-workspace-rest-contract-sync-anchors.d.ts +9 -0
  85. package/dist/runtime/cli-add-workspace-rest-contract-sync-anchors.js +142 -0
  86. package/dist/runtime/cli-add-workspace-rest-generated-source-emitters.d.ts +51 -0
  87. package/dist/runtime/cli-add-workspace-rest-generated-source-emitters.js +415 -0
  88. package/dist/runtime/cli-add-workspace-rest-generated.js +5 -3
  89. package/dist/runtime/cli-add-workspace-rest-manual-source-emitters.d.ts +80 -0
  90. package/dist/runtime/cli-add-workspace-rest-manual-source-emitters.js +238 -0
  91. package/dist/runtime/cli-add-workspace-rest-manual.js +3 -16
  92. package/dist/runtime/cli-add-workspace-rest-php-templates.d.ts +1 -7
  93. package/dist/runtime/cli-add-workspace-rest-php-templates.js +3 -322
  94. package/dist/runtime/cli-add-workspace-rest-resource-php-routing-template.d.ts +33 -0
  95. package/dist/runtime/cli-add-workspace-rest-resource-php-routing-template.js +145 -0
  96. package/dist/runtime/cli-add-workspace-rest-resource-sync-anchors.d.ts +9 -0
  97. package/dist/runtime/cli-add-workspace-rest-resource-sync-anchors.js +162 -0
  98. package/dist/runtime/cli-add-workspace-rest-schema-helper-php-template.d.ts +7 -0
  99. package/dist/runtime/cli-add-workspace-rest-schema-helper-php-template.js +193 -0
  100. package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +5 -99
  101. package/dist/runtime/cli-add-workspace-rest-source-emitters.js +5 -663
  102. package/dist/runtime/cli-add-workspace-rest-source-utils.d.ts +17 -0
  103. package/dist/runtime/cli-add-workspace-rest-source-utils.js +50 -0
  104. package/dist/runtime/cli-add-workspace-rest-sync-script-shared.d.ts +56 -0
  105. package/dist/runtime/cli-add-workspace-rest-sync-script-shared.js +122 -0
  106. package/dist/runtime/cli-add-workspace-rest-types.d.ts +3 -3
  107. package/dist/runtime/cli-add-workspace-variation.d.ts +22 -0
  108. package/dist/runtime/cli-add-workspace-variation.js +162 -0
  109. package/dist/runtime/cli-add-workspace.d.ts +42 -107
  110. package/dist/runtime/cli-add-workspace.js +42 -674
  111. package/dist/runtime/cli-add.d.ts +3 -3
  112. package/dist/runtime/cli-add.js +2 -2
  113. package/dist/runtime/cli-core.d.ts +2 -1
  114. package/dist/runtime/cli-core.js +1 -1
  115. package/dist/runtime/cli-doctor-workspace-bindings.js +59 -0
  116. package/dist/runtime/cli-doctor-workspace-block-addons.js +33 -5
  117. package/dist/runtime/cli-doctor.d.ts +2 -0
  118. package/dist/runtime/cli-doctor.js +13 -2
  119. package/dist/runtime/cli-help.js +6 -4
  120. package/dist/runtime/index.d.ts +5 -2
  121. package/dist/runtime/index.js +4 -2
  122. package/dist/runtime/local-dev-presets.js +2 -1
  123. package/dist/runtime/package-versions.d.ts +1 -0
  124. package/dist/runtime/package-versions.js +10 -2
  125. package/dist/runtime/pattern-catalog.d.ts +122 -0
  126. package/dist/runtime/pattern-catalog.js +471 -0
  127. package/dist/runtime/post-meta-binding-fields.d.ts +46 -0
  128. package/dist/runtime/post-meta-binding-fields.js +135 -0
  129. package/dist/runtime/typia-llm-json-schema.d.ts +24 -0
  130. package/dist/runtime/typia-llm-json-schema.js +33 -0
  131. package/dist/runtime/typia-llm-openapi-constraints.d.ts +20 -0
  132. package/dist/runtime/typia-llm-openapi-constraints.js +254 -0
  133. package/dist/runtime/typia-llm-projection.d.ts +25 -0
  134. package/dist/runtime/typia-llm-projection.js +58 -0
  135. package/dist/runtime/typia-llm-render.d.ts +21 -0
  136. package/dist/runtime/typia-llm-render.js +252 -0
  137. package/dist/runtime/typia-llm-sync.d.ts +10 -0
  138. package/dist/runtime/typia-llm-sync.js +63 -0
  139. package/dist/runtime/typia-llm-types.d.ts +197 -0
  140. package/dist/runtime/typia-llm-types.js +1 -0
  141. package/dist/runtime/typia-llm.d.ts +9 -255
  142. package/dist/runtime/typia-llm.js +5 -634
  143. package/dist/runtime/workspace-inventory-mutations.js +13 -0
  144. package/dist/runtime/workspace-inventory-section-descriptors.js +9 -1
  145. package/dist/runtime/workspace-inventory-templates.d.ts +2 -2
  146. package/dist/runtime/workspace-inventory-templates.js +9 -1
  147. package/dist/runtime/workspace-inventory-types.d.ts +9 -1
  148. package/package.json +8 -3
  149. package/templates/_shared/compound/core/scripts/block-config.ts.mustache +22 -0
  150. package/templates/_shared/compound/core/scripts/sync-types-to-block-json.ts.mustache +103 -2
  151. package/templates/_shared/compound/core/src/inner-blocks-templates.ts.mustache +13 -0
  152. package/templates/_shared/compound/persistence/scripts/block-config.ts.mustache +22 -1
@@ -1,953 +1,9 @@
1
- import { promises as fsp } from "node:fs";
2
- import path from "node:path";
3
- import { syncBlockMetadata, } from "@wp-typia/block-runtime/metadata-core";
4
- import ts from "typescript";
5
- import { resolveWorkspaceProject, } from "./workspace-project.js";
6
- import { appendWorkspaceInventoryEntries, readWorkspaceInventoryAsync, } from "./workspace-inventory.js";
7
- import { toPascalCase, toTitleCase } from "./string-case.js";
8
- import { findPhpFunctionRange, hasPhpFunctionDefinition, quotePhpString, replacePhpFunctionDefinition, } from "./php-utils.js";
9
- import { assertBindingSourceDoesNotExist, assertEditorPluginDoesNotExist, assertPatternDoesNotExist, assertValidEditorPluginSlot, assertValidGeneratedSlug, getWorkspaceBootstrapPath, normalizeBlockSlug, patchFile, quoteTsString, resolveWorkspaceBlock, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
10
- import { appendPhpSnippetBeforeClosingTag, insertPhpSnippetBeforeWorkspaceAnchors, } from "./cli-add-workspace-mutation.js";
11
- import { resolveWorkspaceBlockTargetName } from "./block-targets.js";
12
- import { pathExists, readOptionalUtf8File } from "./fs-async.js";
13
- import { normalizeOptionalCliString } from "./cli-validation.js";
14
- import { getPropertyNameText } from "./ts-property-names.js";
15
- const PATTERN_BOOTSTRAP_CATEGORY = "register_block_pattern_category";
16
- const BINDING_SOURCE_SERVER_GLOB = "/src/bindings/*/server.php";
17
- const BINDING_SOURCE_EDITOR_SCRIPT = "build/bindings/index.js";
18
- const BINDING_SOURCE_EDITOR_ASSET = "build/bindings/index.asset.php";
19
- const EDITOR_PLUGIN_EDITOR_SCRIPT = "build/editor-plugins/index.js";
20
- const EDITOR_PLUGIN_EDITOR_ASSET = "build/editor-plugins/index.asset.php";
21
- const EDITOR_PLUGIN_EDITOR_STYLE = "build/editor-plugins/style-index.css";
22
- const EDITOR_PLUGIN_EDITOR_STYLE_RTL = "build/editor-plugins/style-index-rtl.css";
23
- const BINDING_ATTRIBUTE_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_-]*$/u;
24
- function buildPatternConfigEntry(patternSlug) {
25
- return [
26
- "\t{",
27
- `\t\tfile: ${quoteTsString(`src/patterns/${patternSlug}.php`)},`,
28
- `\t\tslug: ${quoteTsString(patternSlug)},`,
29
- "\t},",
30
- ].join("\n");
31
- }
32
- function buildBindingSourceConfigEntry(bindingSourceSlug, target) {
33
- return [
34
- "\t{",
35
- ...(target ? [`\t\tattribute: ${quoteTsString(target.attributeName)},`] : []),
36
- ...(target ? [`\t\tblock: ${quoteTsString(target.blockSlug)},`] : []),
37
- `\t\teditorFile: ${quoteTsString(`src/bindings/${bindingSourceSlug}/editor.ts`)},`,
38
- `\t\tserverFile: ${quoteTsString(`src/bindings/${bindingSourceSlug}/server.php`)},`,
39
- `\t\tslug: ${quoteTsString(bindingSourceSlug)},`,
40
- "\t},",
41
- ].join("\n");
42
- }
43
- function assertValidBindingAttributeName(attributeName) {
44
- const trimmed = attributeName.trim();
45
- if (!trimmed) {
46
- throw new Error("`wp-typia add binding-source` requires --attribute <attribute> to include a value when --block is provided.");
47
- }
48
- if (!BINDING_ATTRIBUTE_NAME_PATTERN.test(trimmed)) {
49
- throw new Error(`Binding attribute "${attributeName}" must start with a letter and use only letters, numbers, underscores, or hyphens.`);
50
- }
51
- return trimmed;
52
- }
53
- function buildEditorPluginConfigEntry(editorPluginSlug, slot) {
54
- return [
55
- "\t{",
56
- `\t\tfile: ${quoteTsString(`src/editor-plugins/${editorPluginSlug}/index.tsx`)},`,
57
- `\t\tslug: ${quoteTsString(editorPluginSlug)},`,
58
- `\t\tslot: ${quoteTsString(slot)},`,
59
- "\t},",
60
- ].join("\n");
61
- }
62
- function buildPatternSource(patternSlug, namespace, textDomain) {
63
- const patternTitle = toTitleCase(patternSlug);
64
- return `<?php
65
- if ( ! defined( 'ABSPATH' ) ) {
66
- \treturn;
67
- }
68
-
69
- register_block_pattern(
70
- \t'${namespace}/${patternSlug}',
71
- \tarray(
72
- \t\t'title' => __( ${JSON.stringify(patternTitle)}, '${textDomain}' ),
73
- \t\t'description' => __( ${JSON.stringify(`A starter pattern for ${patternTitle}.`)}, '${textDomain}' ),
74
- \t\t'categories' => array( '${namespace}' ),
75
- \t\t'content' => '<!-- wp:paragraph --><p>' . esc_html__( 'Describe this pattern here.', '${textDomain}' ) . '</p><!-- /wp:paragraph -->',
76
- \t)
77
- );
78
- `;
79
- }
80
- function buildBindingSourceServerSource(bindingSourceSlug, phpPrefix, namespace, textDomain, target) {
81
- const bindingSourceTitle = toTitleCase(bindingSourceSlug);
82
- const bindingSourcePhpId = bindingSourceSlug.replace(/-/g, "_");
83
- const bindingSourceValueFunctionName = `${phpPrefix}_${bindingSourcePhpId}_binding_source_values`;
84
- const bindingSourceResolveFunctionName = `${phpPrefix}_${bindingSourcePhpId}_resolve_binding_source_value`;
85
- const bindingSourceSupportedAttributesFunctionName = `${phpPrefix}_${bindingSourcePhpId}_supported_binding_attributes`;
86
- const starterValue = `${bindingSourceTitle} starter value`;
87
- const supportedAttributesSource = target
88
- ? `
89
- if ( ! function_exists( '${bindingSourceSupportedAttributesFunctionName}' ) ) {
90
- \tfunction ${bindingSourceSupportedAttributesFunctionName}( array $supported_attributes ) : array {
91
- \t\tif ( ! in_array( ${quotePhpString(target.attributeName)}, $supported_attributes, true ) ) {
92
- \t\t\t$supported_attributes[] = ${quotePhpString(target.attributeName)};
93
- \t\t}
94
-
95
- \t\treturn $supported_attributes;
96
- \t}
97
- }
98
- `
99
- : "";
100
- const supportedAttributesHook = target
101
- ? `
102
- if ( function_exists( '${bindingSourceSupportedAttributesFunctionName}' ) ) {
103
- \tadd_filter(
104
- \t\t${quotePhpString(`block_bindings_supported_attributes_${namespace}/${target.blockSlug}`)},
105
- \t\t${quotePhpString(bindingSourceSupportedAttributesFunctionName)}
106
- \t);
107
- }
108
- `
109
- : "";
110
- return `<?php
111
- if ( ! defined( 'ABSPATH' ) ) {
112
- \treturn;
113
- }
114
-
115
- if ( ! function_exists( 'register_block_bindings_source' ) ) {
116
- \treturn;
117
- }
118
-
119
- if ( ! function_exists( '${bindingSourceValueFunctionName}' ) ) {
120
- \tfunction ${bindingSourceValueFunctionName}() : array {
121
- \t\treturn array(
122
- \t\t\t${quotePhpString(bindingSourceSlug)} => ${quotePhpString(starterValue)},
123
- \t\t);
124
- \t}
125
- }
126
-
127
- if ( ! function_exists( '${bindingSourceResolveFunctionName}' ) ) {
128
- \tfunction ${bindingSourceResolveFunctionName}( array $source_args ) : string {
129
- \t\t$field = isset( $source_args['field'] ) && is_string( $source_args['field'] )
130
- \t\t\t? $source_args['field']
131
- \t\t\t: '${bindingSourceSlug}';
132
- \t\t$binding_source_values = ${bindingSourceValueFunctionName}();
133
- \t\t$value = $binding_source_values[ $field ] ?? '';
134
-
135
- \t\treturn is_string( $value ) ? $value : '';
136
- \t}
137
- }
138
- ${supportedAttributesSource}
139
-
140
- register_block_bindings_source(
141
- \t${quotePhpString(`${namespace}/${bindingSourceSlug}`)},
142
- \tarray(
143
- \t\t'label' => __( ${quotePhpString(bindingSourceTitle)}, ${quotePhpString(textDomain)} ),
144
- \t\t'get_value_callback' => ${quotePhpString(bindingSourceResolveFunctionName)},
145
- \t)
146
- );
147
- ${supportedAttributesHook}`;
148
- }
149
- function buildBindingSourceEditorSource(bindingSourceSlug, namespace, textDomain, target) {
150
- const bindingSourceTitle = toTitleCase(bindingSourceSlug);
151
- const starterValue = `${bindingSourceTitle} starter value`;
152
- const bindingSourceName = `${namespace}/${bindingSourceSlug}`;
153
- const targetSource = target
154
- ? `
155
- export const BINDING_SOURCE_TARGET = {
156
- \tattribute: ${quoteTsString(target.attributeName)},
157
- \tblock: ${quoteTsString(`${namespace}/${target.blockSlug}`)},
158
- \tfield: ${quoteTsString(bindingSourceSlug)},
159
- \tsource: ${quoteTsString(bindingSourceName)},
160
- } as const;
161
- `
162
- : "";
163
- return `import { registerBlockBindingsSource } from '@wordpress/blocks';
164
- import { __ } from '@wordpress/i18n';
165
-
166
- interface BindingSourceRegistration {
167
- \targs?: {
168
- \t\tfield?: string;
169
- \t};
170
- }
171
-
172
- const BINDING_SOURCE_VALUES: Record<string, string> = {
173
- \t${quoteTsString(bindingSourceSlug)}: ${quoteTsString(starterValue)},
174
- };
175
- ${targetSource}
176
-
177
- function resolveBindingSourceValue( field: string ): string {
178
- \treturn BINDING_SOURCE_VALUES[ field ] ?? '';
179
- }
180
-
181
- registerBlockBindingsSource( {
182
- \tname: ${quoteTsString(bindingSourceName)},
183
- \tlabel: __( ${quoteTsString(bindingSourceTitle)}, ${quoteTsString(textDomain)} ),
184
- \tgetFieldsList() {
185
- \t\treturn [
186
- \t\t\t{
187
- \t\t\t\tlabel: __( ${quoteTsString(bindingSourceTitle)}, ${quoteTsString(textDomain)} ),
188
- \t\t\t\ttype: 'string',
189
- \t\t\t\targs: {
190
- \t\t\t\t\tfield: ${quoteTsString(bindingSourceSlug)},
191
- \t\t\t\t},
192
- \t\t\t},
193
- \t\t];
194
- \t},
195
- \tgetValues( { bindings } ) {
196
- \t\tconst values: Record<string, string> = {};
197
- \t\tfor ( const [ attributeName, binding ] of Object.entries(
198
- \t\t\tbindings as Record<string, BindingSourceRegistration>
199
- \t\t) ) {
200
- \t\t\tconst field =
201
- \t\t\t\ttypeof binding?.args?.field === 'string'
202
- \t\t\t\t\t? binding.args.field
203
- \t\t\t\t\t: ${quoteTsString(bindingSourceSlug)};
204
- \t\t\tvalues[ attributeName ] = resolveBindingSourceValue( field );
205
- \t\t}
206
- \t\treturn values;
207
- \t},
208
- } );
209
- `;
210
- }
211
- function resolveBindingTarget(options, namespace) {
212
- const blockName = normalizeOptionalCliString(options.blockName);
213
- const attributeName = normalizeOptionalCliString(options.attributeName);
214
- const hasBlock = blockName !== undefined;
215
- const hasAttribute = attributeName !== undefined;
216
- if (!hasBlock && !hasAttribute) {
217
- return undefined;
218
- }
219
- if (!hasBlock || !hasAttribute) {
220
- throw new Error("`wp-typia add binding-source` requires --block and --attribute to be provided together.");
221
- }
222
- const targetBlock = resolveWorkspaceBlockTargetName(blockName ?? "", namespace, {
223
- empty: () => "`wp-typia add binding-source` requires --block <block-slug|namespace/block-slug> to include a value when --attribute is provided.",
224
- emptySegment: (input) => `Binding target block "${input}" must use <block-slug> or <namespace/block-slug> format without empty path segments.`,
225
- invalidFormat: (input) => `Binding target block "${input}" must use <block-slug> or <namespace/block-slug> format.`,
226
- namespaceMismatch: (input, actualNamespace, expectedNamespace) => `Binding target block "${input}" uses namespace "${actualNamespace}". Expected "${expectedNamespace}".`,
227
- });
228
- return {
229
- attributeName: assertValidBindingAttributeName(attributeName ?? ""),
230
- blockSlug: targetBlock.blockSlug,
231
- };
232
- }
233
- function formatBindingAttributeTypeMember(attributeName) {
234
- const propertyName = /^[A-Za-z_$][\w$]*$/u.test(attributeName)
235
- ? attributeName
236
- : JSON.stringify(attributeName);
237
- return [
238
- "\t/**",
239
- "\t * Starter string attribute declared for WordPress Block Bindings.",
240
- "\t */",
241
- `\t${propertyName}?: string;`,
242
- ].join("\n");
243
- }
244
- function getInterfaceDeclaration(source, interfaceName) {
245
- const sourceFile = ts.createSourceFile("types.ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
246
- let declaration;
247
- const visit = (node) => {
248
- if (ts.isInterfaceDeclaration(node) && node.name.text === interfaceName) {
249
- declaration = node;
250
- return true;
251
- }
252
- return ts.forEachChild(node, (child) => (visit(child) ? true : undefined)) ?? false;
253
- };
254
- visit(sourceFile);
255
- return declaration ? { declaration, sourceFile } : undefined;
256
- }
257
- function interfaceHasAttributeMember(declaration, attributeName) {
258
- return declaration.members.some((member) => ts.isPropertySignature(member) &&
259
- member.name !== undefined &&
260
- getPropertyNameText(member.name) === attributeName);
261
- }
262
- function insertBindingAttributeTypeMember(source, declaration, attributeName) {
263
- let closeBracePosition = declaration.end - 1;
264
- while (closeBracePosition > declaration.pos && source[closeBracePosition] !== "}") {
265
- closeBracePosition -= 1;
266
- }
267
- if (source[closeBracePosition] !== "}") {
268
- throw new Error("Unable to locate the target interface closing brace.");
269
- }
270
- const lineEnding = source.includes("\r\n") ? "\r\n" : "\n";
271
- const beforeCloseBrace = source.slice(0, closeBracePosition);
272
- const afterCloseBrace = source.slice(closeBracePosition);
273
- const memberSource = formatBindingAttributeTypeMember(attributeName)
274
- .split("\n")
275
- .join(lineEnding);
276
- const prefix = beforeCloseBrace.endsWith(lineEnding) ? "" : lineEnding;
277
- return `${beforeCloseBrace}${prefix}${memberSource}${lineEnding}${afterCloseBrace}`;
278
- }
279
- async function ensureBindingTargetBlockAttributeType(projectDir, block, target) {
280
- if (!block.attributeTypeName) {
281
- throw new Error(`Workspace block "${block.slug}" must include attributeTypeName in scripts/block-config.ts before it can receive binding-source targets.`);
282
- }
283
- const typesPath = path.join(projectDir, block.typesFile);
284
- const source = await fsp.readFile(typesPath, "utf8");
285
- const targetInterface = getInterfaceDeclaration(source, block.attributeTypeName);
286
- if (!targetInterface) {
287
- throw new Error(`Unable to locate interface ${block.attributeTypeName} in ${block.typesFile}.`);
288
- }
289
- let nextSource = source;
290
- if (!interfaceHasAttributeMember(targetInterface.declaration, target.attributeName)) {
291
- nextSource = insertBindingAttributeTypeMember(source, targetInterface.declaration, target.attributeName);
292
- await fsp.writeFile(typesPath, nextSource, "utf8");
293
- }
294
- await syncBlockMetadata({
295
- blockJsonFile: path.join("src", "blocks", block.slug, "block.json"),
296
- jsonSchemaFile: path.join("src", "blocks", block.slug, "typia.schema.json"),
297
- manifestFile: path.join("src", "blocks", block.slug, "typia.manifest.json"),
298
- openApiFile: path.join("src", "blocks", block.slug, "typia.openapi.json"),
299
- projectRoot: projectDir,
300
- sourceTypeName: block.attributeTypeName,
301
- typesFile: block.typesFile,
302
- });
303
- }
304
- function buildEditorPluginTypesSource(editorPluginSlug) {
305
- const typeName = `${toPascalCase(editorPluginSlug)}EditorPluginModel`;
306
- return `export interface ${typeName} {
307
- \tprimaryActionLabel: string;
308
- \tsummary: string;
309
- }
310
- `;
311
- }
312
- function buildEditorPluginDataSource(editorPluginSlug, slot) {
313
- const typeName = `${toPascalCase(editorPluginSlug)}EditorPluginModel`;
314
- const pluginTitle = toTitleCase(editorPluginSlug);
315
- const modelFactoryName = `get${toPascalCase(editorPluginSlug)}EditorPluginModel`;
316
- const enabledFactoryName = `is${toPascalCase(editorPluginSlug)}Enabled`;
317
- return `import type { ${typeName} } from './types';
318
-
319
- export const EDITOR_PLUGIN_SLOT = ${quoteTsString(slot)} as const;
320
- export const REQUIRED_CAPABILITY = 'edit_posts' as const;
321
-
322
- const DEFAULT_EDITOR_PLUGIN_MODEL: ${typeName} = {
323
- \tprimaryActionLabel: ${quoteTsString(`Review ${pluginTitle}`)},
324
- \tsummary: ${quoteTsString(`Replace this summary with your ${pluginTitle} workflow state.`)},
325
- };
326
-
327
- export function ${modelFactoryName}(): ${typeName} {
328
- \treturn DEFAULT_EDITOR_PLUGIN_MODEL;
329
- }
330
-
331
- export function ${enabledFactoryName}(): boolean {
332
- \treturn true;
333
- }
334
- `;
335
- }
336
- function buildEditorPluginSurfaceSource(editorPluginSlug, slot, textDomain) {
337
- const pascalName = toPascalCase(editorPluginSlug);
338
- const modelFactoryName = `get${pascalName}EditorPluginModel`;
339
- const enabledFactoryName = `is${pascalName}Enabled`;
340
- const componentName = `${pascalName}Surface`;
341
- if (slot === "document-setting-panel") {
342
- return `import { Button } from '@wordpress/components';
343
- import { PluginDocumentSettingPanel } from '@wordpress/editor';
344
- import { __ } from '@wordpress/i18n';
345
-
346
- import { ${modelFactoryName}, ${enabledFactoryName} } from './data';
347
- import './style.scss';
348
-
349
- export interface ${componentName}Props {
350
- \tsurfaceName: string;
351
- \ttitle: string;
352
- }
353
-
354
- export function ${componentName}( {
355
- \tsurfaceName,
356
- \ttitle,
357
- }: ${componentName}Props ) {
358
- \tif ( ! ${enabledFactoryName}() ) {
359
- \t\treturn null;
360
- \t}
361
-
362
- \tconst editorPluginModel = ${modelFactoryName}();
363
-
364
- \treturn (
365
- \t\t<PluginDocumentSettingPanel
366
- \t\t\tclassName="wp-typia-editor-plugin-shell"
367
- \t\t\tname={ surfaceName }
368
- \t\t\ttitle={ title }
369
- \t\t>
370
- \t\t\t<p>{ editorPluginModel.summary }</p>
371
- \t\t\t<Button variant="secondary">
372
- \t\t\t\t{ editorPluginModel.primaryActionLabel }
373
- \t\t\t</Button>
374
- \t\t\t<p className="wp-typia-editor-plugin-shell__hint">
375
- \t\t\t\t{ __( 'Use data.ts to add post type, capability, or editor context guards before showing this panel.', ${quoteTsString(textDomain)} ) }
376
- \t\t\t</p>
377
- \t\t</PluginDocumentSettingPanel>
378
- \t);
379
- }
380
- `;
381
- }
382
- return `import { Button, PanelBody } from '@wordpress/components';
383
- import { PluginSidebar, PluginSidebarMoreMenuItem } from '@wordpress/editor';
384
- import { __ } from '@wordpress/i18n';
385
-
386
- import { ${modelFactoryName}, ${enabledFactoryName} } from './data';
387
- import './style.scss';
388
-
389
- export interface ${componentName}Props {
390
- \tsurfaceName: string;
391
- \ttitle: string;
392
- }
393
-
394
- export function ${componentName}( {
395
- \tsurfaceName,
396
- \ttitle,
397
- }: ${componentName}Props ) {
398
- \tif ( ! ${enabledFactoryName}() ) {
399
- \t\treturn null;
400
- \t}
401
-
402
- \tconst editorPluginModel = ${modelFactoryName}();
403
-
404
- \treturn (
405
- \t\t<>
406
- \t\t\t<PluginSidebarMoreMenuItem target={ surfaceName }>
407
- \t\t\t\t{ title }
408
- \t\t\t</PluginSidebarMoreMenuItem>
409
- \t\t\t<PluginSidebar name={ surfaceName } title={ title }>
410
- \t\t\t\t<div className="wp-typia-editor-plugin-shell">
411
- \t\t\t\t\t<PanelBody
412
- \t\t\t\t\t\tinitialOpen
413
- \t\t\t\t\t\ttitle={ __( 'Document workflow', ${quoteTsString(textDomain)} ) }
414
- \t\t\t\t\t>
415
- \t\t\t\t\t\t<p>{ editorPluginModel.summary }</p>
416
- \t\t\t\t\t\t<Button variant="secondary">
417
- \t\t\t\t\t\t\t{ editorPluginModel.primaryActionLabel }
418
- \t\t\t\t\t\t</Button>
419
- \t\t\t\t\t</PanelBody>
420
- \t\t\t\t</div>
421
- \t\t\t</PluginSidebar>
422
- \t\t</>
423
- \t);
424
- }
425
- `;
426
- }
427
- function buildEditorPluginEntrySource(editorPluginSlug, namespace, textDomain) {
428
- const pascalName = toPascalCase(editorPluginSlug);
429
- const componentName = `${pascalName}Surface`;
430
- const pluginName = `${namespace}-${editorPluginSlug}`;
431
- const surfaceName = `${pluginName}-surface`;
432
- const pluginTitle = toTitleCase(editorPluginSlug);
433
- return `import { registerPlugin } from '@wordpress/plugins';
434
- import { __ } from '@wordpress/i18n';
435
-
436
- import { REQUIRED_CAPABILITY } from './data';
437
- import { ${componentName} } from './Surface';
438
-
439
- const EDITOR_PLUGIN_NAME = ${quoteTsString(pluginName)};
440
- const EDITOR_PLUGIN_SURFACE_NAME = ${quoteTsString(surfaceName)};
441
- const EDITOR_PLUGIN_TITLE = __( ${quoteTsString(pluginTitle)}, ${quoteTsString(textDomain)} );
442
-
443
- registerPlugin( EDITOR_PLUGIN_NAME, {
444
- \ticon: 'admin-generic',
445
- \trender: () => (
446
- \t\t<${componentName}
447
- \t\t\tsurfaceName={ EDITOR_PLUGIN_SURFACE_NAME }
448
- \t\t\ttitle={ EDITOR_PLUGIN_TITLE }
449
- \t\t/>
450
- \t),
451
- } );
452
-
453
- export { REQUIRED_CAPABILITY };
454
- `;
455
- }
456
- function buildEditorPluginStyleSource() {
457
- return `.wp-typia-editor-plugin-shell {
458
- \tpadding: 16px;
459
- }
460
-
461
- .wp-typia-editor-plugin-shell p {
462
- \tmargin: 0 0 12px;
463
- }
464
-
465
- .wp-typia-editor-plugin-shell__hint {
466
- \tcolor: #757575;
467
- \tfont-size: 12px;
468
- }
469
- `;
470
- }
471
- function buildBindingSourceIndexSource(bindingSourceSlugs) {
472
- const importLines = bindingSourceSlugs
473
- .map((bindingSourceSlug) => `import './${bindingSourceSlug}/editor';`)
474
- .join("\n");
475
- return `${importLines}${importLines ? "\n\n" : ""}// wp-typia add binding-source entries\n`;
476
- }
477
- function buildEditorPluginRegistrySource(editorPluginSlugs) {
478
- const importLines = editorPluginSlugs
479
- .map((editorPluginSlug) => `import './${editorPluginSlug}';`)
480
- .join("\n");
481
- return `${importLines}${importLines ? "\n\n" : ""}// wp-typia add editor-plugin entries\n`;
482
- }
483
- async function ensurePatternBootstrapAnchors(workspace) {
484
- const workspaceBaseName = workspace.packageName.split("/").pop() ?? workspace.packageName;
485
- const bootstrapPath = getWorkspaceBootstrapPath(workspace);
486
- await patchFile(bootstrapPath, (source) => {
487
- let nextSource = source;
488
- const patternCategoryFunctionName = `${workspace.workspace.phpPrefix}_register_pattern_category`;
489
- const patternRegistrationFunctionName = `${workspace.workspace.phpPrefix}_register_patterns`;
490
- const patternCategoryHook = `add_action( 'init', '${patternCategoryFunctionName}' );`;
491
- const patternRegistrationHook = `add_action( 'init', '${patternRegistrationFunctionName}', 20 );`;
492
- const patternFunctions = `
493
-
494
- function ${patternCategoryFunctionName}() {
495
- \tif ( function_exists( 'register_block_pattern_category' ) ) {
496
- \t\tregister_block_pattern_category(
497
- \t\t\t'${workspace.workspace.namespace}',
498
- \t\t\tarray(
499
- \t\t\t\t'label' => __( ${JSON.stringify(`${toTitleCase(workspaceBaseName)} Patterns`)}, '${workspace.workspace.textDomain}' ),
500
- \t\t\t)
501
- \t\t);
502
- \t}
503
- }
504
-
505
- function ${patternRegistrationFunctionName}() {
506
- \tforeach ( glob( __DIR__ . '/src/patterns/*.php' ) ?: array() as $pattern_module ) {
507
- \t\trequire $pattern_module;
508
- \t}
509
- }
510
- `;
511
- if (!nextSource.includes(PATTERN_BOOTSTRAP_CATEGORY)) {
512
- const insertionAnchors = [
513
- /add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
514
- /\?>\s*$/u,
515
- ];
516
- let inserted = false;
517
- for (const anchor of insertionAnchors) {
518
- const candidate = nextSource.replace(anchor, (match) => `${patternFunctions}\n${match}`);
519
- if (candidate !== nextSource) {
520
- nextSource = candidate;
521
- inserted = true;
522
- break;
523
- }
524
- }
525
- if (!inserted) {
526
- nextSource = `${nextSource.trimEnd()}\n${patternFunctions}\n`;
527
- }
528
- }
529
- if (!nextSource.includes(patternCategoryFunctionName) ||
530
- !nextSource.includes(patternRegistrationFunctionName)) {
531
- throw new Error(`Unable to inject pattern bootstrap functions into ${path.basename(bootstrapPath)}.`);
532
- }
533
- if (!nextSource.includes(patternCategoryHook)) {
534
- nextSource = `${nextSource.trimEnd()}\n${patternCategoryHook}\n`;
535
- }
536
- if (!nextSource.includes(patternRegistrationHook)) {
537
- nextSource = `${nextSource.trimEnd()}\n${patternRegistrationHook}\n`;
538
- }
539
- return nextSource;
540
- });
541
- }
542
- async function ensureBindingSourceBootstrapAnchors(workspace) {
543
- const bootstrapPath = getWorkspaceBootstrapPath(workspace);
544
- await patchFile(bootstrapPath, (source) => {
545
- let nextSource = source;
546
- const workspaceBaseName = workspace.packageName.split("/").pop() ?? workspace.packageName;
547
- const bindingRegistrationFunctionName = `${workspace.workspace.phpPrefix}_register_binding_sources`;
548
- const bindingEditorEnqueueFunctionName = `${workspace.workspace.phpPrefix}_enqueue_binding_sources_editor`;
549
- const bindingRegistrationHook = `add_action( 'init', '${bindingRegistrationFunctionName}', 20 );`;
550
- const bindingEditorEnqueueHook = `add_action( 'enqueue_block_editor_assets', '${bindingEditorEnqueueFunctionName}' );`;
551
- const bindingRegistrationFunction = `
552
-
553
- function ${bindingRegistrationFunctionName}() {
554
- \tforeach ( glob( __DIR__ . '${BINDING_SOURCE_SERVER_GLOB}' ) ?: array() as $binding_source_module ) {
555
- \t\trequire_once $binding_source_module;
556
- \t}
557
- }
558
- `;
559
- const bindingEditorEnqueueFunction = `
560
-
561
- function ${bindingEditorEnqueueFunctionName}() {
562
- \t$script_path = __DIR__ . '/${BINDING_SOURCE_EDITOR_SCRIPT}';
563
- \t$asset_path = __DIR__ . '/${BINDING_SOURCE_EDITOR_ASSET}';
564
-
565
- \tif ( ! file_exists( $script_path ) || ! file_exists( $asset_path ) ) {
566
- \t\treturn;
567
- \t}
568
-
569
- \t$asset = require $asset_path;
570
- \tif ( ! is_array( $asset ) ) {
571
- \t\t$asset = array();
572
- \t}
573
-
574
- \twp_enqueue_script(
575
- \t\t'${workspaceBaseName}-binding-sources',
576
- \t\tplugins_url( '${BINDING_SOURCE_EDITOR_SCRIPT}', __FILE__ ),
577
- \t\tisset( $asset['dependencies'] ) && is_array( $asset['dependencies'] ) ? $asset['dependencies'] : array(),
578
- \t\tisset( $asset['version'] ) ? $asset['version'] : filemtime( $script_path ),
579
- \t\ttrue
580
- \t);
581
- }
582
- `;
583
- if (!hasPhpFunctionDefinition(nextSource, bindingRegistrationFunctionName)) {
584
- nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, bindingRegistrationFunction);
585
- }
586
- if (!hasPhpFunctionDefinition(nextSource, bindingEditorEnqueueFunctionName)) {
587
- nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, bindingEditorEnqueueFunction);
588
- }
589
- if (!nextSource.includes(bindingRegistrationHook)) {
590
- nextSource = appendPhpSnippetBeforeClosingTag(nextSource, bindingRegistrationHook);
591
- }
592
- if (!nextSource.includes(bindingEditorEnqueueHook)) {
593
- nextSource = appendPhpSnippetBeforeClosingTag(nextSource, bindingEditorEnqueueHook);
594
- }
595
- return nextSource;
596
- });
597
- }
598
- async function ensureEditorPluginBootstrapAnchors(workspace) {
599
- const bootstrapPath = getWorkspaceBootstrapPath(workspace);
600
- await patchFile(bootstrapPath, (source) => {
601
- let nextSource = source;
602
- const workspaceBaseName = workspace.packageName.split("/").pop() ?? workspace.packageName;
603
- const enqueueFunctionName = `${workspace.workspace.phpPrefix}_enqueue_editor_plugins_editor`;
604
- const enqueueHook = `add_action( 'enqueue_block_editor_assets', '${enqueueFunctionName}' );`;
605
- const enqueueFunction = `
606
-
607
- function ${enqueueFunctionName}() {
608
- \t$script_path = __DIR__ . '/${EDITOR_PLUGIN_EDITOR_SCRIPT}';
609
- \t$asset_path = __DIR__ . '/${EDITOR_PLUGIN_EDITOR_ASSET}';
610
- \t$style_path = __DIR__ . '/${EDITOR_PLUGIN_EDITOR_STYLE}';
611
- \t$style_rtl_path = __DIR__ . '/${EDITOR_PLUGIN_EDITOR_STYLE_RTL}';
612
-
613
- \tif ( ! file_exists( $script_path ) || ! file_exists( $asset_path ) ) {
614
- \t\treturn;
615
- \t}
616
-
617
- \t$asset = require $asset_path;
618
- \tif ( ! is_array( $asset ) ) {
619
- \t\t$asset = array();
620
- \t}
621
-
622
- \twp_enqueue_script(
623
- \t\t'${workspaceBaseName}-editor-plugins',
624
- \t\tplugins_url( '${EDITOR_PLUGIN_EDITOR_SCRIPT}', __FILE__ ),
625
- \t\tisset( $asset['dependencies'] ) && is_array( $asset['dependencies'] ) ? $asset['dependencies'] : array(),
626
- \t\tisset( $asset['version'] ) ? $asset['version'] : filemtime( $script_path ),
627
- \t\ttrue
628
- \t);
629
-
630
- \tif ( file_exists( $style_path ) ) {
631
- \t\twp_enqueue_style(
632
- \t\t\t'${workspaceBaseName}-editor-plugins',
633
- \t\t\tplugins_url( '${EDITOR_PLUGIN_EDITOR_STYLE}', __FILE__ ),
634
- \t\t\tarray(),
635
- \t\t\tisset( $asset['version'] ) ? $asset['version'] : filemtime( $style_path )
636
- \t\t);
637
- \t\tif ( file_exists( $style_rtl_path ) ) {
638
- \t\t\twp_style_add_data( '${workspaceBaseName}-editor-plugins', 'rtl', 'replace' );
639
- \t\t}
640
- \t}
641
- }
642
- `;
643
- if (!hasPhpFunctionDefinition(nextSource, enqueueFunctionName)) {
644
- nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, enqueueFunction);
645
- }
646
- else {
647
- const requiredReferences = [
648
- EDITOR_PLUGIN_EDITOR_SCRIPT,
649
- EDITOR_PLUGIN_EDITOR_ASSET,
650
- EDITOR_PLUGIN_EDITOR_STYLE,
651
- EDITOR_PLUGIN_EDITOR_STYLE_RTL,
652
- "wp_style_add_data",
653
- ];
654
- const functionRange = findPhpFunctionRange(nextSource, enqueueFunctionName);
655
- const functionSource = functionRange
656
- ? nextSource.slice(functionRange.start, functionRange.end)
657
- : "";
658
- const missingReferences = requiredReferences.filter((reference) => !functionSource.includes(reference));
659
- if (missingReferences.length > 0) {
660
- const replacedSource = replacePhpFunctionDefinition(nextSource, enqueueFunctionName, enqueueFunction);
661
- if (!replacedSource) {
662
- throw new Error(`Unable to repair ${path.basename(bootstrapPath)} for ${enqueueFunctionName}.`);
663
- }
664
- nextSource = replacedSource;
665
- }
666
- }
667
- if (!nextSource.includes(enqueueHook)) {
668
- nextSource = appendPhpSnippetBeforeClosingTag(nextSource, enqueueHook);
669
- }
670
- return nextSource;
671
- });
672
- }
673
- async function ensureEditorPluginBuildScriptAnchors(workspace) {
674
- const buildScriptPath = path.join(workspace.projectDir, "scripts", "build-workspace.mjs");
675
- await patchFile(buildScriptPath, (source) => {
676
- if (/['"]src\/editor-plugins\/index\.(?:ts|js)['"]/u.test(source)) {
677
- return source;
678
- }
679
- const legacySharedEntriesPattern = /\[\s*['"]src\/bindings\/index\.ts['"]\s*,\s*['"]src\/bindings\/index\.js['"]\s*(?:,\s*)?\]/u;
680
- const nextSource = source.replace(legacySharedEntriesPattern, `[
681
- \t\t'src/bindings/index.ts',
682
- \t\t'src/bindings/index.js',
683
- \t\t'src/editor-plugins/index.ts',
684
- \t\t'src/editor-plugins/index.js',
685
- \t]`);
686
- if (nextSource === source) {
687
- throw new Error(`Unable to update ${path.relative(workspace.projectDir, buildScriptPath)} for editor plugin shared entries.`);
688
- }
689
- return nextSource;
690
- });
691
- }
692
- async function ensureEditorPluginWebpackAnchors(workspace) {
693
- const webpackConfigPath = path.join(workspace.projectDir, "webpack.config.js");
694
- await patchFile(webpackConfigPath, (source) => {
695
- if (/['"]editor-plugins\/index['"]/u.test(source)) {
696
- return source;
697
- }
698
- const legacySharedEntriesBlockPattern = /for\s*\(\s*const\s+relativePath\s+of\s+\[\s*['"]src\/bindings\/index\.ts['"]\s*,\s*['"]src\/bindings\/index\.js['"]\s*(?:,\s*)?\]\s*\)\s*\{[\s\S]*?entries\.push\(\s*\[\s*['"]bindings\/index['"]\s*,\s*entryPath\s*\]\s*\);\s*break;\s*\}/u;
699
- const nextSharedEntriesBlock = `\tfor ( const [ entryName, candidates ] of [\n\t\t[\n\t\t\t'bindings/index',\n\t\t\t[ 'src/bindings/index.ts', 'src/bindings/index.js' ],\n\t\t],\n\t\t[\n\t\t\t'editor-plugins/index',\n\t\t\t[ 'src/editor-plugins/index.ts', 'src/editor-plugins/index.js' ],\n\t\t],\n\t] ) {\n\t\tfor ( const relativePath of candidates ) {\n\t\t\tconst entryPath = path.resolve( process.cwd(), relativePath );\n\t\t\tif ( ! fs.existsSync( entryPath ) ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tentries.push( [ entryName, entryPath ] );\n\t\t\tbreak;\n\t\t}\n\t}`;
700
- const nextSource = source.replace(legacySharedEntriesBlockPattern, nextSharedEntriesBlock);
701
- if (nextSource === source) {
702
- throw new Error(`Unable to update ${path.relative(workspace.projectDir, webpackConfigPath)} for editor plugin shared entries.`);
703
- }
704
- return nextSource;
705
- });
706
- }
707
- async function resolveBindingSourceRegistryPath(projectDir) {
708
- const bindingsDir = path.join(projectDir, "src", "bindings");
709
- for (const candidatePath of [
710
- path.join(bindingsDir, "index.ts"),
711
- path.join(bindingsDir, "index.js"),
712
- ]) {
713
- if (await pathExists(candidatePath)) {
714
- return candidatePath;
715
- }
716
- }
717
- return path.join(bindingsDir, "index.ts");
718
- }
719
- async function writeBindingSourceRegistry(projectDir, bindingSourceSlug) {
720
- const bindingsDir = path.join(projectDir, "src", "bindings");
721
- const bindingsIndexPath = await resolveBindingSourceRegistryPath(projectDir);
722
- await fsp.mkdir(bindingsDir, { recursive: true });
723
- const existingBindingSourceSlugs = (await fsp.readdir(bindingsDir, { withFileTypes: true }))
724
- .filter((entry) => entry.isDirectory())
725
- .map((entry) => entry.name);
726
- const nextBindingSourceSlugs = Array.from(new Set([...existingBindingSourceSlugs, bindingSourceSlug])).sort();
727
- await fsp.writeFile(bindingsIndexPath, buildBindingSourceIndexSource(nextBindingSourceSlugs), "utf8");
728
- }
729
- async function resolveEditorPluginRegistryPath(projectDir) {
730
- const editorPluginsDir = path.join(projectDir, "src", "editor-plugins");
731
- for (const candidatePath of [
732
- path.join(editorPluginsDir, "index.ts"),
733
- path.join(editorPluginsDir, "index.js"),
734
- ]) {
735
- if (await pathExists(candidatePath)) {
736
- return candidatePath;
737
- }
738
- }
739
- return path.join(editorPluginsDir, "index.ts");
740
- }
741
- async function readEditorPluginRegistrySlugs(registryPath) {
742
- const source = await readOptionalUtf8File(registryPath);
743
- if (source === null) {
744
- return [];
745
- }
746
- return Array.from(source.matchAll(/^\s*import\s+['"]\.\/([^/'"]+)(?:\/index(?:\.[cm]?[jt]sx?)?)?['"];?\s*$/gmu)).map((match) => match[1]);
747
- }
748
- async function writeEditorPluginRegistry(projectDir, editorPluginSlug) {
749
- const editorPluginsDir = path.join(projectDir, "src", "editor-plugins");
750
- const registryPath = await resolveEditorPluginRegistryPath(projectDir);
751
- await fsp.mkdir(editorPluginsDir, { recursive: true });
752
- const existingEditorPluginSlugs = (await readWorkspaceInventoryAsync(projectDir)).editorPlugins.map((entry) => entry.slug);
753
- const existingRegistrySlugs = await readEditorPluginRegistrySlugs(registryPath);
754
- const nextEditorPluginSlugs = Array.from(new Set([...existingEditorPluginSlugs, ...existingRegistrySlugs, editorPluginSlug])).sort();
755
- await fsp.writeFile(registryPath, buildEditorPluginRegistrySource(nextEditorPluginSlugs), "utf8");
756
- }
757
1
  /**
758
- * Add one document-level editor plugin scaffold to an official workspace project.
2
+ * Compatibility facade for workspace asset add commands.
759
3
  *
760
- * @param options Command options for the editor-plugin scaffold workflow.
761
- * @param options.cwd Working directory used to resolve the nearest official workspace.
762
- * Defaults to `process.cwd()`.
763
- * @param options.editorPluginName Human-entered editor-plugin name that will be
764
- * normalized and validated before files are written.
765
- * @param options.slot Optional editor plugin shell slot. Defaults to `sidebar`.
766
- * @returns A promise that resolves with the normalized `editorPluginSlug`, chosen
767
- * `slot`, and owning `projectDir` after the scaffold files and inventory entry
768
- * are written successfully.
769
- * @throws {Error} When the command is run outside an official workspace, when the
770
- * slug or slot is invalid, or when a conflicting file or inventory entry exists.
4
+ * Keep the public runtime import path stable while each workflow lives in a
5
+ * focused implementation module.
771
6
  */
772
- export async function runAddEditorPluginCommand({ cwd = process.cwd(), editorPluginName, slot, }) {
773
- const workspace = resolveWorkspaceProject(cwd);
774
- const editorPluginSlug = assertValidGeneratedSlug("Editor plugin name", normalizeBlockSlug(editorPluginName), "wp-typia add editor-plugin <name> [--slot <sidebar|document-setting-panel>]");
775
- const resolvedSlot = assertValidEditorPluginSlot(slot);
776
- const inventory = await readWorkspaceInventoryAsync(workspace.projectDir);
777
- assertEditorPluginDoesNotExist(workspace.projectDir, editorPluginSlug, inventory);
778
- const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
779
- const bootstrapPath = getWorkspaceBootstrapPath(workspace);
780
- const buildScriptPath = path.join(workspace.projectDir, "scripts", "build-workspace.mjs");
781
- const editorPluginsIndexPath = await resolveEditorPluginRegistryPath(workspace.projectDir);
782
- const webpackConfigPath = path.join(workspace.projectDir, "webpack.config.js");
783
- const editorPluginDir = path.join(workspace.projectDir, "src", "editor-plugins", editorPluginSlug);
784
- const entryFilePath = path.join(editorPluginDir, "index.tsx");
785
- const surfaceFilePath = path.join(editorPluginDir, "Surface.tsx");
786
- const dataFilePath = path.join(editorPluginDir, "data.ts");
787
- const typesFilePath = path.join(editorPluginDir, "types.ts");
788
- const styleFilePath = path.join(editorPluginDir, "style.scss");
789
- const mutationSnapshot = {
790
- fileSources: await snapshotWorkspaceFiles([
791
- blockConfigPath,
792
- bootstrapPath,
793
- buildScriptPath,
794
- editorPluginsIndexPath,
795
- webpackConfigPath,
796
- ]),
797
- snapshotDirs: [],
798
- targetPaths: [editorPluginDir],
799
- };
800
- try {
801
- await fsp.mkdir(editorPluginDir, { recursive: true });
802
- await ensureEditorPluginBootstrapAnchors(workspace);
803
- await ensureEditorPluginBuildScriptAnchors(workspace);
804
- await ensureEditorPluginWebpackAnchors(workspace);
805
- await fsp.writeFile(entryFilePath, buildEditorPluginEntrySource(editorPluginSlug, workspace.workspace.namespace, workspace.workspace.textDomain), "utf8");
806
- await fsp.writeFile(surfaceFilePath, buildEditorPluginSurfaceSource(editorPluginSlug, resolvedSlot, workspace.workspace.textDomain), "utf8");
807
- await fsp.writeFile(dataFilePath, buildEditorPluginDataSource(editorPluginSlug, resolvedSlot), "utf8");
808
- await fsp.writeFile(typesFilePath, buildEditorPluginTypesSource(editorPluginSlug), "utf8");
809
- await fsp.writeFile(styleFilePath, buildEditorPluginStyleSource(), "utf8");
810
- await writeEditorPluginRegistry(workspace.projectDir, editorPluginSlug);
811
- await appendWorkspaceInventoryEntries(workspace.projectDir, {
812
- editorPluginEntries: [
813
- buildEditorPluginConfigEntry(editorPluginSlug, resolvedSlot),
814
- ],
815
- });
816
- return {
817
- editorPluginSlug,
818
- projectDir: workspace.projectDir,
819
- slot: resolvedSlot,
820
- };
821
- }
822
- catch (error) {
823
- await rollbackWorkspaceMutation(mutationSnapshot);
824
- throw error;
825
- }
826
- }
827
- /**
828
- * Add one PHP block pattern shell to an official workspace project.
829
- *
830
- * @param options Command options for the pattern scaffold workflow.
831
- * @param options.cwd Working directory used to resolve the nearest official workspace.
832
- * Defaults to `process.cwd()`.
833
- * @param options.patternName Human-entered pattern name that will be normalized
834
- * and validated before files are written.
835
- * @returns A promise that resolves with the normalized `patternSlug` and
836
- * owning `projectDir` after the pattern file and inventory entry have been
837
- * written successfully.
838
- * @throws {Error} When the command is run outside an official workspace, when
839
- * the pattern slug is invalid, or when a conflicting file or inventory entry
840
- * already exists.
841
- */
842
- export async function runAddPatternCommand({ cwd = process.cwd(), patternName, }) {
843
- const workspace = resolveWorkspaceProject(cwd);
844
- const patternSlug = assertValidGeneratedSlug("Pattern name", normalizeBlockSlug(patternName), "wp-typia add pattern <name>");
845
- const inventory = await readWorkspaceInventoryAsync(workspace.projectDir);
846
- assertPatternDoesNotExist(workspace.projectDir, patternSlug, inventory);
847
- const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
848
- const bootstrapPath = getWorkspaceBootstrapPath(workspace);
849
- const patternFilePath = path.join(workspace.projectDir, "src", "patterns", `${patternSlug}.php`);
850
- const mutationSnapshot = {
851
- fileSources: await snapshotWorkspaceFiles([blockConfigPath, bootstrapPath]),
852
- snapshotDirs: [],
853
- targetPaths: [patternFilePath],
854
- };
855
- try {
856
- await fsp.mkdir(path.dirname(patternFilePath), { recursive: true });
857
- await ensurePatternBootstrapAnchors(workspace);
858
- await fsp.writeFile(patternFilePath, buildPatternSource(patternSlug, workspace.workspace.namespace, workspace.workspace.textDomain), "utf8");
859
- await appendWorkspaceInventoryEntries(workspace.projectDir, {
860
- patternEntries: [buildPatternConfigEntry(patternSlug)],
861
- });
862
- return {
863
- patternSlug,
864
- projectDir: workspace.projectDir,
865
- };
866
- }
867
- catch (error) {
868
- await rollbackWorkspaceMutation(mutationSnapshot);
869
- throw error;
870
- }
871
- }
872
- /**
873
- * Add one block binding source scaffold to an official workspace project.
874
- *
875
- * @param options Command options for the binding-source scaffold workflow.
876
- * @param options.attributeName Optional generated block attribute to declare as
877
- * bindable. Must be provided together with `blockName`.
878
- * @param options.blockName Optional generated block slug or full block name to
879
- * receive the bindable attribute wiring. Must be provided together with
880
- * `attributeName`.
881
- * @param options.bindingSourceName Human-entered binding source name that will
882
- * be normalized and validated before files are written.
883
- * @param options.cwd Working directory used to resolve the nearest official
884
- * workspace. Defaults to `process.cwd()`.
885
- * @returns A promise that resolves with the normalized `bindingSourceSlug` and
886
- * owning `projectDir` after the server/editor files, optional target block
887
- * metadata, and inventory entry have been written successfully.
888
- * @throws {Error} When the command is run outside an official workspace, when
889
- * the slug is invalid, when a binding target is incomplete or unknown, or when
890
- * a conflicting file or inventory entry exists.
891
- */
892
- export async function runAddBindingSourceCommand({ attributeName, bindingSourceName, blockName, cwd = process.cwd(), }) {
893
- const workspace = resolveWorkspaceProject(cwd);
894
- const bindingSourceSlug = assertValidGeneratedSlug("Binding source name", normalizeBlockSlug(bindingSourceName), "wp-typia add binding-source <name> [--block <block-slug|namespace/block-slug> --attribute <attribute>]");
895
- const inventory = await readWorkspaceInventoryAsync(workspace.projectDir);
896
- assertBindingSourceDoesNotExist(workspace.projectDir, bindingSourceSlug, inventory);
897
- const target = resolveBindingTarget({
898
- attributeName,
899
- blockName,
900
- }, workspace.workspace.namespace);
901
- const targetBlock = target ? resolveWorkspaceBlock(inventory, target.blockSlug) : undefined;
902
- const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
903
- const bootstrapPath = getWorkspaceBootstrapPath(workspace);
904
- const bindingsIndexPath = await resolveBindingSourceRegistryPath(workspace.projectDir);
905
- const bindingSourceDir = path.join(workspace.projectDir, "src", "bindings", bindingSourceSlug);
906
- const serverFilePath = path.join(bindingSourceDir, "server.php");
907
- const editorFilePath = path.join(bindingSourceDir, "editor.ts");
908
- const blockJsonPath = target
909
- ? path.join(workspace.projectDir, "src", "blocks", target.blockSlug, "block.json")
910
- : undefined;
911
- const targetGeneratedMetadataPaths = target
912
- ? [
913
- path.join(workspace.projectDir, "src", "blocks", target.blockSlug, "typia.manifest.json"),
914
- path.join(workspace.projectDir, "src", "blocks", target.blockSlug, "typia.openapi.json"),
915
- path.join(workspace.projectDir, "src", "blocks", target.blockSlug, "typia.schema.json"),
916
- path.join(workspace.projectDir, "src", "blocks", target.blockSlug, "typia-validator.php"),
917
- ]
918
- : [];
919
- const mutationSnapshot = {
920
- fileSources: await snapshotWorkspaceFiles([
921
- blockConfigPath,
922
- bootstrapPath,
923
- bindingsIndexPath,
924
- ...(blockJsonPath ? [blockJsonPath] : []),
925
- ...(targetBlock ? [path.join(workspace.projectDir, targetBlock.typesFile)] : []),
926
- ...targetGeneratedMetadataPaths,
927
- ]),
928
- snapshotDirs: [],
929
- targetPaths: [bindingSourceDir],
930
- };
931
- try {
932
- await fsp.mkdir(bindingSourceDir, { recursive: true });
933
- await ensureBindingSourceBootstrapAnchors(workspace);
934
- await fsp.writeFile(serverFilePath, buildBindingSourceServerSource(bindingSourceSlug, workspace.workspace.phpPrefix, workspace.workspace.namespace, workspace.workspace.textDomain, target), "utf8");
935
- await fsp.writeFile(editorFilePath, buildBindingSourceEditorSource(bindingSourceSlug, workspace.workspace.namespace, workspace.workspace.textDomain, target), "utf8");
936
- if (target && targetBlock) {
937
- await ensureBindingTargetBlockAttributeType(workspace.projectDir, targetBlock, target);
938
- }
939
- await writeBindingSourceRegistry(workspace.projectDir, bindingSourceSlug);
940
- await appendWorkspaceInventoryEntries(workspace.projectDir, {
941
- bindingSourceEntries: [buildBindingSourceConfigEntry(bindingSourceSlug, target)],
942
- });
943
- return {
944
- ...(target ? { attributeName: target.attributeName, blockSlug: target.blockSlug } : {}),
945
- bindingSourceSlug,
946
- projectDir: workspace.projectDir,
947
- };
948
- }
949
- catch (error) {
950
- await rollbackWorkspaceMutation(mutationSnapshot);
951
- throw error;
952
- }
953
- }
7
+ export { runAddBindingSourceCommand, } from "./cli-add-workspace-binding-source.js";
8
+ export { runAddEditorPluginCommand } from "./cli-add-workspace-editor-plugin.js";
9
+ export { runAddPatternCommand } from "./cli-add-workspace-pattern.js";