@wp-typia/project-tools 0.22.4 → 0.22.5

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.
@@ -2,6 +2,7 @@ import { promises as fsp } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { getPackageVersions } from "./package-versions.js";
4
4
  import { getWorkspaceBootstrapPath, patchFile, } from "./cli-add-shared.js";
5
+ import { appendPhpSnippetBeforeClosingTag, insertPhpSnippetBeforeWorkspaceAnchors, } from "./cli-add-workspace-mutation.js";
5
6
  import { hasPhpFunctionDefinition } from "./php-utils.js";
6
7
  const AI_FEATURE_SERVER_GLOB = "/inc/ai-features/*.php";
7
8
  /**
@@ -21,30 +22,8 @@ function ${registerFunctionName}() {
21
22
  \t}
22
23
  }
23
24
  `;
24
- const insertionAnchors = [
25
- /add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
26
- /\?>\s*$/u,
27
- ];
28
- const insertPhpSnippet = (snippet) => {
29
- for (const anchor of insertionAnchors) {
30
- const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
31
- if (candidate !== nextSource) {
32
- nextSource = candidate;
33
- return;
34
- }
35
- }
36
- nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
37
- };
38
- const appendPhpSnippet = (snippet) => {
39
- const closingTagPattern = /\?>\s*$/u;
40
- if (closingTagPattern.test(nextSource)) {
41
- nextSource = nextSource.replace(closingTagPattern, `${snippet}\n?>`);
42
- return;
43
- }
44
- nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
45
- };
46
25
  if (!hasPhpFunctionDefinition(nextSource, registerFunctionName)) {
47
- insertPhpSnippet(registerFunction);
26
+ nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, registerFunction);
48
27
  }
49
28
  else if (!nextSource.includes(AI_FEATURE_SERVER_GLOB)) {
50
29
  throw new Error([
@@ -54,7 +33,7 @@ function ${registerFunctionName}() {
54
33
  ].join(" "));
55
34
  }
56
35
  if (!nextSource.includes(registerHook)) {
57
- appendPhpSnippet(registerHook);
36
+ nextSource = appendPhpSnippetBeforeClosingTag(nextSource, registerHook);
58
37
  }
59
38
  return nextSource;
60
39
  });
@@ -8,6 +8,7 @@ import { readWorkspaceInventory, appendWorkspaceInventoryEntries, } from "./work
8
8
  import { toPascalCase, toTitleCase } from "./string-case.js";
9
9
  import { findPhpFunctionRange, hasPhpFunctionDefinition, quotePhpString, replacePhpFunctionDefinition, } from "./php-utils.js";
10
10
  import { assertBindingSourceDoesNotExist, assertEditorPluginDoesNotExist, assertPatternDoesNotExist, assertValidEditorPluginSlot, assertValidGeneratedSlug, getWorkspaceBootstrapPath, normalizeBlockSlug, patchFile, quoteTsString, resolveWorkspaceBlock, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
11
+ import { appendPhpSnippetBeforeClosingTag, insertPhpSnippetBeforeWorkspaceAnchors, } from "./cli-add-workspace-mutation.js";
11
12
  import { normalizeOptionalCliString } from "./cli-validation.js";
12
13
  const PATTERN_BOOTSTRAP_CATEGORY = "register_block_pattern_category";
13
14
  const BINDING_SOURCE_SERVER_GLOB = "/src/bindings/*/server.php";
@@ -597,39 +598,17 @@ function ${bindingEditorEnqueueFunctionName}() {
597
598
  \t);
598
599
  }
599
600
  `;
600
- const insertionAnchors = [
601
- /add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
602
- /\?>\s*$/u,
603
- ];
604
- const insertPhpSnippet = (snippet) => {
605
- for (const anchor of insertionAnchors) {
606
- const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
607
- if (candidate !== nextSource) {
608
- nextSource = candidate;
609
- return;
610
- }
611
- }
612
- nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
613
- };
614
- const appendPhpSnippet = (snippet) => {
615
- const closingTagPattern = /\?>\s*$/u;
616
- if (closingTagPattern.test(nextSource)) {
617
- nextSource = nextSource.replace(closingTagPattern, `${snippet}\n?>`);
618
- return;
619
- }
620
- nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
621
- };
622
601
  if (!hasPhpFunctionDefinition(nextSource, bindingRegistrationFunctionName)) {
623
- insertPhpSnippet(bindingRegistrationFunction);
602
+ nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, bindingRegistrationFunction);
624
603
  }
625
604
  if (!hasPhpFunctionDefinition(nextSource, bindingEditorEnqueueFunctionName)) {
626
- insertPhpSnippet(bindingEditorEnqueueFunction);
605
+ nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, bindingEditorEnqueueFunction);
627
606
  }
628
607
  if (!nextSource.includes(bindingRegistrationHook)) {
629
- appendPhpSnippet(bindingRegistrationHook);
608
+ nextSource = appendPhpSnippetBeforeClosingTag(nextSource, bindingRegistrationHook);
630
609
  }
631
610
  if (!nextSource.includes(bindingEditorEnqueueHook)) {
632
- appendPhpSnippet(bindingEditorEnqueueHook);
611
+ nextSource = appendPhpSnippetBeforeClosingTag(nextSource, bindingEditorEnqueueHook);
633
612
  }
634
613
  return nextSource;
635
614
  });
@@ -679,30 +658,8 @@ function ${enqueueFunctionName}() {
679
658
  \t}
680
659
  }
681
660
  `;
