@wp-typia/project-tools 0.16.11 → 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 (101) 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/migration-command-surface.d.ts +67 -0
  45. package/dist/runtime/migration-command-surface.js +189 -0
  46. package/dist/runtime/migration-diff-rename.d.ts +13 -0
  47. package/dist/runtime/migration-diff-rename.js +192 -0
  48. package/dist/runtime/migration-diff-transform.d.ts +14 -0
  49. package/dist/runtime/migration-diff-transform.js +105 -0
  50. package/dist/runtime/migration-diff.js +12 -297
  51. package/dist/runtime/migration-generated-artifacts.d.ts +3 -0
  52. package/dist/runtime/migration-generated-artifacts.js +41 -0
  53. package/dist/runtime/migration-maintenance.d.ts +51 -0
  54. package/dist/runtime/migration-maintenance.js +380 -0
  55. package/dist/runtime/migration-planning.d.ts +23 -0
  56. package/dist/runtime/migration-planning.js +131 -0
  57. package/dist/runtime/migration-project-config-source.d.ts +6 -0
  58. package/dist/runtime/migration-project-config-source.js +424 -0
  59. package/dist/runtime/migration-project-layout-discovery.d.ts +61 -0
  60. package/dist/runtime/migration-project-layout-discovery.js +337 -0
  61. package/dist/runtime/migration-project-layout-paths.d.ts +135 -0
  62. package/dist/runtime/migration-project-layout-paths.js +288 -0
  63. package/dist/runtime/migration-project-layout.d.ts +3 -0
  64. package/dist/runtime/migration-project-layout.js +2 -0
  65. package/dist/runtime/migration-project-workspace.d.ts +47 -0
  66. package/dist/runtime/migration-project-workspace.js +212 -0
  67. package/dist/runtime/migration-project.d.ts +4 -94
  68. package/dist/runtime/migration-project.js +3 -1101
  69. package/dist/runtime/migration-render-diff-rule.d.ts +5 -0
  70. package/dist/runtime/migration-render-diff-rule.js +120 -0
  71. package/dist/runtime/migration-render-execution.d.ts +3 -0
  72. package/dist/runtime/migration-render-execution.js +428 -0
  73. package/dist/runtime/migration-render-generated.d.ts +27 -0
  74. package/dist/runtime/migration-render-generated.js +230 -0
  75. package/dist/runtime/migration-render-support.d.ts +3 -0
  76. package/dist/runtime/migration-render-support.js +16 -0
  77. package/dist/runtime/migration-render.d.ts +3 -33
  78. package/dist/runtime/migration-render.js +3 -789
  79. package/dist/runtime/migrations.d.ts +24 -118
  80. package/dist/runtime/migrations.js +12 -700
  81. package/dist/runtime/scaffold-bootstrap.d.ts +45 -0
  82. package/dist/runtime/scaffold-bootstrap.js +185 -0
  83. package/dist/runtime/scaffold-package-manager-files.d.ts +35 -0
  84. package/dist/runtime/scaffold-package-manager-files.js +79 -0
  85. package/dist/runtime/scaffold.d.ts +1 -12
  86. package/dist/runtime/scaffold.js +10 -393
  87. package/dist/runtime/template-source-contracts.d.ts +81 -0
  88. package/dist/runtime/template-source-contracts.js +1 -0
  89. package/dist/runtime/template-source-external.d.ts +21 -0
  90. package/dist/runtime/template-source-external.js +184 -0
  91. package/dist/runtime/template-source-locators.d.ts +4 -0
  92. package/dist/runtime/template-source-locators.js +72 -0
  93. package/dist/runtime/template-source-normalization.d.ts +7 -0
  94. package/dist/runtime/template-source-normalization.js +53 -0
  95. package/dist/runtime/template-source-remote.d.ts +23 -0
  96. package/dist/runtime/template-source-remote.js +336 -0
  97. package/dist/runtime/template-source-seeds.d.ts +12 -0
  98. package/dist/runtime/template-source-seeds.js +243 -0
  99. package/dist/runtime/template-source.d.ts +4 -86
  100. package/dist/runtime/template-source.js +9 -828
  101. package/package.json +4 -4
@@ -1,22 +1,12 @@
1
1
  import fs from "node:fs";
2
2
  import { promises as fsp } from "node:fs";
3
3
  import path from "node:path";
