@wp-typia/project-tools 0.16.10 → 0.16.12

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 (104) hide show
  1. package/README.md +9 -3
  2. package/dist/runtime/built-in-block-artifact-documents.d.ts +3 -0
  3. package/dist/runtime/built-in-block-artifact-documents.js +2 -0
  4. package/dist/runtime/built-in-block-artifact-types.d.ts +51 -0
  5. package/dist/runtime/built-in-block-artifact-types.js +304 -0
  6. package/dist/runtime/built-in-block-artifacts.js +4 -803
  7. package/dist/runtime/built-in-block-attribute-emitters.d.ts +71 -0
  8. package/dist/runtime/built-in-block-attribute-emitters.js +176 -0
  9. package/dist/runtime/built-in-block-attribute-specs.d.ts +38 -0
  10. package/dist/runtime/built-in-block-attribute-specs.js +358 -0
  11. package/dist/runtime/built-in-block-code-templates/basic.d.ts +4 -0
  12. package/dist/runtime/built-in-block-code-templates/basic.js +249 -0
  13. package/dist/runtime/built-in-block-code-templates/compound-child.d.ts +4 -0
  14. package/dist/runtime/built-in-block-code-templates/compound-child.js +138 -0
  15. package/dist/runtime/built-in-block-code-templates/compound-parent.d.ts +6 -0
  16. package/dist/runtime/built-in-block-code-templates/compound-parent.js +227 -0
  17. package/dist/runtime/built-in-block-code-templates/compound-persistence.d.ts +4 -0
  18. package/dist/runtime/built-in-block-code-templates/compound-persistence.js +478 -0
  19. package/dist/runtime/built-in-block-code-templates/compound.d.ts +3 -0
  20. package/dist/runtime/built-in-block-code-templates/compound.js +3 -0
  21. package/dist/runtime/built-in-block-code-templates/interactivity.d.ts +5 -0
  22. package/dist/runtime/built-in-block-code-templates/interactivity.js +547 -0
  23. package/dist/runtime/built-in-block-code-templates/persistence.d.ts +5 -0
  24. package/dist/runtime/built-in-block-code-templates/persistence.js +550 -0
  25. package/dist/runtime/built-in-block-code-templates/shared.d.ts +16 -0
  26. package/dist/runtime/built-in-block-code-templates/shared.js +53 -0
  27. package/dist/runtime/built-in-block-code-templates.d.ts +5 -32
  28. package/dist/runtime/built-in-block-code-templates.js +5 -2230
  29. package/dist/runtime/cli-add-block-config.d.ts +6 -0
  30. package/dist/runtime/cli-add-block-config.js +143 -0
  31. package/dist/runtime/cli-add-block-legacy-validator.d.ts +4 -0
  32. package/dist/runtime/cli-add-block-legacy-validator.js +168 -0
  33. package/dist/runtime/cli-add-block.js +3 -301
  34. package/dist/runtime/cli-add-workspace-assets.d.ts +38 -0
  35. package/dist/runtime/cli-add-workspace-assets.js +399 -0
  36. package/dist/runtime/cli-add-workspace.d.ts +2 -38
  37. package/dist/runtime/cli-add-workspace.js +5 -396
  38. package/dist/runtime/cli-doctor-environment.d.ts +12 -0
  39. package/dist/runtime/cli-doctor-environment.js +123 -0
  40. package/dist/runtime/cli-doctor-workspace.d.ts +14 -0
  41. package/dist/runtime/cli-doctor-workspace.js +296 -0
  42. package/dist/runtime/cli-doctor.d.ts +4 -2
  43. package/dist/runtime/cli-doctor.js +10 -405
  44. package/dist/runtime/cli-help.js +1 -1
  45. package/dist/runtime/cli-scaffold.js +1 -1
  46. package/dist/runtime/migration-command-surface.d.ts +67 -0
  47. package/dist/runtime/migration-command-surface.js +189 -0
  48. package/dist/runtime/migration-diff-rename.d.ts +13 -0
  49. package/dist/runtime/migration-diff-rename.js +192 -0
  50. package/dist/runtime/migration-diff-transform.d.ts +14 -0
  51. package/dist/runtime/migration-diff-transform.js +105 -0
  52. package/dist/runtime/migration-diff.js +12 -297
  53. package/dist/runtime/migration-generated-artifacts.d.ts +3 -0
  54. package/dist/runtime/migration-generated-artifacts.js +41 -0
  55. package/dist/runtime/migration-maintenance.d.ts +51 -0
  56. package/dist/runtime/migration-maintenance.js +380 -0
  57. package/dist/runtime/migration-planning.d.ts +23 -0
  58. package/dist/runtime/migration-planning.js +131 -0
  59. package/dist/runtime/migration-project-config-source.d.ts +6 -0
  60. package/dist/runtime/migration-project-config-source.js +424 -0
  61. package/dist/runtime/migration-project-layout-discovery.d.ts +61 -0
  62. package/dist/runtime/migration-project-layout-discovery.js +337 -0
  63. package/dist/runtime/migration-project-layout-paths.d.ts +135 -0
  64. package/dist/runtime/migration-project-layout-paths.js +288 -0
  65. package/dist/runtime/migration-project-layout.d.ts +3 -0
  66. package/dist/runtime/migration-project-layout.js +2 -0
  67. package/dist/runtime/migration-project-workspace.d.ts +47 -0
  68. package/dist/runtime/migration-project-workspace.js +212 -0
  69. package/dist/runtime/migration-project.d.ts +4 -94
  70. package/dist/runtime/migration-project.js +3 -1101
  71. package/dist/runtime/migration-render-diff-rule.d.ts +5 -0
  72. package/dist/runtime/migration-render-diff-rule.js +120 -0
  73. package/dist/runtime/migration-render-execution.d.ts +3 -0
  74. package/dist/runtime/migration-render-execution.js +428 -0
  75. package/dist/runtime/migration-render-generated.d.ts +27 -0
  76. package/dist/runtime/migration-render-generated.js +230 -0
  77. package/dist/runtime/migration-render-support.d.ts +3 -0
  78. package/dist/runtime/migration-render-support.js +16 -0
  79. package/dist/runtime/migration-render.d.ts +3 -33
  80. package/dist/runtime/migration-render.js +3 -789
  81. package/dist/runtime/migration-ui-capability.js +1 -1
  82. package/dist/runtime/migrations.d.ts +24 -118
  83. package/dist/runtime/migrations.js +12 -700
  84. package/dist/runtime/scaffold-bootstrap.d.ts +45 -0
  85. package/dist/runtime/scaffold-bootstrap.js +185 -0
  86. package/dist/runtime/scaffold-package-manager-files.d.ts +35 -0
  87. package/dist/runtime/scaffold-package-manager-files.js +79 -0
  88. package/dist/runtime/scaffold.d.ts +1 -12
  89. package/dist/runtime/scaffold.js +10 -393
  90. package/dist/runtime/template-source-contracts.d.ts +81 -0
  91. package/dist/runtime/template-source-contracts.js +1 -0
  92. package/dist/runtime/template-source-external.d.ts +21 -0
  93. package/dist/runtime/template-source-external.js +184 -0
  94. package/dist/runtime/template-source-locators.d.ts +4 -0
  95. package/dist/runtime/template-source-locators.js +72 -0
  96. package/dist/runtime/template-source-normalization.d.ts +7 -0
  97. package/dist/runtime/template-source-normalization.js +53 -0
  98. package/dist/runtime/template-source-remote.d.ts +23 -0
  99. package/dist/runtime/template-source-remote.js +336 -0
  100. package/dist/runtime/template-source-seeds.d.ts +12 -0
  101. package/dist/runtime/template-source-seeds.js +243 -0
  102. package/dist/runtime/template-source.d.ts +4 -86
  103. package/dist/runtime/template-source.js +9 -828
  104. package/package.json +5 -5