682
- const insertionAnchors = [
683
- /add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
684
- /\?>\s*$/u,
685
- ];
686
- const insertPhpSnippet = (snippet) => {
687
- for (const anchor of insertionAnchors) {
688
- const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
689
- if (candidate !== nextSource) {
690
- nextSource = candidate;
691
- return;
692
- }
693
- }
694
- nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
695
- };
696
- const appendPhpSnippet = (snippet) => {
697
- const closingTagPattern = /\?>\s*$/u;
698
- if (closingTagPattern.test(nextSource)) {
699
- nextSource = nextSource.replace(closingTagPattern, `${snippet}\n?>`);
700
- return;
701
- }
702
- nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
703
- };
704
661
  if (!hasPhpFunctionDefinition(nextSource, enqueueFunctionName)) {
705
- insertPhpSnippet(enqueueFunction);
662
+ nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, enqueueFunction);
706
663
  }
707
664
  else {
708
665
  const requiredReferences = [
@@ -726,7 +683,7 @@ function ${enqueueFunctionName}() {
726
683
  }
727
684
  }
728
685
  if (!nextSource.includes(enqueueHook)) {
729
- appendPhpSnippet(enqueueHook);
686
+ nextSource = appendPhpSnippetBeforeClosingTag(nextSource, enqueueHook);
730
687
  }
731
688
  return nextSource;
732
689
  });
@@ -1,5 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { getWorkspaceBootstrapPath, patchFile, } from "./cli-add-shared.js";
3
+ import { appendPhpSnippetBeforeClosingTag, insertPhpSnippetBeforeWorkspaceAnchors, } from "./cli-add-workspace-mutation.js";
3
4
  import { hasPhpFunctionDefinition } from "./php-utils.js";
4
5
  const REST_RESOURCE_SERVER_GLOB = "/inc/rest/*.php";
5
6
  export async function ensureRestResourceBootstrapAnchors(workspace) {
@@ -16,30 +17,8 @@ function ${registerFunctionName}() {
16
17
  \t}
17
18
  }
18
19
  `;
19
- const insertionAnchors = [
20
- /add_action\(\s*["']init["']\s*,\s*["'][^"']+_load_textdomain["']\s*\);\s*\n/u,
21
- /\?>\s*$/u,
22
- ];
23
- const insertPhpSnippet = (snippet) => {
24
- for (const anchor of insertionAnchors) {
25
- const candidate = nextSource.replace(anchor, (match) => `${snippet}\n${match}`);
26
- if (candidate !== nextSource) {
27
- nextSource = candidate;
28
- return;
29
- }
30
- }
31
- nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
32
- };
33
- const appendPhpSnippet = (snippet) => {
34
- const closingTagPattern = /\?>\s*$/u;
35
- if (closingTagPattern.test(nextSource)) {
36
- nextSource = nextSource.replace(closingTagPattern, `${snippet}\n?>`);
37
- return;
38
- }
39
- nextSource = `${nextSource.trimEnd()}\n${snippet}\n`;
40
- };
41
20
  if (!hasPhpFunctionDefinition(nextSource, registerFunctionName)) {
42
- insertPhpSnippet(registerFunction);
21
+ nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, registerFunction);
43
22
  }
44
23
  else if (!nextSource.includes(REST_RESOURCE_SERVER_GLOB)) {
45
24
  throw new Error([
@@ -49,7 +28,7 @@ function ${registerFunctionName}() {
49
28
  ].join(" "));
50
29
  }
51
30
  if (!nextSource.includes(registerHook)) {
52
- appendPhpSnippet(registerHook);
31
+ nextSource = appendPhpSnippetBeforeClosingTag(nextSource, registerHook);
53
32
  }
54
33
  return nextSource;
55
34
  });
@@ -4,6 +4,9 @@ import { readFile, writeFile } from "node:fs/promises";
4
4
  import ts from "typescript";
5
5
  import { REST_RESOURCE_METHOD_IDS } from "./cli-add-shared.js";
6
6
  import { escapeRegex } from "./php-utils.js";
7
+ function defineInventoryEntryParser(descriptor) {
8
+ return descriptor;
9
+ }
7
10
  export const BLOCK_CONFIG_ENTRY_MARKER = "\t// wp-typia add block entries";
8
11
  export const VARIATION_CONFIG_ENTRY_MARKER = "\t// wp-typia add variation entries";
9
12
  export const BLOCK_STYLE_CONFIG_ENTRY_MARKER = "\t// wp-typia add style entries";
@@ -202,12 +205,41 @@ export const EDITOR_PLUGINS: WorkspaceEditorPluginConfig[] = [
202
205
  \t// wp-typia add editor-plugin entries
203
206
  ];
204
207
  `;