4
- import { resolveWorkspaceProject, } from "./workspace-project.js";
5
- import { appendWorkspaceInventoryEntries, readWorkspaceInventory, } from "./workspace-inventory.js";
6
- import { toKebabCase, toTitleCase, } from "./string-case.js";
7
- import { assertBindingSourceDoesNotExist, assertPatternDoesNotExist, assertValidGeneratedSlug, assertValidHookAnchor, assertValidHookedBlockPosition, assertVariationDoesNotExist, getMutableBlockHooks, getWorkspaceBootstrapPath, normalizeBlockSlug, patchFile, quoteTsString, readWorkspaceBlockJson, resolveWorkspaceBlock, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
4
+ import { resolveWorkspaceProject } from "./workspace-project.js";
5
+ import { appendWorkspaceInventoryEntries, readWorkspaceInventory } from "./workspace-inventory.js";
6
+ import { toKebabCase, toTitleCase } from "./string-case.js";
7
+ import { assertValidGeneratedSlug, assertValidHookAnchor, assertValidHookedBlockPosition, assertVariationDoesNotExist, getMutableBlockHooks, normalizeBlockSlug, patchFile, quoteTsString, readWorkspaceBlockJson, resolveWorkspaceBlock, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
8
8
  const VARIATIONS_IMPORT_LINE = "import { registerWorkspaceVariations } from './variations';";
9
9
  const VARIATIONS_CALL_LINE = "registerWorkspaceVariations();";
10
- const PATTERN_BOOTSTRAP_CATEGORY = "register_block_pattern_category";
11
- const BINDING_SOURCE_SERVER_GLOB = "/src/bindings/*/server.php";
12
- const BINDING_SOURCE_EDITOR_SCRIPT = "build/bindings/index.js";
13
- const BINDING_SOURCE_EDITOR_ASSET = "build/bindings/index.asset.php";
14
- function escapeRegex(value) {
15
- return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
16
- }
17
- function quotePhpString(value) {
18
- return `'${value.replace(/\\/gu, "\\\\").replace(/'/gu, "\\'")}'`;
19
- }
20
10
  function buildVariationConfigEntry(blockSlug, variationSlug) {
21
11
  return [
22
12
  "\t{",
@@ -26,23 +16,6 @@ function buildVariationConfigEntry(blockSlug, variationSlug) {
26
16
  "\t},",
27
17
  ].join("\n");
28
18
  }
29
- function buildPatternConfigEntry(patternSlug) {
30
- return [
31
- "\t{",
32
- `\t\tfile: ${quoteTsString(`src/patterns/${patternSlug}.php`)},`,
33
- `\t\tslug: ${quoteTsString(patternSlug)},`,
34
- "\t},",
35
- ].join("\n");
36
- }
37
- function buildBindingSourceConfigEntry(bindingSourceSlug) {
38
- return [
39
- "\t{",
40
- `\t\teditorFile: ${quoteTsString(`src/bindings/${bindingSourceSlug}/editor.ts`)},`,
41
- `\t\tserverFile: ${quoteTsString(`src/bindings/${bindingSourceSlug}/server.php`)},`,
42
- `\t\tslug: ${quoteTsString(bindingSourceSlug)},`,
43
- "\t},",
44
- ].join("\n");
45
- }
46
19
  function buildVariationConstName(variationSlug) {
47
20
  const identifierSegments = toKebabCase(variationSlug)
48
21
  .split("-")
@@ -105,124 +78,6 @@ export function registerWorkspaceVariations() {
105
78
  }
106
79
  `;
107
80
  }
108
- function buildPatternSource(patternSlug, namespace, textDomain) {
109
- const patternTitle = toTitleCase(patternSlug);
110
- return `<?php
111
- if ( ! defined( 'ABSPATH' ) ) {
112
- \treturn;
113
- }
114
-
115
- register_block_pattern(
116
- \t'${namespace}/${patternSlug}',
117
- \tarray(
118
- \t\t'title' => __( ${JSON.stringify(patternTitle)}, '${textDomain}' ),
119
- \t\t'description' => __( ${JSON.stringify(`A starter pattern for ${patternTitle}.`)}, '${textDomain}' ),
120
- \t\t'categories' => array( '${namespace}' ),
121
- \t\t'content' => '<!-- wp:paragraph --><p>' . esc_html__( 'Describe this pattern here.', '${textDomain}' ) . '</p><!-- /wp:paragraph -->',
122
- \t)
123
- );
124
- `;
125
- }
126
- function buildBindingSourceServerSource(bindingSourceSlug, phpPrefix, namespace, textDomain) {
127
- const bindingSourceTitle = toTitleCase(bindingSourceSlug);
128
- const bindingSourcePhpId = bindingSourceSlug.replace(/-/g, "_");
129
- const bindingSourceValueFunctionName = `${phpPrefix}_${bindingSourcePhpId}_binding_source_values`;
130
- const bindingSourceResolveFunctionName = `${phpPrefix}_${bindingSourcePhpId}_resolve_binding_source_value`;
131
- const starterValue = `${bindingSourceTitle} starter value`;
132
- return `<?php
133
- if ( ! defined( 'ABSPATH' ) ) {
134
- \treturn;
135
- }
136
-
137
- if ( ! function_exists( 'register_block_bindings_source' ) ) {
138
- \treturn;
139
- }
140
-
141
- if ( ! function_exists( '${bindingSourceValueFunctionName}' ) ) {
142
- \tfunction ${bindingSourceValueFunctionName}() : array {
143
- \t\treturn array(
144
- \t\t\t${quotePhpString(bindingSourceSlug)} => ${quotePhpString(starterValue)},
145
- \t\t);
146
- \t}
147
- }
148
-
149
- if ( ! function_exists( '${bindingSourceResolveFunctionName}' ) ) {
150
- \tfunction ${bindingSourceResolveFunctionName}( array $source_args ) : string {
151
- \t\t$field = isset( $source_args['field'] ) && is_string( $source_args['field'] )
152
- \t\t\t? $source_args['field']
153
- \t\t\t: '${bindingSourceSlug}';
154
- \t\t$binding_source_values = ${bindingSourceValueFunctionName}();
155
- \t\t$value = $binding_source_values[ $field ] ?? '';
156
-
157
- \t\treturn is_string( $value ) ? $value : '';
158
- \t}
159
- }
160
-
161
- register_block_bindings_source(
162
- \t${quotePhpString(`${namespace}/${bindingSourceSlug}`)},
163
- \tarray(
164
- \t\t'label' => __( ${quotePhpString(bindingSourceTitle)}, ${quotePhpString(textDomain)} ),
165
- \t\t'get_value_callback' => ${quotePhpString(bindingSourceResolveFunctionName)},
166
- \t)
167
- );
168
- `;
169
- }
170
- function buildBindingSourceEditorSource(bindingSourceSlug, namespace, textDomain) {
171
- const bindingSourceTitle = toTitleCase(bindingSourceSlug);
172
- const starterValue = `${bindingSourceTitle} starter value`;
173
- return `import { registerBlockBindingsSource } from '@wordpress/blocks';
174
- import { __ } from '@wordpress/i18n';
175
-
176
- interface BindingSourceRegistration {
177
- \targs?: {
178
- \t\tfield?: string;
179
- \t};
180
- }
181
-
182
- const BINDING_SOURCE_VALUES: Record<string, string> = {
183
- \t${quoteTsString(bindingSourceSlug)}: ${quoteTsString(starterValue)},
184
- };
185
-
186
- function resolveBindingSourceValue( field: string ): string {
187
- \treturn BINDING_SOURCE_VALUES[ field ] ?? '';
188
- }
189
-
190
- registerBlockBindingsSource( {
191
- \tname: ${quoteTsString(`${namespace}/${bindingSourceSlug}`)},
192
- \tlabel: __( ${quoteTsString(bindingSourceTitle)}, ${quoteTsString(textDomain)} ),
193
- \tgetFieldsList() {
194
- \t\treturn [
195
- \t\t\t{
196
- \t\t\t\tlabel: __( ${quoteTsString(bindingSourceTitle)}, ${quoteTsString(textDomain)} ),
197
- \t\t\t\ttype: 'string',
198
- \t\t\t\targs: {
199
- \t\t\t\t\tfield: ${quoteTsString(bindingSourceSlug)},
200
- \t\t\t\t},
201
- \t\t\t},
202
- \t\t];
203
- \t},
204
- \tgetValues( { bindings } ) {
205
- \t\tconst values: Record<string, string> = {};
206
- \t\tfor ( const [ attributeName, binding ] of Object.entries(
207
- \t\t\tbindings as Record<string, BindingSourceRegistration>
208
- \t\t) ) {
209
- \t\t\tconst field =
210
- \t\t\t\ttypeof binding?.args?.field === 'string'
211
- \t\t\t\t\t? binding.args.field
212
- \t\t\t\t\t: ${quoteTsString(bindingSourceSlug)};
213
- \t\t\tvalues[ attributeName ] = resolveBindingSourceValue( field );
214
- \t\t}
215
- \t\treturn values;
216
- \t},
217
- } );
218
- `;
219
- }
220
- function buildBindingSourceIndexSource(bindingSourceSlugs) {
221
- const importLines = bindingSourceSlugs
222
- .map((bindingSourceSlug) => `import './${bindingSourceSlug}/editor';`)
223
- .join("\n");
224
- return `${importLines}${importLines ? "\n\n" : ""}// wp-typia add binding-source entries\n`;
225
- }
226
81
  async function ensureVariationRegistrationHook(blockIndexPath) {
227
82
  await patchFile(blockIndexPath, (source) => {
228
83
  let nextSource = source;
@@ -264,159 +119,7 @@ async function writeVariationRegistry(projectDir, blockSlug, variationSlug) {
264
119
  const nextVariationSlugs = Array.from(new Set([...existingVariationSlugs, variationSlug])).sort();
265
120
  await fsp.writeFile(variationsIndexPath, buildVariationIndexSource(nextVariationSlugs), "utf8");
266
121
  }
267
- async function ensurePatternBootstrapAnchors(workspace) {
268
- const workspaceBaseName = workspace.packageName.split("/").pop() ?? workspace.packageName;
269
- const bootstrapPath = getWorkspaceBootstrapPath(workspace);
270
- await patchFile(bootstrapPath, (source) => {
271
- let nextSource = source;
272
- const patternCategoryFunctionName = `${workspace.workspace.phpPrefix}_register_pattern_category`;
273
- const patternRegistrationFunctionName = `${workspace.workspace.phpPrefix}_register_patterns`;
274
- const patternCategoryHook = `add_action( 'init', '${patternCategoryFunctionName}' );`;
275
- const patternRegistrationHook = `add_action( 'init', '${patternRegistrationFunctionName}', 20 );`;
276
- const patternFunctions = `
277
-
278
- function ${patternCategoryFunctionName}() {
279
- \tif ( function_exists( 'register_block_pattern_category' ) ) {
280
- \t\tregister_block_pattern_category(
281
- \t\t\t'${workspace.workspace.namespace}',
282
- \t\t\tarray(
283
- \t\t\t\t'label' => __( ${JSON.stringify(`${toTitleCase(workspaceBaseName)} Patterns`)}, '${workspace.workspace.textDomain}' ),
284
- \t\t\t)
285
- \t\t);
286
- \t}
287
- }
288
-
289
- function ${patternRegistrationFunctionName}() {
290
- \tforeach ( glob( __DIR__ . '/src/patterns/*.php' ) ?: array() as $pattern_module ) {
291
- \t\trequire $pattern_module;
292
- \t}
293
- }
294
- `;
295
- if (!nextSource.includes(PATTERN_BOOTSTRAP_CATEGORY)) {
296
- const insertionAnchors = [
297
- /add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
298
- /\?>\s*$/u,
299
- ];
300
- let inserted = false;
301
- for (const anchor of insertionAnchors) {
302
- const candidate = nextSource.replace(anchor, (match) => `${patternFunctions}\n${match}`);
303
- if (candidate !== nextSource) {
304
- nextSource = candidate;
305
- inserted = true;
306
- break;
307
- }
308
- }
309
- if (!inserted) {
310
- nextSource = `${nextSource.trimEnd()}\n${patternFunctions}\n`;
311
- }
312
- }
313
- if (!nextSource.includes(patternCategoryFunctionName) ||
314
- !nextSource.includes(patternRegistrationFunctionName)) {
315
- throw new Error(`Unable to inject pattern bootstrap functions into ${path.basename(bootstrapPath)}.`);
316
- }
317
- if (!nextSource.includes(patternCategoryHook)) {
318
- nextSource = `${nextSource.trimEnd()}\n${patternCategoryHook}\n`;
319
- }
320
- if (!nextSource.includes(patternRegistrationHook)) {
321
- nextSource = `${nextSource.trimEnd()}\n${patternRegistrationHook}\n`;
322
- }
323
- return nextSource;
324
- });
325
- }
326
- async function ensureBindingSourceBootstrapAnchors(workspace) {
327
- const bootstrapPath = getWorkspaceBootstrapPath(workspace);
328
- await patchFile(bootstrapPath, (source) => {
329
- let nextSource = source;
330
- const workspaceBaseName = workspace.packageName.split("/").pop() ?? workspace.packageName;
331
- const bindingRegistrationFunctionName = `${workspace.workspace.phpPrefix}_register_binding_sources`;
332
- const bindingEditorEnqueueFunctionName = `${workspace.workspace.phpPrefix}_enqueue_binding_sources_editor`;
333
- const bindingRegistrationHook = `add_action( 'init', '${bindingRegistrationFunctionName}', 20 );`;
334
- const bindingEditorEnqueueHook = `add_action( 'enqueue_block_editor_assets', '${bindingEditorEnqueueFunctionName}' );`;
335
- const bindingRegistrationFunction = `
336
-
337
- function ${bindingRegistrationFunctionName}() {
338
- \tforeach ( glob( __DIR__ . '${BINDING_SOURCE_SERVER_GLOB}' ) ?: array() as $binding_source_module ) {
339
- \t\trequire_once $binding_source_module;
340
- \t}
341
- }
342
- `;
343
- const bindingEditorEnqueueFunction = `
344
-
345
- function ${bindingEditorEnqueueFunctionName}() {
346
- \t$script_path = __DIR__ . '/${BINDING_SOURCE_EDITOR_SCRIPT}';
347
- \t$asset_path = __DIR__ . '/${BINDING_SOURCE_EDITOR_ASSET}';
348
-
349
- \tif ( ! file_exists( $script_path ) || ! file_exists( $asset_path ) ) {
350
- \t\treturn;
351
- \t}
352
-
353
- \t$asset = require $asset_path;
354
- \tif ( ! is_array( $asset ) ) {
355
- \t\t$asset = array();
356
- \t}
357
-
358
- \twp_enqueue_script(
359
- \t\t'${workspaceBaseName}-binding-sources',
360
- \t\tplugins_url( '${BINDING_SOURCE_EDITOR_SCRIPT}', __FILE__ ),
361
- \t\tisset( $asset['dependencies'] ) && is_array( $asset['dependencies'] ) ? $asset['dependencies'] : array(),
362
- \t\tisset( $asset['version'] ) ? $asset['version'] : filemtime( $script_path ),
363
- \t\ttrue
364
- \t);
365
- }
366
- `;
367
- const insertionAnchors = [
368
- /add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
369
- /\?>\s*$/u,
370
- ];
371
- const hasPhpFunctionDefinition = (functionName) => new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\(`, "u").test(nextSource);
372
- const insertPhpSnippet = (snippet) => {
373
- for (const anchor of insertionAnchors) {
374
- const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
375
- if (candidate !== nextSource) {
376
- nextSource = candidate;
377
- return;
378
- }
379
- }
380
- nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
381
- };
382
- const appendPhpSnippet = (snippet) => {
383
- const closingTagPattern = /\?>\s*$/u;
384
- if (closingTagPattern.test(nextSource)) {
385
- nextSource = nextSource.replace(closingTagPattern, `${snippet}\n?>`);
386
- return;
387
- }
388
- nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
389
- };
390
- if (!hasPhpFunctionDefinition(bindingRegistrationFunctionName)) {
391
- insertPhpSnippet(bindingRegistrationFunction);
392
- }
393
- if (!hasPhpFunctionDefinition(bindingEditorEnqueueFunctionName)) {
394
- insertPhpSnippet(bindingEditorEnqueueFunction);
395
- }
396
- if (!nextSource.includes(bindingRegistrationHook)) {
397
- appendPhpSnippet(bindingRegistrationHook);
398
- }
399
- if (!nextSource.includes(bindingEditorEnqueueHook)) {
400
- appendPhpSnippet(bindingEditorEnqueueHook);
401
- }
402
- return nextSource;
403
- });
404
- }
405
- function resolveBindingSourceRegistryPath(projectDir) {
406
- const bindingsDir = path.join(projectDir, "src", "bindings");
407
- return [path.join(bindingsDir, "index.ts"), path.join(bindingsDir, "index.js")].find((candidatePath) => fs.existsSync(candidatePath)) ?? path.join(bindingsDir, "index.ts");
408
- }
409
- async function writeBindingSourceRegistry(projectDir, bindingSourceSlug) {
410
- const bindingsDir = path.join(projectDir, "src", "bindings");
411
- const bindingsIndexPath = resolveBindingSourceRegistryPath(projectDir);
412
- await fsp.mkdir(bindingsDir, { recursive: true });
413
- const existingBindingSourceSlugs = fs
414
- .readdirSync(bindingsDir, { withFileTypes: true })
415
- .filter((entry) => entry.isDirectory())
416
- .map((entry) => entry.name);
417
- const nextBindingSourceSlugs = Array.from(new Set([...existingBindingSourceSlugs, bindingSourceSlug])).sort();
418
- await fsp.writeFile(bindingsIndexPath, buildBindingSourceIndexSource(nextBindingSourceSlugs), "utf8");
419
- }
122
+ export { runAddBindingSourceCommand, runAddPatternCommand, } from "./cli-add-workspace-assets.js";
420
123
  /**
421
124
  * Add one variation entry to an existing workspace block.
422
125
  *
@@ -473,100 +176,6 @@ export async function runAddVariationCommand({ blockName, cwd = process.cwd(), v
473
176
  throw error;
474
177
  }
475
178
  }
476
- /**
477
- * Add one PHP block pattern shell to an official workspace project.
478
- *
479
- * @param options Command options for the pattern scaffold workflow.
480
- * @param options.cwd Working directory used to resolve the nearest official workspace.
481
- * Defaults to `process.cwd()`.
482
- * @param options.patternName Human-entered pattern name that will be normalized
483
- * and validated before files are written.
484
- * @returns A promise that resolves with the normalized `patternSlug` and
485
- * owning `projectDir` after the pattern file and inventory entry have been
486
- * written successfully.
487
- * @throws {Error} When the command is run outside an official workspace, when
488
- * the pattern slug is invalid, or when a conflicting file or inventory entry
489
- * already exists.
490
- */
491
- export async function runAddPatternCommand({ cwd = process.cwd(), patternName, }) {
492
- const workspace = resolveWorkspaceProject(cwd);
493
- const patternSlug = assertValidGeneratedSlug("Pattern name", normalizeBlockSlug(patternName), "wp-typia add pattern <name>");
494
- const inventory = readWorkspaceInventory(workspace.projectDir);
495
- assertPatternDoesNotExist(workspace.projectDir, patternSlug, inventory);
496
- const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
497
- const bootstrapPath = getWorkspaceBootstrapPath(workspace);
498
- const patternFilePath = path.join(workspace.projectDir, "src", "patterns", `${patternSlug}.php`);
499
- const mutationSnapshot = {
500
- fileSources: await snapshotWorkspaceFiles([blockConfigPath, bootstrapPath]),
501
- snapshotDirs: [],
502
- targetPaths: [patternFilePath],
503
- };
504
- try {
505
- await fsp.mkdir(path.dirname(patternFilePath), { recursive: true });
506
- await ensurePatternBootstrapAnchors(workspace);
507
- await fsp.writeFile(patternFilePath, buildPatternSource(patternSlug, workspace.workspace.namespace, workspace.workspace.textDomain), "utf8");
508
- await appendWorkspaceInventoryEntries(workspace.projectDir, {
509
- patternEntries: [buildPatternConfigEntry(patternSlug)],
510
- });
511
- return {
512
- patternSlug,
513
- projectDir: workspace.projectDir,
514
- };
515
- }
516
- catch (error) {
517
- await rollbackWorkspaceMutation(mutationSnapshot);
518
- throw error;
519
- }
520
- }
521
- /**
522
- * Add one block binding source scaffold to an official workspace project.
523
- *
524
- * @param options Command options for the binding-source scaffold workflow.
525
- * @param options.bindingSourceName Human-entered binding source name that will
526
- * be normalized and validated before files are written.
527
- * @param options.cwd Working directory used to resolve the nearest official
528
- * workspace. Defaults to `process.cwd()`.
529
- * @returns A promise that resolves with the normalized `bindingSourceSlug` and
530
- * owning `projectDir` after the server/editor files and inventory entry have
531
- * been written successfully.
532
- * @throws {Error} When the command is run outside an official workspace, when
533
- * the slug is invalid, or when a conflicting file or inventory entry exists.
534
- */
535
- export async function runAddBindingSourceCommand({ bindingSourceName, cwd = process.cwd(), }) {
536
- const workspace = resolveWorkspaceProject(cwd);
537
- const bindingSourceSlug = assertValidGeneratedSlug("Binding source name", normalizeBlockSlug(bindingSourceName), "wp-typia add binding-source <name>");
538
- const inventory = readWorkspaceInventory(workspace.projectDir);
539
- assertBindingSourceDoesNotExist(workspace.projectDir, bindingSourceSlug, inventory);
540
- const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
541
- const bootstrapPath = getWorkspaceBootstrapPath(workspace);
542
- const bindingsIndexPath = resolveBindingSourceRegistryPath(workspace.projectDir);
543
- const bindingSourceDir = path.join(workspace.projectDir, "src", "bindings", bindingSourceSlug);
544
- const serverFilePath = path.join(bindingSourceDir, "server.php");
545
- const editorFilePath = path.join(bindingSourceDir, "editor.ts");
546
- const mutationSnapshot = {
547
- fileSources: await snapshotWorkspaceFiles([blockConfigPath, bootstrapPath, bindingsIndexPath]),
548
- snapshotDirs: [],
549
- targetPaths: [bindingSourceDir],
550
- };
551
- try {
552
- await fsp.mkdir(bindingSourceDir, { recursive: true });
553
- await ensureBindingSourceBootstrapAnchors(workspace);
554
- await fsp.writeFile(serverFilePath, buildBindingSourceServerSource(bindingSourceSlug, workspace.workspace.phpPrefix, workspace.workspace.namespace, workspace.workspace.textDomain), "utf8");
555
- await fsp.writeFile(editorFilePath, buildBindingSourceEditorSource(bindingSourceSlug, workspace.workspace.namespace, workspace.workspace.textDomain), "utf8");
556
- await writeBindingSourceRegistry(workspace.projectDir, bindingSourceSlug);
557
- await appendWorkspaceInventoryEntries(workspace.projectDir, {
558
- bindingSourceEntries: [buildBindingSourceConfigEntry(bindingSourceSlug)],
559
- });
560
- return {
561
- bindingSourceSlug,
562
- projectDir: workspace.projectDir,
563
- };
564
- }
565
- catch (error) {
566
- await rollbackWorkspaceMutation(mutationSnapshot);
567
- throw error;
568
- }
569
- }
570
179
  /**
571
180
  * Add one `blockHooks` entry to an existing official workspace block.
572
181
  *
@@ -0,0 +1,12 @@
1
+ import type { DoctorCheck } from "./cli-doctor.js";
2
+ /**
3
+ * Collect environment-scoped doctor checks for the current working directory.
4
+ *
5
+ * The returned rows cover Bun/Node/git availability, writability of the
6
+ * current working directory and OS temp directory, and built-in or external
7
+ * template asset integrity in display order.
8
+ *
9
+ * @param cwd Working directory validated for writability.
10
+ * @returns Ordered environment check rows ready for CLI rendering.
11
+ */
12
+ export declare function getEnvironmentDoctorChecks(cwd: string): Promise<DoctorCheck[]>;
@@ -0,0 +1,123 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { access, constants as fsConstants, rm, writeFile } from "node:fs/promises";
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { getBuiltInTemplateLayerDirs, isOmittableBuiltInTemplateLayerDir, } from "./template-builtins.js";
7
+ import { isBuiltInTemplateId, listTemplates } from "./template-registry.js";
8
+ function readCommandVersion(command, args = ["--version"]) {
9
+ try {
10
+ return execFileSync(command, args, {
11
+ encoding: "utf8",
12
+ stdio: ["ignore", "pipe", "ignore"],
13
+ }).trim();
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ function compareMajorVersion(actualVersion, minimumMajor) {
20
+ const parsed = Number.parseInt(actualVersion.replace(/^v/, "").split(".")[0] ?? "", 10);
21
+ return Number.isFinite(parsed) && parsed >= minimumMajor;
22
+ }
23
+ async function checkWritableDirectory(directory) {
24
+ try {
25
+ await access(directory, fsConstants.W_OK);
26
+ return true;
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
32
+ async function checkTempDirectory() {
33
+ const tempFile = path.join(os.tmpdir(), `wp-typia-${Date.now()}.tmp`);
34
+ try {
35
+ await writeFile(tempFile, "ok", "utf8");
36
+ await rm(tempFile, { force: true });
37
+ return true;
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ }
43
+ function createDoctorCheck(label, status, detail) {
44
+ return { detail, label, status };
45
+ }
46
+ function getTemplateDoctorChecks() {
47
+ const checks = [];
48
+ for (const template of listTemplates()) {
49
+ if (!isBuiltInTemplateId(template.id)) {
50
+ const templateDirExists = fs.existsSync(template.templateDir);
51
+ const hasAssets = templateDirExists &&
52
+ fs.existsSync(path.join(template.templateDir, "package.json.mustache"));
53
+ checks.push({
54
+ status: !templateDirExists || hasAssets ? "pass" : "fail",
55
+ label: `Template ${template.id}`,
56
+ detail: !templateDirExists
57
+ ? "External template metadata only; local overlay package is not installed."
58
+ : hasAssets
59
+ ? template.templateDir
60
+ : "Missing core template assets",
61
+ });
62
+ continue;
63
+ }
64
+ const builtInTemplateId = template.id;
65
+ const layerDirs = builtInTemplateId === "persistence"
66
+ ? Array.from(new Set([
67
+ ...getBuiltInTemplateLayerDirs(builtInTemplateId, { persistencePolicy: "authenticated" }),
68
+ ...getBuiltInTemplateLayerDirs(builtInTemplateId, { persistencePolicy: "public" }),
69
+ ]))
70
+ : builtInTemplateId === "compound"
71
+ ? Array.from(new Set([
72
+ ...getBuiltInTemplateLayerDirs(builtInTemplateId),
73
+ ...getBuiltInTemplateLayerDirs(builtInTemplateId, {
74
+ persistenceEnabled: true,
75
+ persistencePolicy: "authenticated",
76
+ }),
77
+ ...getBuiltInTemplateLayerDirs(builtInTemplateId, {
78
+ persistenceEnabled: true,
79
+ persistencePolicy: "public",
80
+ }),
81
+ ]))
82
+ : getBuiltInTemplateLayerDirs(builtInTemplateId);
83
+ const missingRequiredLayer = layerDirs.some((layerDir) => !fs.existsSync(layerDir) &&
84
+ !isOmittableBuiltInTemplateLayerDir(builtInTemplateId, layerDir));
85
+ const existingLayerDirs = layerDirs.filter((layerDir) => fs.existsSync(layerDir));
86
+ const hasAssets = !missingRequiredLayer &&
87
+ existingLayerDirs.some((layerDir) => fs.existsSync(path.join(layerDir, "package.json.mustache"))) &&
88
+ existingLayerDirs.some((layerDir) => fs.existsSync(path.join(layerDir, "src")));
89
+ checks.push({
90
+ status: hasAssets ? "pass" : "fail",
91
+ label: `Template ${template.id}`,
92
+ detail: hasAssets
93
+ ? existingLayerDirs.join(" + ")
94
+ : "Missing core template assets",
95
+ });
96
+ }
97
+ return checks;
98
+ }
99
+ /**
100
+ * Collect environment-scoped doctor checks for the current working directory.
101
+ *
102
+ * The returned rows cover Bun/Node/git availability, writability of the
103
+ * current working directory and OS temp directory, and built-in or external
104
+ * template asset integrity in display order.
105
+ *
106
+ * @param cwd Working directory validated for writability.
107
+ * @returns Ordered environment check rows ready for CLI rendering.
108
+ */
109
+ export async function getEnvironmentDoctorChecks(cwd) {
110
+ const bunVersion = readCommandVersion("bun");
111
+ const nodeVersion = readCommandVersion("node");
112
+ const gitVersion = readCommandVersion("git");
113
+ const cwdWritable = await checkWritableDirectory(cwd);
114
+ const tempWritable = await checkTempDirectory();
115
+ return [
116
+ createDoctorCheck("Bun", bunVersion && compareMajorVersion(bunVersion, 1) ? "pass" : "fail", bunVersion ? `Detected ${bunVersion}` : "Not available"),
117
+ createDoctorCheck("Node", nodeVersion && compareMajorVersion(nodeVersion, 20) ? "pass" : "fail", nodeVersion ? `Detected ${nodeVersion}` : "Not available"),
118
+ createDoctorCheck("git", gitVersion ? "pass" : "fail", gitVersion ?? "Not available"),
119
+ createDoctorCheck("Current directory", cwdWritable ? "pass" : "fail", cwdWritable ? "Writable" : "Not writable"),
120
+ createDoctorCheck("Temp directory", tempWritable ? "pass" : "fail", tempWritable ? "Writable" : "Not writable"),
121
+ ...getTemplateDoctorChecks(),
122
+ ];
123
+ }
@@ -0,0 +1,14 @@
1
+ import type { DoctorCheck } from "./cli-doctor.js";
2
+ /**
3
+ * Collect workspace-scoped doctor checks for the given working directory.
4
+ *
5
+ * When the directory is not an official workspace, the function returns an
6
+ * empty array or a single failing "Workspace package metadata" row describing
7
+ * the reason. When workspace resolution or metadata parsing throws, the
8
+ * corresponding failing row is returned early and the remaining checks are
9
+ * skipped.
10
+ *
11
+ * @param cwd Working directory expected to host an official workspace.
12
+ * @returns Ordered workspace check rows ready for CLI rendering.
13
+ */
14
+ export declare function getWorkspaceDoctorChecks(cwd: string): DoctorCheck[];