@@ -0,0 +1,38 @@
1
+ import { type RunAddBindingSourceCommandOptions, type RunAddPatternCommandOptions } from "./cli-add-shared.js";
2
+ /**
3
+ * Add one PHP block pattern shell to an official workspace project.
4
+ *
5
+ * @param options Command options for the pattern scaffold workflow.
6
+ * @param options.cwd Working directory used to resolve the nearest official workspace.
7
+ * Defaults to `process.cwd()`.
8
+ * @param options.patternName Human-entered pattern name that will be normalized
9
+ * and validated before files are written.
10
+ * @returns A promise that resolves with the normalized `patternSlug` and
11
+ * owning `projectDir` after the pattern file and inventory entry have been
12
+ * written successfully.
13
+ * @throws {Error} When the command is run outside an official workspace, when
14
+ * the pattern slug is invalid, or when a conflicting file or inventory entry
15
+ * already exists.
16
+ */
17
+ export declare function runAddPatternCommand({ cwd, patternName, }: RunAddPatternCommandOptions): Promise<{
18
+ patternSlug: string;
19
+ projectDir: string;
20
+ }>;
21
+ /**
22
+ * Add one block binding source scaffold to an official workspace project.
23
+ *
24
+ * @param options Command options for the binding-source scaffold workflow.
25
+ * @param options.bindingSourceName Human-entered binding source name that will
26
+ * be normalized and validated before files are written.
27
+ * @param options.cwd Working directory used to resolve the nearest official
28
+ * workspace. Defaults to `process.cwd()`.
29
+ * @returns A promise that resolves with the normalized `bindingSourceSlug` and
30
+ * owning `projectDir` after the server/editor files and inventory entry have
31
+ * been written successfully.
32
+ * @throws {Error} When the command is run outside an official workspace, when
33
+ * the slug is invalid, or when a conflicting file or inventory entry exists.
34
+ */
35
+ export declare function runAddBindingSourceCommand({ bindingSourceName, cwd, }: RunAddBindingSourceCommandOptions): Promise<{
36
+ bindingSourceSlug: string;
37
+ projectDir: string;
38
+ }>;
@@ -0,0 +1,399 @@
1
+ import fs from "node:fs";
2
+ import { promises as fsp } from "node:fs";
3
+ import path from "node:path";
4
+ import { resolveWorkspaceProject, } from "./workspace-project.js";
5
+ import { readWorkspaceInventory, appendWorkspaceInventoryEntries } from "./workspace-inventory.js";
6
+ import { toTitleCase } from "./string-case.js";
7
+ import { assertBindingSourceDoesNotExist, assertPatternDoesNotExist, assertValidGeneratedSlug, getWorkspaceBootstrapPath, normalizeBlockSlug, patchFile, quoteTsString, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
8
+ const PATTERN_BOOTSTRAP_CATEGORY = "register_block_pattern_category";
9
+ const BINDING_SOURCE_SERVER_GLOB = "/src/bindings/*/server.php";
10
+ const BINDING_SOURCE_EDITOR_SCRIPT = "build/bindings/index.js";
11
+ const BINDING_SOURCE_EDITOR_ASSET = "build/bindings/index.asset.php";
12
+ function escapeRegex(value) {
13
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
14
+ }
15
+ function quotePhpString(value) {
16
+ return `'${value.replace(/\\/gu, "\\\\").replace(/'/gu, "\\'")}'`;
17
+ }
18
+ function buildPatternConfigEntry(patternSlug) {
19
+ return [
20
+ "\t{",
21
+ `\t\tfile: ${quoteTsString(`src/patterns/${patternSlug}.php`)},`,
22
+ `\t\tslug: ${quoteTsString(patternSlug)},`,
23
+ "\t},",
24
+ ].join("\n");
25
+ }
26
+ function buildBindingSourceConfigEntry(bindingSourceSlug) {
27
+ return [
28
+ "\t{",
29
+ `\t\teditorFile: ${quoteTsString(`src/bindings/${bindingSourceSlug}/editor.ts`)},`,
30
+ `\t\tserverFile: ${quoteTsString(`src/bindings/${bindingSourceSlug}/server.php`)},`,
31
+ `\t\tslug: ${quoteTsString(bindingSourceSlug)},`,
32
+ "\t},",
33
+ ].join("\n");
34
+ }
35
+ function buildPatternSource(patternSlug, namespace, textDomain) {
36
+ const patternTitle = toTitleCase(patternSlug);
37
+ return `<?php
38
+ if ( ! defined( 'ABSPATH' ) ) {
39
+ \treturn;
40
+ }
41
+
42
+ register_block_pattern(
43
+ \t'${namespace}/${patternSlug}',
44
+ \tarray(
45
+ \t\t'title' => __( ${JSON.stringify(patternTitle)}, '${textDomain}' ),
46
+ \t\t'description' => __( ${JSON.stringify(`A starter pattern for ${patternTitle}.`)}, '${textDomain}' ),
47
+ \t\t'categories' => array( '${namespace}' ),
48
+ \t\t'content' => '<!-- wp:paragraph --><p>' . esc_html__( 'Describe this pattern here.', '${textDomain}' ) . '</p><!-- /wp:paragraph -->',
49
+ \t)
50
+ );
51
+ `;
52
+ }
53
+ function buildBindingSourceServerSource(bindingSourceSlug, phpPrefix, namespace, textDomain) {
54
+ const bindingSourceTitle = toTitleCase(bindingSourceSlug);
55
+ const bindingSourcePhpId = bindingSourceSlug.replace(/-/g, "_");
56
+ const bindingSourceValueFunctionName = `${phpPrefix}_${bindingSourcePhpId}_binding_source_values`;
57
+ const bindingSourceResolveFunctionName = `${phpPrefix}_${bindingSourcePhpId}_resolve_binding_source_value`;
58
+ const starterValue = `${bindingSourceTitle} starter value`;
59
+ return `<?php
60
+ if ( ! defined( 'ABSPATH' ) ) {
61
+ \treturn;
62
+ }
63
+
64
+ if ( ! function_exists( 'register_block_bindings_source' ) ) {
65
+ \treturn;
66
+ }
67
+
68
+ if ( ! function_exists( '${bindingSourceValueFunctionName}' ) ) {
69
+ \tfunction ${bindingSourceValueFunctionName}() : array {
70
+ \t\treturn array(
71
+ \t\t\t${quotePhpString(bindingSourceSlug)} => ${quotePhpString(starterValue)},
72
+ \t\t);
73
+ \t}
74
+ }
75
+
76
+ if ( ! function_exists( '${bindingSourceResolveFunctionName}' ) ) {
77
+ \tfunction ${bindingSourceResolveFunctionName}( array $source_args ) : string {
78
+ \t\t$field = isset( $source_args['field'] ) && is_string( $source_args['field'] )
79
+ \t\t\t? $source_args['field']
80
+ \t\t\t: '${bindingSourceSlug}';
81
+ \t\t$binding_source_values = ${bindingSourceValueFunctionName}();
82
+ \t\t$value = $binding_source_values[ $field ] ?? '';
83
+
84
+ \t\treturn is_string( $value ) ? $value : '';
85
+ \t}
86
+ }
87
+
88
+ register_block_bindings_source(
89
+ \t${quotePhpString(`${namespace}/${bindingSourceSlug}`)},
90
+ \tarray(
91
+ \t\t'label' => __( ${quotePhpString(bindingSourceTitle)}, ${quotePhpString(textDomain)} ),
92
+ \t\t'get_value_callback' => ${quotePhpString(bindingSourceResolveFunctionName)},
93
+ \t)
94
+ );
95
+ `;
96
+ }
97
+ function buildBindingSourceEditorSource(bindingSourceSlug, namespace, textDomain) {
98
+ const bindingSourceTitle = toTitleCase(bindingSourceSlug);
99
+ const starterValue = `${bindingSourceTitle} starter value`;
100
+ return `import { registerBlockBindingsSource } from '@wordpress/blocks';
101
+ import { __ } from '@wordpress/i18n';
102
+
103
+ interface BindingSourceRegistration {
104
+ \targs?: {
105
+ \t\tfield?: string;
106
+ \t};
107
+ }
108
+
109
+ const BINDING_SOURCE_VALUES: Record<string, string> = {
110
+ \t${quoteTsString(bindingSourceSlug)}: ${quoteTsString(starterValue)},
111
+ };
112
+
113
+ function resolveBindingSourceValue( field: string ): string {
114
+ \treturn BINDING_SOURCE_VALUES[ field ] ?? '';
115
+ }
116
+
117
+ registerBlockBindingsSource( {
118
+ \tname: ${quoteTsString(`${namespace}/${bindingSourceSlug}`)},
119
+ \tlabel: __( ${quoteTsString(bindingSourceTitle)}, ${quoteTsString(textDomain)} ),
120
+ \tgetFieldsList() {
121
+ \t\treturn [
122
+ \t\t\t{
123
+ \t\t\t\tlabel: __( ${quoteTsString(bindingSourceTitle)}, ${quoteTsString(textDomain)} ),
124
+ \t\t\t\ttype: 'string',
125
+ \t\t\t\targs: {
126
+ \t\t\t\t\tfield: ${quoteTsString(bindingSourceSlug)},
127
+ \t\t\t\t},
128
+ \t\t\t},
129
+ \t\t];
130
+ \t},
131
+ \tgetValues( { bindings } ) {
132
+ \t\tconst values: Record<string, string> = {};
133
+ \t\tfor ( const [ attributeName, binding ] of Object.entries(
134
+ \t\t\tbindings as Record<string, BindingSourceRegistration>
135
+ \t\t) ) {
136
+ \t\t\tconst field =
137
+ \t\t\t\ttypeof binding?.args?.field === 'string'
138
+ \t\t\t\t\t? binding.args.field
139
+ \t\t\t\t\t: ${quoteTsString(bindingSourceSlug)};
140
+ \t\t\tvalues[ attributeName ] = resolveBindingSourceValue( field );
141
+ \t\t}
142
+ \t\treturn values;
143
+ \t},
144
+ } );
145
+ `;
146
+ }
147
+ function buildBindingSourceIndexSource(bindingSourceSlugs) {
148
+ const importLines = bindingSourceSlugs
149
+ .map((bindingSourceSlug) => `import './${bindingSourceSlug}/editor';`)
150
+ .join("\n");
151
+ return `${importLines}${importLines ? "\n\n" : ""}// wp-typia add binding-source entries\n`;
152
+ }
153
+ async function ensurePatternBootstrapAnchors(workspace) {
154
+ const workspaceBaseName = workspace.packageName.split("/").pop() ?? workspace.packageName;
155
+ const bootstrapPath = getWorkspaceBootstrapPath(workspace);
156
+ await patchFile(bootstrapPath, (source) => {
157
+ let nextSource = source;
158
+ const patternCategoryFunctionName = `${workspace.workspace.phpPrefix}_register_pattern_category`;
159
+ const patternRegistrationFunctionName = `${workspace.workspace.phpPrefix}_register_patterns`;
160
+ const patternCategoryHook = `add_action( 'init', '${patternCategoryFunctionName}' );`;
161
+ const patternRegistrationHook = `add_action( 'init', '${patternRegistrationFunctionName}', 20 );`;
162
+ const patternFunctions = `
163
+
164
+ function ${patternCategoryFunctionName}() {
165
+ \tif ( function_exists( 'register_block_pattern_category' ) ) {
166
+ \t\tregister_block_pattern_category(
167
+ \t\t\t'${workspace.workspace.namespace}',
168
+ \t\t\tarray(
169
+ \t\t\t\t'label' => __( ${JSON.stringify(`${toTitleCase(workspaceBaseName)} Patterns`)}, '${workspace.workspace.textDomain}' ),
170
+ \t\t\t)
171
+ \t\t);
172
+ \t}
173
+ }
174
+
175
+ function ${patternRegistrationFunctionName}() {
176
+ \tforeach ( glob( __DIR__ . '/src/patterns/*.php' ) ?: array() as $pattern_module ) {
177
+ \t\trequire $pattern_module;
178
+ \t}
179
+ }
180
+ `;
181
+ if (!nextSource.includes(PATTERN_BOOTSTRAP_CATEGORY)) {
182
+ const insertionAnchors = [
183
+ /add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
184
+ /\?>\s*$/u,
185
+ ];
186
+ let inserted = false;
187
+ for (const anchor of insertionAnchors) {
188
+ const candidate = nextSource.replace(anchor, (match) => `${patternFunctions}\n${match}`);
189
+ if (candidate !== nextSource) {
190
+ nextSource = candidate;
191
+ inserted = true;
192
+ break;
193
+ }
194
+ }
195
+ if (!inserted) {
196
+ nextSource = `${nextSource.trimEnd()}\n${patternFunctions}\n`;
197
+ }
198
+ }
199
+ if (!nextSource.includes(patternCategoryFunctionName) ||
200
+ !nextSource.includes(patternRegistrationFunctionName)) {
201
+ throw new Error(`Unable to inject pattern bootstrap functions into ${path.basename(bootstrapPath)}.`);
202
+ }
203
+ if (!nextSource.includes(patternCategoryHook)) {
204
+ nextSource = `${nextSource.trimEnd()}\n${patternCategoryHook}\n`;
205
+ }
206
+ if (!nextSource.includes(patternRegistrationHook)) {
207
+ nextSource = `${nextSource.trimEnd()}\n${patternRegistrationHook}\n`;
208
+ }
209
+ return nextSource;
210
+ });
211
+ }
212
+ async function ensureBindingSourceBootstrapAnchors(workspace) {
213
+ const bootstrapPath = getWorkspaceBootstrapPath(workspace);
214
+ await patchFile(bootstrapPath, (source) => {
215
+ let nextSource = source;
216
+ const workspaceBaseName = workspace.packageName.split("/").pop() ?? workspace.packageName;
217
+ const bindingRegistrationFunctionName = `${workspace.workspace.phpPrefix}_register_binding_sources`;
218
+ const bindingEditorEnqueueFunctionName = `${workspace.workspace.phpPrefix}_enqueue_binding_sources_editor`;
219
+ const bindingRegistrationHook = `add_action( 'init', '${bindingRegistrationFunctionName}', 20 );`;
220
+ const bindingEditorEnqueueHook = `add_action( 'enqueue_block_editor_assets', '${bindingEditorEnqueueFunctionName}' );`;
221
+ const bindingRegistrationFunction = `
222
+
223
+ function ${bindingRegistrationFunctionName}() {
224
+ \tforeach ( glob( __DIR__ . '${BINDING_SOURCE_SERVER_GLOB}' ) ?: array() as $binding_source_module ) {
225
+ \t\trequire_once $binding_source_module;
226
+ \t}
227
+ }
228
+ `;
229
+ const bindingEditorEnqueueFunction = `
230
+
231
+ function ${bindingEditorEnqueueFunctionName}() {
232
+ \t$script_path = __DIR__ . '/${BINDING_SOURCE_EDITOR_SCRIPT}';
233
+ \t$asset_path = __DIR__ . '/${BINDING_SOURCE_EDITOR_ASSET}';
234
+
235
+ \tif ( ! file_exists( $script_path ) || ! file_exists( $asset_path ) ) {
236
+ \t\treturn;
237
+ \t}
238
+
239
+ \t$asset = require $asset_path;
240
+ \tif ( ! is_array( $asset ) ) {
241
+ \t\t$asset = array();
242
+ \t}
243
+
244
+ \twp_enqueue_script(
245
+ \t\t'${workspaceBaseName}-binding-sources',
246
+ \t\tplugins_url( '${BINDING_SOURCE_EDITOR_SCRIPT}', __FILE__ ),
247
+ \t\tisset( $asset['dependencies'] ) && is_array( $asset['dependencies'] ) ? $asset['dependencies'] : array(),
248
+ \t\tisset( $asset['version'] ) ? $asset['version'] : filemtime( $script_path ),
249
+ \t\ttrue
250
+ \t);
251
+ }
252
+ `;
253
+ const insertionAnchors = [
254
+ /add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
255
+ /\?>\s*$/u,
256
+ ];
257
+ const hasPhpFunctionDefinition = (functionName) => new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\(`, "u").test(nextSource);
258
+ const insertPhpSnippet = (snippet) => {
259
+ for (const anchor of insertionAnchors) {
260
+ const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
261
+ if (candidate !== nextSource) {
262
+ nextSource = candidate;
263
+ return;
264
+ }
265
+ }
266
+ nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
267
+ };
268
+ const appendPhpSnippet = (snippet) => {
269
+ const closingTagPattern = /\?>\s*$/u;
270
+ if (closingTagPattern.test(nextSource)) {
271
+ nextSource = nextSource.replace(closingTagPattern, `${snippet}\n?>`);
272
+ return;
273
+ }
274
+ nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
275
+ };
276
+ if (!hasPhpFunctionDefinition(bindingRegistrationFunctionName)) {
277
+ insertPhpSnippet(bindingRegistrationFunction);
278
+ }
279
+ if (!hasPhpFunctionDefinition(bindingEditorEnqueueFunctionName)) {
280
+ insertPhpSnippet(bindingEditorEnqueueFunction);
281
+ }
282
+ if (!nextSource.includes(bindingRegistrationHook)) {
283
+ appendPhpSnippet(bindingRegistrationHook);
284
+ }
285
+ if (!nextSource.includes(bindingEditorEnqueueHook)) {
286
+ appendPhpSnippet(bindingEditorEnqueueHook);
287
+ }
288
+ return nextSource;
289
+ });
290
+ }
291
+ function resolveBindingSourceRegistryPath(projectDir) {
292
+ const bindingsDir = path.join(projectDir, "src", "bindings");
293
+ return [path.join(bindingsDir, "index.ts"), path.join(bindingsDir, "index.js")].find((candidatePath) => fs.existsSync(candidatePath)) ?? path.join(bindingsDir, "index.ts");
294
+ }
295
+ async function writeBindingSourceRegistry(projectDir, bindingSourceSlug) {
296
+ const bindingsDir = path.join(projectDir, "src", "bindings");
297
+ const bindingsIndexPath = resolveBindingSourceRegistryPath(projectDir);
298
+ await fsp.mkdir(bindingsDir, { recursive: true });
299
+ const existingBindingSourceSlugs = fs
300
+ .readdirSync(bindingsDir, { withFileTypes: true })
301
+ .filter((entry) => entry.isDirectory())
302
+ .map((entry) => entry.name);
303
+ const nextBindingSourceSlugs = Array.from(new Set([...existingBindingSourceSlugs, bindingSourceSlug])).sort();
304
+ await fsp.writeFile(bindingsIndexPath, buildBindingSourceIndexSource(nextBindingSourceSlugs), "utf8");
305
+ }
306
+ /**
307
+ * Add one PHP block pattern shell to an official workspace project.
308
+ *
309
+ * @param options Command options for the pattern scaffold workflow.
310
+ * @param options.cwd Working directory used to resolve the nearest official workspace.
311
+ * Defaults to `process.cwd()`.
312
+ * @param options.patternName Human-entered pattern name that will be normalized
313
+ * and validated before files are written.
314
+ * @returns A promise that resolves with the normalized `patternSlug` and
315
+ * owning `projectDir` after the pattern file and inventory entry have been
316
+ * written successfully.
317
+ * @throws {Error} When the command is run outside an official workspace, when
318
+ * the pattern slug is invalid, or when a conflicting file or inventory entry
319
+ * already exists.
320
+ */
321
+ export async function runAddPatternCommand({ cwd = process.cwd(), patternName, }) {
322
+ const workspace = resolveWorkspaceProject(cwd);
323
+ const patternSlug = assertValidGeneratedSlug("Pattern name", normalizeBlockSlug(patternName), "wp-typia add pattern <name>");
324
+ const inventory = readWorkspaceInventory(workspace.projectDir);
325
+ assertPatternDoesNotExist(workspace.projectDir, patternSlug, inventory);
326
+ const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
327
+ const bootstrapPath = getWorkspaceBootstrapPath(workspace);
328
+ const patternFilePath = path.join(workspace.projectDir, "src", "patterns", `${patternSlug}.php`);
329
+ const mutationSnapshot = {
330
+ fileSources: await snapshotWorkspaceFiles([blockConfigPath, bootstrapPath]),
331
+ snapshotDirs: [],
332
+ targetPaths: [patternFilePath],
333
+ };
334
+ try {
335
+ await fsp.mkdir(path.dirname(patternFilePath), { recursive: true });
336
+ await ensurePatternBootstrapAnchors(workspace);
337
+ await fsp.writeFile(patternFilePath, buildPatternSource(patternSlug, workspace.workspace.namespace, workspace.workspace.textDomain), "utf8");
338
+ await appendWorkspaceInventoryEntries(workspace.projectDir, {
339
+ patternEntries: [buildPatternConfigEntry(patternSlug)],
340
+ });
341
+ return {
342
+ patternSlug,
343
+ projectDir: workspace.projectDir,
344
+ };
345
+ }
346
+ catch (error) {
347
+ await rollbackWorkspaceMutation(mutationSnapshot);
348
+ throw error;
349
+ }
350
+ }
351
+ /**
352
+ * Add one block binding source scaffold to an official workspace project.
353
+ *
354
+ * @param options Command options for the binding-source scaffold workflow.
355
+ * @param options.bindingSourceName Human-entered binding source name that will
356
+ * be normalized and validated before files are written.
357
+ * @param options.cwd Working directory used to resolve the nearest official
358
+ * workspace. Defaults to `process.cwd()`.
359
+ * @returns A promise that resolves with the normalized `bindingSourceSlug` and
360
+ * owning `projectDir` after the server/editor files and inventory entry have
361
+ * been written successfully.
362
+ * @throws {Error} When the command is run outside an official workspace, when
363
+ * the slug is invalid, or when a conflicting file or inventory entry exists.
364
+ */
365
+ export async function runAddBindingSourceCommand({ bindingSourceName, cwd = process.cwd(), }) {
366
+ const workspace = resolveWorkspaceProject(cwd);
367
+ const bindingSourceSlug = assertValidGeneratedSlug("Binding source name", normalizeBlockSlug(bindingSourceName), "wp-typia add binding-source <name>");
368
+ const inventory = readWorkspaceInventory(workspace.projectDir);
369
+ assertBindingSourceDoesNotExist(workspace.projectDir, bindingSourceSlug, inventory);
370
+ const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
371
+ const bootstrapPath = getWorkspaceBootstrapPath(workspace);
372
+ const bindingsIndexPath = resolveBindingSourceRegistryPath(workspace.projectDir);
373
+ const bindingSourceDir = path.join(workspace.projectDir, "src", "bindings", bindingSourceSlug);
374
+ const serverFilePath = path.join(bindingSourceDir, "server.php");
375
+ const editorFilePath = path.join(bindingSourceDir, "editor.ts");
376
+ const mutationSnapshot = {
377
+ fileSources: await snapshotWorkspaceFiles([blockConfigPath, bootstrapPath, bindingsIndexPath]),
378
+ snapshotDirs: [],
379
+ targetPaths: [bindingSourceDir],
380
+ };
381
+ try {
382
+ await fsp.mkdir(bindingSourceDir, { recursive: true });
383
+ await ensureBindingSourceBootstrapAnchors(workspace);
384
+ await fsp.writeFile(serverFilePath, buildBindingSourceServerSource(bindingSourceSlug, workspace.workspace.phpPrefix, workspace.workspace.namespace, workspace.workspace.textDomain), "utf8");
385
+ await fsp.writeFile(editorFilePath, buildBindingSourceEditorSource(bindingSourceSlug, workspace.workspace.namespace, workspace.workspace.textDomain), "utf8");
386
+ await writeBindingSourceRegistry(workspace.projectDir, bindingSourceSlug);
387
+ await appendWorkspaceInventoryEntries(workspace.projectDir, {
388
+ bindingSourceEntries: [buildBindingSourceConfigEntry(bindingSourceSlug)],
389
+ });
390
+ return {
391
+ bindingSourceSlug,
392
+ projectDir: workspace.projectDir,
393
+ };
394
+ }
395
+ catch (error) {
396
+ await rollbackWorkspaceMutation(mutationSnapshot);
397
+ throw error;
398
+ }
399
+ }
@@ -1,5 +1,6 @@
1
1
  import type { HookedBlockPositionId } from "./hooked-blocks.js";
2
- import { type RunAddBindingSourceCommandOptions, type RunAddHookedBlockCommandOptions, type RunAddPatternCommandOptions, type RunAddVariationCommandOptions } from "./cli-add-shared.js";
2
+ import { type RunAddHookedBlockCommandOptions, type RunAddVariationCommandOptions } from "./cli-add-shared.js";
3
+ export { runAddBindingSourceCommand, runAddPatternCommand, } from "./cli-add-workspace-assets.js";
3
4
  /**
4
5
  * Add one variation entry to an existing workspace block.
5
6
  *
@@ -21,43 +22,6 @@ export declare function runAddVariationCommand({ blockName, cwd, variationName,
21
22
  projectDir: string;
22
23
  variationSlug: string;
23
24
  }>;
24
- /**
25
- * Add one PHP block pattern shell to an official workspace project.
26
- *
27
- * @param options Command options for the pattern scaffold workflow.
28
- * @param options.cwd Working directory used to resolve the nearest official workspace.
29
- * Defaults to `process.cwd()`.
30
- * @param options.patternName Human-entered pattern name that will be normalized
31
- * and validated before files are written.
32
- * @returns A promise that resolves with the normalized `patternSlug` and
33
- * owning `projectDir` after the pattern file and inventory entry have been
34
- * written successfully.
35
- * @throws {Error} When the command is run outside an official workspace, when
36
- * the pattern slug is invalid, or when a conflicting file or inventory entry
37
- * already exists.
38
- */
39
- export declare function runAddPatternCommand({ cwd, patternName, }: RunAddPatternCommandOptions): Promise<{
40
- patternSlug: string;
41
- projectDir: string;
42
- }>;
43
- /**
44
- * Add one block binding source scaffold to an official workspace project.
45
- *
46
- * @param options Command options for the binding-source scaffold workflow.
47
- * @param options.bindingSourceName Human-entered binding source name that will
48
- * be normalized and validated before files are written.
49
- * @param options.cwd Working directory used to resolve the nearest official
50
- * workspace. Defaults to `process.cwd()`.
51
- * @returns A promise that resolves with the normalized `bindingSourceSlug` and
52
- * owning `projectDir` after the server/editor files and inventory entry have
53
- * been written successfully.
54
- * @throws {Error} When the command is run outside an official workspace, when
55
- * the slug is invalid, or when a conflicting file or inventory entry exists.
56
- */
57
- export declare function runAddBindingSourceCommand({ bindingSourceName, cwd, }: RunAddBindingSourceCommandOptions): Promise<{
58
- bindingSourceSlug: string;
59
- projectDir: string;
60
- }>;
61
25
  /**
62
26
  * Add one `blockHooks` entry to an existing official workspace block.
63
27
  *