208
+ const BLOCK_INVENTORY_SECTION = {
209
+ parse: {
210
+ entriesKey: "blocks",
211
+ entry: defineInventoryEntryParser({
212
+ entryName: "BLOCKS",
213
+ fields: [
214
+ { key: "apiTypesFile" },
215
+ { key: "attributeTypeName" },
216
+ { key: "openApiFile" },
217
+ { key: "slug", required: true },
218
+ { key: "typesFile", required: true },
219
+ ],
220
+ }),
221
+ exportName: "BLOCKS",
222
+ required: true,
223
+ },
224
+ };
205
225
  const INVENTORY_SECTIONS = [
206
226
  {
207
227
  interface: {
208
228
  name: "WorkspaceVariationConfig",
209
229
  section: VARIATIONS_INTERFACE_SECTION,
210
230
  },
231
+ parse: {
232
+ entriesKey: "variations",
233
+ entry: defineInventoryEntryParser({
234
+ entryName: "VARIATIONS",
235
+ fields: [
236
+ { key: "block", required: true },
237
+ { key: "file", required: true },
238
+ { key: "slug", required: true },
239
+ ],
240
+ }),
241
+ hasSectionKey: "hasVariationsSection",
242
+ },
211
243
  value: {
212
244
  name: "VARIATIONS",
213
245
  section: VARIATIONS_CONST_SECTION,
@@ -218,6 +250,18 @@ const INVENTORY_SECTIONS = [
218
250
  name: "WorkspaceBlockStyleConfig",
219
251
  section: BLOCK_STYLES_INTERFACE_SECTION,
220
252
  },
253
+ parse: {
254
+ entriesKey: "blockStyles",
255
+ entry: defineInventoryEntryParser({
256
+ entryName: "BLOCK_STYLES",
257
+ fields: [
258
+ { key: "block", required: true },
259
+ { key: "file", required: true },
260
+ { key: "slug", required: true },
261
+ ],
262
+ }),
263
+ hasSectionKey: "hasBlockStylesSection",
264
+ },
221
265
  value: {
222
266
  name: "BLOCK_STYLES",
223
267
  section: BLOCK_STYLES_CONST_SECTION,
@@ -228,6 +272,20 @@ const INVENTORY_SECTIONS = [
228
272
  name: "WorkspaceBlockTransformConfig",
229
273
  section: BLOCK_TRANSFORMS_INTERFACE_SECTION,
230
274
  },
275
+ parse: {
276
+ entriesKey: "blockTransforms",
277
+ entry: defineInventoryEntryParser({
278
+ entryName: "BLOCK_TRANSFORMS",
279
+ fields: [
280
+ { key: "block", required: true },
281
+ { key: "file", required: true },
282
+ { key: "from", required: true },
283
+ { key: "slug", required: true },
284
+ { key: "to", required: true },
285
+ ],
286
+ }),
287
+ hasSectionKey: "hasBlockTransformsSection",
288
+ },
231
289
  value: {
232
290
  name: "BLOCK_TRANSFORMS",
233
291
  section: BLOCK_TRANSFORMS_CONST_SECTION,
@@ -238,6 +296,17 @@ const INVENTORY_SECTIONS = [
238
296
  name: "WorkspacePatternConfig",
239
297
  section: PATTERNS_INTERFACE_SECTION,
240
298
  },
299
+ parse: {
300
+ entriesKey: "patterns",
301
+ entry: defineInventoryEntryParser({
302
+ entryName: "PATTERNS",
303
+ fields: [
304
+ { key: "file", required: true },
305
+ { key: "slug", required: true },
306
+ ],
307
+ }),
308
+ hasSectionKey: "hasPatternsSection",
309
+ },
241
310
  value: {
242
311
  name: "PATTERNS",
243
312
  section: PATTERNS_CONST_SECTION,
@@ -248,6 +317,20 @@ const INVENTORY_SECTIONS = [
248
317
  name: "WorkspaceBindingSourceConfig",
249
318
  section: BINDING_SOURCES_INTERFACE_SECTION,
250
319
  },
320
+ parse: {
321
+ entriesKey: "bindingSources",
322
+ entry: defineInventoryEntryParser({
323
+ entryName: "BINDING_SOURCES",
324
+ fields: [
325
+ { key: "attribute" },
326
+ { key: "block" },
327
+ { key: "editorFile", required: true },
328
+ { key: "serverFile", required: true },
329
+ { key: "slug", required: true },
330
+ ],
331
+ }),
332
+ hasSectionKey: "hasBindingSourcesSection",
333
+ },
251
334
  value: {
252
335
  name: "BINDING_SOURCES",
253
336
  section: BINDING_SOURCES_CONST_SECTION,
@@ -258,6 +341,36 @@ const INVENTORY_SECTIONS = [
258
341
  name: "WorkspaceRestResourceConfig",
259
342
  section: REST_RESOURCES_INTERFACE_SECTION,
260
343
  },
344
+ parse: {
345
+ entriesKey: "restResources",
346
+ entry: defineInventoryEntryParser({
347
+ entryName: "REST_RESOURCES",
348
+ fields: [
349
+ { key: "apiFile", required: true },
350
+ { key: "clientFile", required: true },
351
+ { key: "dataFile", required: true },
352
+ {
353
+ key: "methods",
354
+ kind: "stringArray",
355
+ required: true,
356
+ validate: (value, context) => {
357
+ const methods = Array.isArray(value) ? value : [];
358
+ const invalidMethods = methods.filter((method) => !REST_RESOURCE_METHOD_IDS.includes(method));
359
+ if (invalidMethods.length > 0) {
360
+ throw new Error(`${context.entryName}[${context.elementIndex}].${context.key} includes unsupported values: ${invalidMethods.join(", ")}.`);
361
+ }
362
+ },
363
+ },
364
+ { key: "namespace", required: true },
365
+ { key: "openApiFile", required: true },
366
+ { key: "phpFile", required: true },
367
+ { key: "slug", required: true },
368
+ { key: "typesFile", required: true },
369
+ { key: "validatorsFile", required: true },
370
+ ],
371
+ }),
372
+ hasSectionKey: "hasRestResourcesSection",
373
+ },
261
374
  value: {
262
375
  name: "REST_RESOURCES",
263
376
  section: REST_RESOURCES_CONST_SECTION,
@@ -268,6 +381,25 @@ const INVENTORY_SECTIONS = [
268
381
  name: "WorkspaceAbilityConfig",
269
382
  section: ABILITIES_INTERFACE_SECTION,
270
383
  },
384
+ parse: {
385
+ entriesKey: "abilities",
386
+ entry: defineInventoryEntryParser({
387
+ entryName: "ABILITIES",
388
+ fields: [
389
+ { key: "clientFile", required: true },
390
+ { key: "configFile", required: true },
391
+ { key: "dataFile", required: true },
392
+ { key: "inputSchemaFile", required: true },
393
+ { key: "inputTypeName", required: true },
394
+ { key: "outputSchemaFile", required: true },
395
+ { key: "outputTypeName", required: true },
396
+ { key: "phpFile", required: true },
397
+ { key: "slug", required: true },
398
+ { key: "typesFile", required: true },
399
+ ],
400
+ }),
401
+ hasSectionKey: "hasAbilitiesSection",
402
+ },
271
403
  value: {
272
404
  name: "ABILITIES",
273
405
  section: ABILITIES_CONST_SECTION,
@@ -278,6 +410,25 @@ const INVENTORY_SECTIONS = [
278
410
  name: "WorkspaceAiFeatureConfig",
279
411
  section: AI_FEATURES_INTERFACE_SECTION,
280
412
  },
413
+ parse: {
414
+ entriesKey: "aiFeatures",
415
+ entry: defineInventoryEntryParser({
416
+ entryName: "AI_FEATURES",
417
+ fields: [
418
+ { key: "aiSchemaFile", required: true },
419
+ { key: "apiFile", required: true },
420
+ { key: "clientFile", required: true },
421
+ { key: "dataFile", required: true },
422
+ { key: "namespace", required: true },
423
+ { key: "openApiFile", required: true },
424
+ { key: "phpFile", required: true },
425
+ { key: "slug", required: true },
426
+ { key: "typesFile", required: true },
427
+ { key: "validatorsFile", required: true },
428
+ ],
429
+ }),
430
+ hasSectionKey: "hasAiFeaturesSection",
431
+ },
281
432
  value: {
282
433
  name: "AI_FEATURES",
283
434
  section: AI_FEATURES_CONST_SECTION,
@@ -288,6 +439,19 @@ const INVENTORY_SECTIONS = [
288
439
  name: "WorkspaceAdminViewConfig",
289
440
  section: ADMIN_VIEWS_INTERFACE_SECTION,
290
441
  },
442
+ parse: {
443
+ entriesKey: "adminViews",
444
+ entry: defineInventoryEntryParser({
445
+ entryName: "ADMIN_VIEWS",
446
+ fields: [
447
+ { key: "file", required: true },
448
+ { key: "phpFile", required: true },
449
+ { key: "slug", required: true },
450
+ { key: "source" },
451
+ ],
452
+ }),
453
+ hasSectionKey: "hasAdminViewsSection",
454
+ },
291
455
  value: {
292
456
  name: "ADMIN_VIEWS",
293
457
  section: ADMIN_VIEWS_CONST_SECTION,
@@ -298,6 +462,18 @@ const INVENTORY_SECTIONS = [
298
462
  name: "WorkspaceEditorPluginConfig",
299
463
  section: EDITOR_PLUGINS_INTERFACE_SECTION,
300
464
  },
465
+ parse: {
466
+ entriesKey: "editorPlugins",
467
+ entry: defineInventoryEntryParser({
468
+ entryName: "EDITOR_PLUGINS",
469
+ fields: [
470
+ { key: "file", required: true },
471
+ { key: "slug", required: true },
472
+ { key: "slot", required: true },
473
+ ],
474
+ }),
475
+ hasSectionKey: "hasEditorPluginsSection",
476
+ },
301
477
  value: {
302
478
  name: "EDITOR_PLUGINS",
303
479
  section: EDITOR_PLUGINS_CONST_SECTION,
@@ -383,169 +559,60 @@ function getRequiredStringArrayProperty(entryName, elementIndex, objectLiteral,
383
559
  }
384
560
  throw new Error(`${entryName}[${elementIndex}] is missing required "${key}" in scripts/block-config.ts.`);
385
561
  }
386
- function parseBlockEntries(arrayLiteral) {
387
- return arrayLiteral.elements.map((element, elementIndex) => {
388
- if (!ts.isObjectLiteralExpression(element)) {
389
- throw new Error(`BLOCKS[${elementIndex}] must be an object literal in scripts/block-config.ts.`);
390
- }
391
- return {
392
- apiTypesFile: getOptionalStringProperty("BLOCKS", elementIndex, element, "apiTypesFile"),
393
- attributeTypeName: getOptionalStringProperty("BLOCKS", elementIndex, element, "attributeTypeName"),
394
- openApiFile: getOptionalStringProperty("BLOCKS", elementIndex, element, "openApiFile"),
395
- slug: getRequiredStringProperty("BLOCKS", elementIndex, element, "slug"),
396
- typesFile: getRequiredStringProperty("BLOCKS", elementIndex, element, "typesFile"),
397
- };
398
- });
399
- }
400
- function parseVariationEntries(arrayLiteral) {
401
- return arrayLiteral.elements.map((element, elementIndex) => {
402
- if (!ts.isObjectLiteralExpression(element)) {
403
- throw new Error(`VARIATIONS[${elementIndex}] must be an object literal in scripts/block-config.ts.`);
404
- }
405
- return {
406
- block: getRequiredStringProperty("VARIATIONS", elementIndex, element, "block"),
407
- file: getRequiredStringProperty("VARIATIONS", elementIndex, element, "file"),
408
- slug: getRequiredStringProperty("VARIATIONS", elementIndex, element, "slug"),
409
- };
410
- });
411
- }
412
- function parseBlockStyleEntries(arrayLiteral) {
413
- return arrayLiteral.elements.map((element, elementIndex) => {
414
- if (!ts.isObjectLiteralExpression(element)) {
415
- throw new Error(`BLOCK_STYLES[${elementIndex}] must be an object literal in scripts/block-config.ts.`);
416
- }
417
- return {
418
- block: getRequiredStringProperty("BLOCK_STYLES", elementIndex, element, "block"),
419
- file: getRequiredStringProperty("BLOCK_STYLES", elementIndex, element, "file"),
420
- slug: getRequiredStringProperty("BLOCK_STYLES", elementIndex, element, "slug"),
421
- };
422
- });
423
- }
424
- function parseBlockTransformEntries(arrayLiteral) {
425
- return arrayLiteral.elements.map((element, elementIndex) => {
426
- if (!ts.isObjectLiteralExpression(element)) {
427
- throw new Error(`BLOCK_TRANSFORMS[${elementIndex}] must be an object literal in scripts/block-config.ts.`);
428
- }
429
- return {
430
- block: getRequiredStringProperty("BLOCK_TRANSFORMS", elementIndex, element, "block"),
431
- file: getRequiredStringProperty("BLOCK_TRANSFORMS", elementIndex, element, "file"),
432
- from: getRequiredStringProperty("BLOCK_TRANSFORMS", elementIndex, element, "from"),
433
- slug: getRequiredStringProperty("BLOCK_TRANSFORMS", elementIndex, element, "slug"),
434
- to: getRequiredStringProperty("BLOCK_TRANSFORMS", elementIndex, element, "to"),
435
- };
436
- });
437
- }
438
- function parsePatternEntries(arrayLiteral) {
439
- return arrayLiteral.elements.map((element, elementIndex) => {
440
- if (!ts.isObjectLiteralExpression(element)) {
441
- throw new Error(`PATTERNS[${elementIndex}] must be an object literal in scripts/block-config.ts.`);
442
- }
443
- return {
444
- file: getRequiredStringProperty("PATTERNS", elementIndex, element, "file"),
445
- slug: getRequiredStringProperty("PATTERNS", elementIndex, element, "slug"),
446
- };
447
- });
448
- }
449
- function parseBindingSourceEntries(arrayLiteral) {
450
- return arrayLiteral.elements.map((element, elementIndex) => {
451
- if (!ts.isObjectLiteralExpression(element)) {
452
- throw new Error(`BINDING_SOURCES[${elementIndex}] must be an object literal in scripts/block-config.ts.`);
453
- }
454
- return {
455
- attribute: getOptionalStringProperty("BINDING_SOURCES", elementIndex, element, "attribute"),
456
- block: getOptionalStringProperty("BINDING_SOURCES", elementIndex, element, "block"),
457
- editorFile: getRequiredStringProperty("BINDING_SOURCES", elementIndex, element, "editorFile"),
458
- serverFile: getRequiredStringProperty("BINDING_SOURCES", elementIndex, element, "serverFile"),
459
- slug: getRequiredStringProperty("BINDING_SOURCES", elementIndex, element, "slug"),
460
- };
461
- });
462
- }
463
- function parseRestResourceEntries(arrayLiteral) {
562
+ function parseInventoryEntries(arrayLiteral, descriptor) {
464
563
  return arrayLiteral.elements.map((element, elementIndex) => {
465
564
  if (!ts.isObjectLiteralExpression(element)) {
466
- throw new Error(`REST_RESOURCES[${elementIndex}] must be an object literal in scripts/block-config.ts.`);
565
+ throw new Error(`${descriptor.entryName}[${elementIndex}] must be an object literal in scripts/block-config.ts.`);
467
566
  }
468
- const methods = getRequiredStringArrayProperty("REST_RESOURCES", elementIndex, element, "methods");
469
- const invalidMethods = methods.filter((method) => !REST_RESOURCE_METHOD_IDS.includes(method));
470
- if (invalidMethods.length > 0) {
471
- throw new Error(`REST_RESOURCES[${elementIndex}].methods includes unsupported values: ${invalidMethods.join(", ")}.`);
567
+ const entry = {};
568
+ for (const field of descriptor.fields) {
569
+ const kind = field.kind ?? "string";
570
+ const value = kind === "stringArray"
571
+ ? getRequiredStringArrayProperty(descriptor.entryName, elementIndex, element, field.key)
572
+ : field.required
573
+ ? getRequiredStringProperty(descriptor.entryName, elementIndex, element, field.key)
574
+ : getOptionalStringProperty(descriptor.entryName, elementIndex, element, field.key);
575
+ field.validate?.(value, {
576
+ elementIndex,
577
+ entryName: descriptor.entryName,
578
+ key: field.key,
579
+ });
580
+ entry[field.key] = value;
472
581
  }
473
- return {
474
- apiFile: getRequiredStringProperty("REST_RESOURCES", elementIndex, element, "apiFile"),
475
- clientFile: getRequiredStringProperty("REST_RESOURCES", elementIndex, element, "clientFile"),
476
- dataFile: getRequiredStringProperty("REST_RESOURCES", elementIndex, element, "dataFile"),
477
- methods,
478
- namespace: getRequiredStringProperty("REST_RESOURCES", elementIndex, element, "namespace"),
479
- openApiFile: getRequiredStringProperty("REST_RESOURCES", elementIndex, element, "openApiFile"),
480
- phpFile: getRequiredStringProperty("REST_RESOURCES", elementIndex, element, "phpFile"),
481
- slug: getRequiredStringProperty("REST_RESOURCES", elementIndex, element, "slug"),
482
- typesFile: getRequiredStringProperty("REST_RESOURCES", elementIndex, element, "typesFile"),
483
- validatorsFile: getRequiredStringProperty("REST_RESOURCES", elementIndex, element, "validatorsFile"),
484
- };
582
+ return entry;
485
583
  });
486
584
  }
487
- function parseAiFeatureEntries(arrayLiteral) {
488
- return arrayLiteral.elements.map((element, elementIndex) => {
489
- if (!ts.isObjectLiteralExpression(element)) {
490
- throw new Error(`AI_FEATURES[${elementIndex}] must be an object literal in scripts/block-config.ts.`);
491
- }
585
+ function parseInventorySection(sourceFile, descriptor) {
586
+ if (!descriptor.parse) {
492
587
  return {
493
- aiSchemaFile: getRequiredStringProperty("AI_FEATURES", elementIndex, element, "aiSchemaFile"),
494
- apiFile: getRequiredStringProperty("AI_FEATURES", elementIndex, element, "apiFile"),
495
- clientFile: getRequiredStringProperty("AI_FEATURES", elementIndex, element, "clientFile"),
496
- dataFile: getRequiredStringProperty("AI_FEATURES", elementIndex, element, "dataFile"),
497
- namespace: getRequiredStringProperty("AI_FEATURES", elementIndex, element, "namespace"),
498
- openApiFile: getRequiredStringProperty("AI_FEATURES", elementIndex, element, "openApiFile"),
499
- phpFile: getRequiredStringProperty("AI_FEATURES", elementIndex, element, "phpFile"),
500
- slug: getRequiredStringProperty("AI_FEATURES", elementIndex, element, "slug"),
501
- typesFile: getRequiredStringProperty("AI_FEATURES", elementIndex, element, "typesFile"),
502
- validatorsFile: getRequiredStringProperty("AI_FEATURES", elementIndex, element, "validatorsFile"),
588
+ entries: [],
589
+ found: false,
503
590
  };
504
- });
505
- }
506
- function parseAbilityEntries(arrayLiteral) {
507
- return arrayLiteral.elements.map((element, elementIndex) => {
508
- if (!ts.isObjectLiteralExpression(element)) {
509
- throw new Error(`ABILITIES[${elementIndex}] must be an object literal in scripts/block-config.ts.`);
510
- }
511
- return {
512
- clientFile: getRequiredStringProperty("ABILITIES", elementIndex, element, "clientFile"),
513
- configFile: getRequiredStringProperty("ABILITIES", elementIndex, element, "configFile"),
514
- dataFile: getRequiredStringProperty("ABILITIES", elementIndex, element, "dataFile"),
515
- inputSchemaFile: getRequiredStringProperty("ABILITIES", elementIndex, element, "inputSchemaFile"),
516
- inputTypeName: getRequiredStringProperty("ABILITIES", elementIndex, element, "inputTypeName"),
517
- outputSchemaFile: getRequiredStringProperty("ABILITIES", elementIndex, element, "outputSchemaFile"),
518
- outputTypeName: getRequiredStringProperty("ABILITIES", elementIndex, element, "outputTypeName"),
519
- phpFile: getRequiredStringProperty("ABILITIES", elementIndex, element, "phpFile"),
520
- slug: getRequiredStringProperty("ABILITIES", elementIndex, element, "slug"),
521
- typesFile: getRequiredStringProperty("ABILITIES", elementIndex, element, "typesFile"),
522
- };
523
- });
524
- }
525
- function parseEditorPluginEntries(arrayLiteral) {
526
- return arrayLiteral.elements.map((element, elementIndex) => {
527
- if (!ts.isObjectLiteralExpression(element)) {
528
- throw new Error(`EDITOR_PLUGINS[${elementIndex}] must be an object literal in scripts/block-config.ts.`);
591
+ }
592
+ const exportName = descriptor.parse.exportName ?? descriptor.value?.name;
593
+ if (!exportName) {
594
+ throw new Error("Inventory parser descriptor is missing an export name.");
595
+ }
596
+ const exportedArray = findExportedArrayLiteral(sourceFile, exportName);
597
+ if (!exportedArray.found) {
598
+ if (descriptor.parse.required) {
599
+ throw new Error(`scripts/block-config.ts must export a ${exportName} array.`);
529
600
  }
530
601
  return {
531
- file: getRequiredStringProperty("EDITOR_PLUGINS", elementIndex, element, "file"),
532
- slug: getRequiredStringProperty("EDITOR_PLUGINS", elementIndex, element, "slug"),
533
- slot: getRequiredStringProperty("EDITOR_PLUGINS", elementIndex, element, "slot"),
602
+ entries: [],
603
+ found: false,
534
604
  };
535
- });
536
- }
537
- function parseAdminViewEntries(arrayLiteral) {
538
- return arrayLiteral.elements.map((element, elementIndex) => {
539
- if (!ts.isObjectLiteralExpression(element)) {
540
- throw new Error(`ADMIN_VIEWS[${elementIndex}] must be an object literal in scripts/block-config.ts.`);
605
+ }
606
+ if (!exportedArray.array) {
607
+ if (descriptor.parse.required) {
608
+ throw new Error(`scripts/block-config.ts must export a ${exportName} array.`);
541
609
  }
542
- return {
543
- file: getRequiredStringProperty("ADMIN_VIEWS", elementIndex, element, "file"),
544
- phpFile: getRequiredStringProperty("ADMIN_VIEWS", elementIndex, element, "phpFile"),
545
- slug: getRequiredStringProperty("ADMIN_VIEWS", elementIndex, element, "slug"),
546
- source: getOptionalStringProperty("ADMIN_VIEWS", elementIndex, element, "source"),
547
- };
548
- });
610
+ throw new Error(`scripts/block-config.ts must export ${exportName} as an array literal.`);
611
+ }
612
+ return {
613
+ entries: parseInventoryEntries(exportedArray.array, descriptor.parse.entry),
614
+ found: true,
615
+ };
549
616
  }
550
617
  /**
551
618
  * Parse workspace inventory entries from the source of `scripts/block-config.ts`.
@@ -556,82 +623,42 @@ function parseAdminViewEntries(arrayLiteral) {
556
623
  */
557
624
  export function parseWorkspaceInventorySource(source) {
558
625
  const sourceFile = ts.createSourceFile("block-config.ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
559
- const blockArray = findExportedArrayLiteral(sourceFile, "BLOCKS");
560
- if (!blockArray.found || !blockArray.array) {
561
- throw new Error("scripts/block-config.ts must export a BLOCKS array.");
562
- }
563
- const variationArray = findExportedArrayLiteral(sourceFile, "VARIATIONS");
564
- const blockStyleArray = findExportedArrayLiteral(sourceFile, "BLOCK_STYLES");
565
- const blockTransformArray = findExportedArrayLiteral(sourceFile, "BLOCK_TRANSFORMS");
566
- const patternArray = findExportedArrayLiteral(sourceFile, "PATTERNS");
567
- const bindingSourceArray = findExportedArrayLiteral(sourceFile, "BINDING_SOURCES");
568
- const restResourceArray = findExportedArrayLiteral(sourceFile, "REST_RESOURCES");
569
- const abilityArray = findExportedArrayLiteral(sourceFile, "ABILITIES");
570
- const aiFeatureArray = findExportedArrayLiteral(sourceFile, "AI_FEATURES");
571
- const adminViewArray = findExportedArrayLiteral(sourceFile, "ADMIN_VIEWS");
572
- const editorPluginArray = findExportedArrayLiteral(sourceFile, "EDITOR_PLUGINS");
573
- if (variationArray.found && !variationArray.array) {
574
- throw new Error("scripts/block-config.ts must export VARIATIONS as an array literal.");
575
- }
576
- if (blockStyleArray.found && !blockStyleArray.array) {
577
- throw new Error("scripts/block-config.ts must export BLOCK_STYLES as an array literal.");
578
- }
579
- if (blockTransformArray.found && !blockTransformArray.array) {
580
- throw new Error("scripts/block-config.ts must export BLOCK_TRANSFORMS as an array literal.");
581
- }
582
- if (patternArray.found && !patternArray.array) {
583
- throw new Error("scripts/block-config.ts must export PATTERNS as an array literal.");
584
- }
585
- if (bindingSourceArray.found && !bindingSourceArray.array) {
586
- throw new Error("scripts/block-config.ts must export BINDING_SOURCES as an array literal.");
587
- }
588
- if (restResourceArray.found && !restResourceArray.array) {
589
- throw new Error("scripts/block-config.ts must export REST_RESOURCES as an array literal.");
590
- }
591
- if (abilityArray.found && !abilityArray.array) {
592
- throw new Error("scripts/block-config.ts must export ABILITIES as an array literal.");
593
- }
594
- if (aiFeatureArray.found && !aiFeatureArray.array) {
595
- throw new Error("scripts/block-config.ts must export AI_FEATURES as an array literal.");
596
- }
597
- if (adminViewArray.found && !adminViewArray.array) {
598
- throw new Error("scripts/block-config.ts must export ADMIN_VIEWS as an array literal.");
599
- }
600
- if (editorPluginArray.found && !editorPluginArray.array) {
601
- throw new Error("scripts/block-config.ts must export EDITOR_PLUGINS as an array literal.");
602
- }
603
- return {
604
- abilities: abilityArray.array ? parseAbilityEntries(abilityArray.array) : [],
605
- adminViews: adminViewArray.array ? parseAdminViewEntries(adminViewArray.array) : [],
606
- aiFeatures: aiFeatureArray.array ? parseAiFeatureEntries(aiFeatureArray.array) : [],
607
- bindingSources: bindingSourceArray.array
608
- ? parseBindingSourceEntries(bindingSourceArray.array)
609
- : [],
610
- blockStyles: blockStyleArray.array ? parseBlockStyleEntries(blockStyleArray.array) : [],
611
- blockTransforms: blockTransformArray.array
612
- ? parseBlockTransformEntries(blockTransformArray.array)
613
- : [],
614
- blocks: parseBlockEntries(blockArray.array),
615
- hasAbilitiesSection: abilityArray.found,
616
- hasAdminViewsSection: adminViewArray.found,
617
- hasAiFeaturesSection: aiFeatureArray.found,
618
- hasBindingSourcesSection: bindingSourceArray.found,
619
- hasBlockStylesSection: blockStyleArray.found,
620
- hasBlockTransformsSection: blockTransformArray.found,
621
- hasEditorPluginsSection: editorPluginArray.found,
622
- hasPatternsSection: patternArray.found,
623
- hasRestResourcesSection: restResourceArray.found,
624
- hasVariationsSection: variationArray.found,
625
- editorPlugins: editorPluginArray.array
626
- ? parseEditorPluginEntries(editorPluginArray.array)
627
- : [],
628
- patterns: patternArray.array ? parsePatternEntries(patternArray.array) : [],
629
- restResources: restResourceArray.array
630
- ? parseRestResourceEntries(restResourceArray.array)
631
- : [],
626
+ const parsedInventory = {
627
+ abilities: [],
628
+ adminViews: [],
629
+ aiFeatures: [],
630
+ bindingSources: [],
631
+ blockStyles: [],
632
+ blockTransforms: [],
633
+ blocks: parseInventorySection(sourceFile, BLOCK_INVENTORY_SECTION).entries,
634
+ editorPlugins: [],
635
+ hasAbilitiesSection: false,
636
+ hasAdminViewsSection: false,
637
+ hasAiFeaturesSection: false,
638
+ hasBindingSourcesSection: false,
639
+ hasBlockStylesSection: false,
640
+ hasBlockTransformsSection: false,
641
+ hasEditorPluginsSection: false,
642
+ hasPatternsSection: false,
643
+ hasRestResourcesSection: false,
644
+ hasVariationsSection: false,
645
+ patterns: [],
646
+ restResources: [],
632
647
  source,
633
- variations: variationArray.array ? parseVariationEntries(variationArray.array) : [],
648
+ variations: [],
634
649
  };
650
+ const mutableInventory = parsedInventory;
651
+ for (const section of INVENTORY_SECTIONS) {
652
+ if (!section.parse) {
653
+ continue;
654
+ }
655
+ const parsedSection = parseInventorySection(sourceFile, section);
656
+ mutableInventory[section.parse.entriesKey] = parsedSection.entries;
657
+ if (section.parse.hasSectionKey) {
658
+ mutableInventory[section.parse.hasSectionKey] = parsedSection.found;
659
+ }
660
+ }
661
+ return parsedInventory;
635
662
  }
636
663
  /**
637
664
  * Read and parse the canonical workspace inventory file.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wp-typia/project-tools",
3
- "version": "0.22.4",
3
+ "version": "0.22.5",
4
4
  "description": "Project orchestration and programmatic tooling for wp-typia",
5
5
  "packageManager": "bun@1.3.11",
6
6
  "type": "module",