@wp-typia/project-tools 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/dist/runtime/ai-feature-artifacts.js +4 -1
  2. package/dist/runtime/block-generator-service-spec.js +2 -1
  3. package/dist/runtime/built-in-block-non-ts-basic-artifacts.d.ts +9 -0
  4. package/dist/runtime/built-in-block-non-ts-basic-artifacts.js +84 -0
  5. package/dist/runtime/built-in-block-non-ts-compound-artifacts.d.ts +9 -0
  6. package/dist/runtime/built-in-block-non-ts-compound-artifacts.js +36 -0
  7. package/dist/runtime/built-in-block-non-ts-compound-templates.d.ts +23 -0
  8. package/dist/runtime/built-in-block-non-ts-compound-templates.js +453 -0
  9. package/dist/runtime/built-in-block-non-ts-family-artifacts.d.ts +8 -26
  10. package/dist/runtime/built-in-block-non-ts-family-artifacts.js +8 -1034
  11. package/dist/runtime/built-in-block-non-ts-interactivity-artifacts.d.ts +9 -0
  12. package/dist/runtime/built-in-block-non-ts-interactivity-artifacts.js +83 -0
  13. package/dist/runtime/built-in-block-non-ts-persistence-artifacts.d.ts +9 -0
  14. package/dist/runtime/built-in-block-non-ts-persistence-artifacts.js +33 -0
  15. package/dist/runtime/built-in-block-non-ts-persistence-templates.d.ts +23 -0
  16. package/dist/runtime/built-in-block-non-ts-persistence-templates.js +395 -0
  17. package/dist/runtime/cli-add-block-json.js +5 -1
  18. package/dist/runtime/cli-add-collision.js +8 -0
  19. package/dist/runtime/cli-add-help.js +14 -10
  20. package/dist/runtime/cli-add-kind-ids.d.ts +1 -1
  21. package/dist/runtime/cli-add-kind-ids.js +1 -0
  22. package/dist/runtime/cli-add-types.d.ts +45 -6
  23. package/dist/runtime/cli-add-types.js +2 -0
  24. package/dist/runtime/cli-add-validation.d.ts +7 -0
  25. package/dist/runtime/cli-add-validation.js +9 -0
  26. package/dist/runtime/cli-add-workspace-ability-anchors.d.ts +24 -0
  27. package/dist/runtime/cli-add-workspace-ability-anchors.js +294 -0
  28. package/dist/runtime/cli-add-workspace-ability-registry.d.ts +10 -0
  29. package/dist/runtime/cli-add-workspace-ability-registry.js +51 -0
  30. package/dist/runtime/cli-add-workspace-ability-scaffold.d.ts +1 -1
  31. package/dist/runtime/cli-add-workspace-ability-scaffold.js +5 -308
  32. package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +6 -2
  33. package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.d.ts +34 -0
  34. package/dist/runtime/cli-add-workspace-admin-view-templates-core-data.js +483 -0
  35. package/dist/runtime/cli-add-workspace-admin-view-templates-default.d.ts +30 -0
  36. package/dist/runtime/cli-add-workspace-admin-view-templates-default.js +310 -0
  37. package/dist/runtime/cli-add-workspace-admin-view-templates-rest.d.ts +25 -0
  38. package/dist/runtime/cli-add-workspace-admin-view-templates-rest.js +124 -0
  39. package/dist/runtime/cli-add-workspace-admin-view-templates-settings.d.ts +34 -0
  40. package/dist/runtime/cli-add-workspace-admin-view-templates-settings.js +370 -0
  41. package/dist/runtime/cli-add-workspace-admin-view-templates-shared.d.ts +49 -0
  42. package/dist/runtime/cli-add-workspace-admin-view-templates-shared.js +259 -0
  43. package/dist/runtime/cli-add-workspace-admin-view-templates.d.ts +18 -27
  44. package/dist/runtime/cli-add-workspace-admin-view-templates.js +30 -1326
  45. package/dist/runtime/cli-add-workspace-ai-anchors.d.ts +4 -4
  46. package/dist/runtime/cli-add-workspace-ai-anchors.js +8 -233
  47. package/dist/runtime/cli-add-workspace-ai-scaffold.js +4 -2
  48. package/dist/runtime/cli-add-workspace-ai-source-emitters.d.ts +1 -4
  49. package/dist/runtime/cli-add-workspace-ai-source-emitters.js +1 -129
  50. package/dist/runtime/cli-add-workspace-ai-sync-rest-anchors.d.ts +5 -0
  51. package/dist/runtime/cli-add-workspace-ai-sync-rest-anchors.js +236 -0
  52. package/dist/runtime/cli-add-workspace-ai-sync-script-source.d.ts +4 -0
  53. package/dist/runtime/cli-add-workspace-ai-sync-script-source.js +145 -0
  54. package/dist/runtime/cli-add-workspace-assets.d.ts +6 -63
  55. package/dist/runtime/cli-add-workspace-assets.js +6 -950
  56. package/dist/runtime/cli-add-workspace-binding-source-anchors.d.ts +23 -0
  57. package/dist/runtime/cli-add-workspace-binding-source-anchors.js +112 -0
  58. package/dist/runtime/cli-add-workspace-binding-source-source-emitters.d.ts +33 -0
  59. package/dist/runtime/cli-add-workspace-binding-source-source-emitters.js +436 -0
  60. package/dist/runtime/cli-add-workspace-binding-source-types.d.ts +20 -0
  61. package/dist/runtime/cli-add-workspace-binding-source-types.js +1 -0
  62. package/dist/runtime/cli-add-workspace-binding-source.d.ts +40 -0
  63. package/dist/runtime/cli-add-workspace-binding-source.js +275 -0
  64. package/dist/runtime/cli-add-workspace-block-style.d.ts +22 -0
  65. package/dist/runtime/cli-add-workspace-block-style.js +148 -0
  66. package/dist/runtime/cli-add-workspace-block-transform.d.ts +32 -0
  67. package/dist/runtime/cli-add-workspace-block-transform.js +197 -0
  68. package/dist/runtime/cli-add-workspace-contract.js +1 -1
  69. package/dist/runtime/cli-add-workspace-core-variation.d.ts +20 -0
  70. package/dist/runtime/cli-add-workspace-core-variation.js +322 -0
  71. package/dist/runtime/cli-add-workspace-editor-plugin-anchors.d.ts +37 -0
  72. package/dist/runtime/cli-add-workspace-editor-plugin-anchors.js +206 -0
  73. package/dist/runtime/cli-add-workspace-editor-plugin-source-emitters.d.ts +47 -0
  74. package/dist/runtime/cli-add-workspace-editor-plugin-source-emitters.js +219 -0
  75. package/dist/runtime/cli-add-workspace-editor-plugin.d.ts +22 -0
  76. package/dist/runtime/cli-add-workspace-editor-plugin.js +78 -0
  77. package/dist/runtime/cli-add-workspace-hooked-block.d.ts +23 -0
  78. package/dist/runtime/cli-add-workspace-hooked-block.js +57 -0
  79. package/dist/runtime/cli-add-workspace-integration-env-files.d.ts +33 -0
  80. package/dist/runtime/cli-add-workspace-integration-env-files.js +65 -0
  81. package/dist/runtime/cli-add-workspace-integration-env-package-json.d.ts +38 -0
  82. package/dist/runtime/cli-add-workspace-integration-env-package-json.js +122 -0
  83. package/dist/runtime/cli-add-workspace-integration-env-source-emitters.d.ts +44 -0
  84. package/dist/runtime/cli-add-workspace-integration-env-source-emitters.js +262 -0
  85. package/dist/runtime/cli-add-workspace-integration-env.d.ts +3 -1
  86. package/dist/runtime/cli-add-workspace-integration-env.js +10 -313
  87. package/dist/runtime/cli-add-workspace-pattern-anchors.d.ts +10 -0
  88. package/dist/runtime/cli-add-workspace-pattern-anchors.js +95 -0
  89. package/dist/runtime/cli-add-workspace-pattern-options.d.ts +20 -0
  90. package/dist/runtime/cli-add-workspace-pattern-options.js +113 -0
  91. package/dist/runtime/cli-add-workspace-pattern-source-emitters.d.ts +20 -0
  92. package/dist/runtime/cli-add-workspace-pattern-source-emitters.js +57 -0
  93. package/dist/runtime/cli-add-workspace-pattern.d.ts +42 -0
  94. package/dist/runtime/cli-add-workspace-pattern.js +99 -0
  95. package/dist/runtime/cli-add-workspace-post-meta.js +1 -1
  96. package/dist/runtime/cli-add-workspace-registration-hooks.d.ts +50 -0
  97. package/dist/runtime/cli-add-workspace-registration-hooks.js +162 -0
  98. package/dist/runtime/cli-add-workspace-rest-anchors.d.ts +9 -4
  99. package/dist/runtime/cli-add-workspace-rest-anchors.js +9 -428
  100. package/dist/runtime/cli-add-workspace-rest-bootstrap-anchors.d.ts +17 -0
  101. package/dist/runtime/cli-add-workspace-rest-bootstrap-anchors.js +108 -0
  102. package/dist/runtime/cli-add-workspace-rest-contract-sync-anchors.d.ts +9 -0
  103. package/dist/runtime/cli-add-workspace-rest-contract-sync-anchors.js +142 -0
  104. package/dist/runtime/cli-add-workspace-rest-generated-source-emitters.d.ts +51 -0
  105. package/dist/runtime/cli-add-workspace-rest-generated-source-emitters.js +415 -0
  106. package/dist/runtime/cli-add-workspace-rest-generated.d.ts +9 -0
  107. package/dist/runtime/cli-add-workspace-rest-generated.js +160 -0
  108. package/dist/runtime/cli-add-workspace-rest-manual-source-emitters.d.ts +80 -0
  109. package/dist/runtime/cli-add-workspace-rest-manual-source-emitters.js +238 -0
  110. package/dist/runtime/cli-add-workspace-rest-manual.d.ts +8 -0
  111. package/dist/runtime/cli-add-workspace-rest-manual.js +266 -0
  112. package/dist/runtime/cli-add-workspace-rest-php-templates.d.ts +18 -0
  113. package/dist/runtime/cli-add-workspace-rest-php-templates.js +359 -0
  114. package/dist/runtime/cli-add-workspace-rest-resource-php-routing-template.d.ts +33 -0
  115. package/dist/runtime/cli-add-workspace-rest-resource-php-routing-template.js +145 -0
  116. package/dist/runtime/cli-add-workspace-rest-resource-sync-anchors.d.ts +9 -0
  117. package/dist/runtime/cli-add-workspace-rest-resource-sync-anchors.js +162 -0
  118. package/dist/runtime/cli-add-workspace-rest-schema-helper-php-template.d.ts +7 -0
  119. package/dist/runtime/cli-add-workspace-rest-schema-helper-php-template.js +193 -0
  120. package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +5 -91
  121. package/dist/runtime/cli-add-workspace-rest-source-emitters.js +5 -642
  122. package/dist/runtime/cli-add-workspace-rest-source-utils.d.ts +17 -0
  123. package/dist/runtime/cli-add-workspace-rest-source-utils.js +50 -0
  124. package/dist/runtime/cli-add-workspace-rest-sync-script-shared.d.ts +56 -0
  125. package/dist/runtime/cli-add-workspace-rest-sync-script-shared.js +122 -0
  126. package/dist/runtime/cli-add-workspace-rest-types.d.ts +108 -0
  127. package/dist/runtime/cli-add-workspace-rest-types.js +1 -0
  128. package/dist/runtime/cli-add-workspace-rest.d.ts +3 -20
  129. package/dist/runtime/cli-add-workspace-rest.js +33 -788
  130. package/dist/runtime/cli-add-workspace-variation.d.ts +22 -0
  131. package/dist/runtime/cli-add-workspace-variation.js +162 -0
  132. package/dist/runtime/cli-add-workspace.d.ts +42 -107
  133. package/dist/runtime/cli-add-workspace.js +42 -674
  134. package/dist/runtime/cli-add.d.ts +3 -3
  135. package/dist/runtime/cli-add.js +2 -2
  136. package/dist/runtime/cli-core.d.ts +3 -2
  137. package/dist/runtime/cli-core.js +2 -2
  138. package/dist/runtime/cli-diagnostics.d.ts +3 -1
  139. package/dist/runtime/cli-diagnostics.js +17 -5
  140. package/dist/runtime/cli-doctor-workspace-bindings.js +63 -1
  141. package/dist/runtime/cli-doctor-workspace-block-addons.d.ts +12 -0
  142. package/dist/runtime/cli-doctor-workspace-block-addons.js +162 -0
  143. package/dist/runtime/cli-doctor-workspace-block-iframe.d.ts +9 -0
  144. package/dist/runtime/cli-doctor-workspace-block-iframe.js +228 -0
  145. package/dist/runtime/cli-doctor-workspace-block-metadata.d.ts +11 -0
  146. package/dist/runtime/cli-doctor-workspace-block-metadata.js +111 -0
  147. package/dist/runtime/cli-doctor-workspace-blocks.js +6 -424
  148. package/dist/runtime/cli-doctor-workspace-features-abilities.d.ts +11 -0
  149. package/dist/runtime/cli-doctor-workspace-features-abilities.js +112 -0
  150. package/dist/runtime/cli-doctor-workspace-features-admin-views.d.ts +11 -0
  151. package/dist/runtime/cli-doctor-workspace-features-admin-views.js +128 -0
  152. package/dist/runtime/cli-doctor-workspace-features-ai.d.ts +11 -0
  153. package/dist/runtime/cli-doctor-workspace-features-ai.js +57 -0
  154. package/dist/runtime/cli-doctor-workspace-features-editor-plugins.d.ts +11 -0
  155. package/dist/runtime/cli-doctor-workspace-features-editor-plugins.js +80 -0
  156. package/dist/runtime/cli-doctor-workspace-features-post-meta.d.ts +11 -0
  157. package/dist/runtime/cli-doctor-workspace-features-post-meta.js +77 -0
  158. package/dist/runtime/cli-doctor-workspace-features-rest.d.ts +11 -0
  159. package/dist/runtime/cli-doctor-workspace-features-rest.js +120 -0
  160. package/dist/runtime/cli-doctor-workspace-features.js +14 -487
  161. package/dist/runtime/cli-doctor.d.ts +54 -3
  162. package/dist/runtime/cli-doctor.js +92 -10
  163. package/dist/runtime/cli-help.js +12 -7
  164. package/dist/runtime/cli-init-package-json.js +4 -2
  165. package/dist/runtime/cli-prompt.d.ts +16 -2
  166. package/dist/runtime/cli-prompt.js +29 -12
  167. package/dist/runtime/cli-scaffold.d.ts +2 -1
  168. package/dist/runtime/cli-scaffold.js +19 -10
  169. package/dist/runtime/external-template-guards.js +4 -6
  170. package/dist/runtime/index.d.ts +6 -3
  171. package/dist/runtime/index.js +4 -2
  172. package/dist/runtime/json-utils.d.ts +62 -4
  173. package/dist/runtime/json-utils.js +78 -4
  174. package/dist/runtime/local-dev-presets.js +6 -2
  175. package/dist/runtime/migration-ui-capability.js +4 -1
  176. package/dist/runtime/migration-utils.js +4 -1
  177. package/dist/runtime/package-managers.js +6 -1
  178. package/dist/runtime/package-versions.d.ts +1 -0
  179. package/dist/runtime/package-versions.js +16 -3
  180. package/dist/runtime/pattern-catalog.d.ts +122 -0
  181. package/dist/runtime/pattern-catalog.js +471 -0
  182. package/dist/runtime/post-meta-binding-fields.d.ts +46 -0
  183. package/dist/runtime/post-meta-binding-fields.js +135 -0
  184. package/dist/runtime/scaffold-bootstrap.js +7 -2
  185. package/dist/runtime/scaffold-package-manager-files.js +5 -1
  186. package/dist/runtime/scaffold-repository-reference.js +4 -2
  187. package/dist/runtime/scaffold-template-variables.js +2 -1
  188. package/dist/runtime/scaffold.d.ts +18 -1
  189. package/dist/runtime/scaffold.js +55 -2
  190. package/dist/runtime/temp-roots.js +4 -1
  191. package/dist/runtime/template-layers.js +4 -1
  192. package/dist/runtime/template-registry.js +9 -3
  193. package/dist/runtime/template-source-contracts.d.ts +2 -0
  194. package/dist/runtime/template-source-normalization.js +2 -1
  195. package/dist/runtime/template-source-remote.js +18 -5
  196. package/dist/runtime/template-source-seeds.js +10 -3
  197. package/dist/runtime/typia-llm-json-schema.d.ts +24 -0
  198. package/dist/runtime/typia-llm-json-schema.js +33 -0
  199. package/dist/runtime/typia-llm-openapi-constraints.d.ts +20 -0
  200. package/dist/runtime/typia-llm-openapi-constraints.js +254 -0
  201. package/dist/runtime/typia-llm-projection.d.ts +25 -0
  202. package/dist/runtime/typia-llm-projection.js +58 -0
  203. package/dist/runtime/typia-llm-render.d.ts +21 -0
  204. package/dist/runtime/typia-llm-render.js +252 -0
  205. package/dist/runtime/typia-llm-sync.d.ts +10 -0
  206. package/dist/runtime/typia-llm-sync.js +63 -0
  207. package/dist/runtime/typia-llm-types.d.ts +197 -0
  208. package/dist/runtime/typia-llm-types.js +1 -0
  209. package/dist/runtime/typia-llm.d.ts +9 -255
  210. package/dist/runtime/typia-llm.js +5 -634
  211. package/dist/runtime/workspace-inventory-mutations.js +15 -1
  212. package/dist/runtime/workspace-inventory-parser-entries.d.ts +17 -0
  213. package/dist/runtime/workspace-inventory-parser-entries.js +157 -0
  214. package/dist/runtime/workspace-inventory-parser-validation.d.ts +104 -0
  215. package/dist/runtime/workspace-inventory-parser-validation.js +34 -0
  216. package/dist/runtime/workspace-inventory-parser.d.ts +3 -45
  217. package/dist/runtime/workspace-inventory-parser.js +3 -581
  218. package/dist/runtime/workspace-inventory-section-descriptors.d.ts +19 -0
  219. package/dist/runtime/workspace-inventory-section-descriptors.js +443 -0
  220. package/dist/runtime/workspace-inventory-templates.d.ts +3 -3
  221. package/dist/runtime/workspace-inventory-templates.js +10 -1
  222. package/dist/runtime/workspace-inventory-types.d.ts +10 -1
  223. package/dist/runtime/workspace-project.js +4 -6
  224. package/package.json +8 -3
  225. package/templates/_shared/compound/core/scripts/block-config.ts.mustache +22 -0
  226. package/templates/_shared/compound/core/scripts/sync-types-to-block-json.ts.mustache +103 -2
  227. package/templates/_shared/compound/core/src/inner-blocks-templates.ts.mustache +13 -0
  228. package/templates/_shared/compound/persistence/scripts/block-config.ts.mustache +22 -1
@@ -1,1347 +1,51 @@
1
- import path from 'node:path';
2
- import { quotePhpString } from './php-utils.js';
3
- import { quoteTsString } from './cli-add-shared.js';
4
- import { ADMIN_VIEWS_ASSET, ADMIN_VIEWS_SCRIPT, ADMIN_VIEWS_STYLE, ADMIN_VIEWS_STYLE_RTL, formatAdminViewSourceLocator, isAdminViewCoreDataSource, } from './cli-add-workspace-admin-view-types.js';
5
- import { toCamelCase, toPascalCase, toTitleCase } from './string-case.js';
6
- function getAdminViewRelativeModuleSpecifier(adminViewSlug, workspaceFile) {
7
- const adminViewDir = `src/admin-views/${adminViewSlug}`;
8
- const normalizedFile = workspaceFile.replace(/\\/gu, '/');
9
- const modulePath = normalizedFile.replace(/\.[cm]?[jt]sx?$/u, '');
10
- const relativeModulePath = path.posix.relative(adminViewDir, modulePath);
11
- return relativeModulePath.startsWith('.')
12
- ? relativeModulePath
13
- : `./${relativeModulePath}`;
14
- }
15
- export function buildAdminViewConfigEntry(adminViewSlug, source) {
16
- return [
17
- '\t{',
18
- `\t\tfile: ${quoteTsString(`src/admin-views/${adminViewSlug}/index.tsx`)},`,
19
- `\t\tphpFile: ${quoteTsString(`inc/admin-views/${adminViewSlug}.php`)},`,
20
- `\t\tslug: ${quoteTsString(adminViewSlug)},`,
21
- source
22
- ? `\t\tsource: ${quoteTsString(formatAdminViewSourceLocator(source))},`
23
- : null,
24
- '\t},',
25
- ]
26
- .filter((line) => typeof line === 'string')
27
- .join('\n');
28
- }
29
- export function buildAdminViewRegistrySource(adminViewSlugs) {
30
- const importLines = adminViewSlugs
31
- .map((adminViewSlug) => `import './${adminViewSlug}';`)
32
- .join('\n');
33
- return `${importLines}${importLines ? '\n\n' : ''}// wp-typia add admin-view entries\n`;
34
- }
1
+ import { buildCoreDataAdminViewConfigSource, buildCoreDataAdminViewTypesSource, buildCoreDataAdminViewDataSource, buildCoreDataAdminViewScreenSource, } from './cli-add-workspace-admin-view-templates-core-data.js';
2
+ import { buildAdminViewScreenSource as buildDefaultAdminViewScreenSource, buildDefaultAdminViewConfigSource, buildDefaultAdminViewDataSource, buildDefaultAdminViewTypesSource, } from './cli-add-workspace-admin-view-templates-default.js';
3
+ import { buildRestAdminViewConfigSource, buildRestAdminViewDataSource, buildRestAdminViewTypesSource, } from './cli-add-workspace-admin-view-templates-rest.js';
4
+ import { buildAdminViewConfigEntry, buildAdminViewEntrySource, buildAdminViewPhpSource as buildSharedAdminViewPhpSource, buildAdminViewRegistrySource, buildAdminViewStyleSource, } from './cli-add-workspace-admin-view-templates-shared.js';
5
+ import { buildRestSettingsAdminViewConfigSource, buildRestSettingsAdminViewDataSource, buildRestSettingsAdminViewScreenSource, buildRestSettingsAdminViewTypesSource, } from './cli-add-workspace-admin-view-templates-settings.js';
6
+ import { isAdminViewCoreDataSource, } from './cli-add-workspace-admin-view-types.js';
7
+ export { buildAdminViewConfigEntry, buildAdminViewEntrySource, buildAdminViewRegistrySource, buildAdminViewStyleSource, buildCoreDataAdminViewConfigSource, buildCoreDataAdminViewDataSource, buildCoreDataAdminViewScreenSource, buildCoreDataAdminViewTypesSource, buildDefaultAdminViewConfigSource, buildDefaultAdminViewDataSource, buildDefaultAdminViewScreenSource, buildDefaultAdminViewTypesSource, buildRestAdminViewConfigSource, buildRestAdminViewDataSource, buildRestAdminViewTypesSource, buildRestSettingsAdminViewConfigSource, buildRestSettingsAdminViewDataSource, buildRestSettingsAdminViewScreenSource, buildRestSettingsAdminViewTypesSource, };
35
8
  /**
36
9
  * Build the generated admin-view item and dataset types for the selected source.
37
10
  */
38
11
  export function buildAdminViewTypesSource(adminViewSlug, restResource, coreDataSource) {
39
- const pascalName = toPascalCase(adminViewSlug);
40
- const coreDataRecordTypeName = `${pascalName}CoreDataRecord`;
41
- const itemTypeName = `${pascalName}AdminViewItem`;
42
- const dataSetTypeName = `${pascalName}AdminViewDataSet`;
43
12
  if (restResource) {
44
- const restPascalName = toPascalCase(restResource.slug);
45
- const restTypesModule = getAdminViewRelativeModuleSpecifier(adminViewSlug, restResource.typesFile);
46
- return `import type { ${restPascalName}Record } from ${quoteTsString(restTypesModule)};
47
-
48
- export type ${itemTypeName} = ${restPascalName}Record;
49
-
50
- export interface ${dataSetTypeName} {
51
- \titems: ${itemTypeName}[];
52
- \tpaginationInfo: {
53
- \t\ttotalItems: number;
54
- \t\ttotalPages: number;
55
- \t};
56
- }
57
- `;
13
+ return buildRestAdminViewTypesSource(adminViewSlug, restResource);
58
14
  }
59
15
  if (coreDataSource) {
60
- if (coreDataSource.entityKind === 'taxonomy') {
61
- return `export interface ${coreDataRecordTypeName} {
62
- \tcount?: number;
63
- \tdescription?: string;
64
- \tid: number;
65
- \tlink?: string;
66
- \tmeta?: Record<string, unknown>;
67
- \tname?: string;
68
- \tparent?: number;
69
- \tslug?: string;
70
- \ttaxonomy?: string;
71
- \t[key: string]: unknown;
72
- }
73
-
74
- export interface ${itemTypeName} {
75
- \tcount: number;
76
- \tdescription: string;
77
- \tid: number;
78
- \tlink: string;
79
- \tname: string;
80
- \tparent: number;
81
- \traw: ${coreDataRecordTypeName};
82
- \tslug: string;
83
- \ttaxonomy: string;
84
- }
85
-
86
- export interface ${dataSetTypeName} {
87
- \titems: ${itemTypeName}[];
88
- \tpaginationInfo: {
89
- \t\ttotalItems: number;
90
- \t\ttotalPages: number;
91
- \t};
92
- }
93
- `;
94
- }
95
- return `export interface ${coreDataRecordTypeName} {
96
- \tid: number;
97
- \tdate?: string;
98
- \tmodified?: string;
99
- \tname?: string;
100
- \tslug?: string;
101
- \tstatus?: string;
102
- \ttitle?: string | {
103
- \t\traw?: string;
104
- \t\trendered?: string;
105
- \t};
106
- \t[key: string]: unknown;
107
- }
108
-
109
- export interface ${itemTypeName} {
110
- \tid: number;
111
- \traw: ${coreDataRecordTypeName};
112
- \tslug: string;
113
- \tstatus: string;
114
- \ttitle: string;
115
- \tupdatedAt: string;
116
- }
117
-
118
- export interface ${dataSetTypeName} {
119
- \titems: ${itemTypeName}[];
120
- \tpaginationInfo: {
121
- \t\ttotalItems: number;
122
- \t\ttotalPages: number;
123
- \t};
124
- }
125
- `;
16
+ return buildCoreDataAdminViewTypesSource(adminViewSlug, coreDataSource);
126
17
  }
127
- return `export type ${pascalName}AdminViewStatus = 'draft' | 'published';
128
-
129
- export interface ${itemTypeName} {
130
- \tid: number;
131
- \towner: string;
132
- \tstatus: ${pascalName}AdminViewStatus;
133
- \ttitle: string;
134
- \tupdatedAt: string;
135
- }
136
-
137
- export interface ${dataSetTypeName} {
138
- \titems: ${itemTypeName}[];
139
- \tpaginationInfo: {
140
- \t\ttotalItems: number;
141
- \t\ttotalPages: number;
142
- \t};
143
- }
144
- `;
18
+ return buildDefaultAdminViewTypesSource(adminViewSlug);
145
19
  }
146
20
  /**
147
21
  * Build the generated DataViews config source for an admin-view scaffold.
148
22
  */
149
23
  export function buildAdminViewConfigSource(adminViewSlug, textDomain, source, restResource) {
150
- const pascalName = toPascalCase(adminViewSlug);
151
- const camelName = toCamelCase(adminViewSlug);
152
- const itemTypeName = `${pascalName}AdminViewItem`;
153
- const dataViewsName = `${camelName}AdminDataViews`;
154
- const isCoreDataSource = isAdminViewCoreDataSource(source);
155
- const isTaxonomyCoreDataSource = isAdminViewCoreDataSource(source) && source.entityKind === 'taxonomy';
156
- const defaultViewFields = restResource
157
- ? "['id']"
158
- : isTaxonomyCoreDataSource
159
- ? "['name', 'slug', 'count']"
160
- : isCoreDataSource
161
- ? "['title', 'slug', 'status', 'updatedAt']"
162
- : "['title', 'status', 'updatedAt']";
163
- const searchEnabled = restResource ? 'false' : 'true';
164
- const titleFieldSource = restResource
165
- ? ''
166
- : isTaxonomyCoreDataSource
167
- ? "\ttitleField: 'name',\n"
168
- : "\ttitleField: 'title',\n";
169
- const defaultViewEnhancementsSource = restResource
170
- ? ''
171
- : isTaxonomyCoreDataSource
172
- ? "\t\ttitleField: 'name',\n"
173
- : isCoreDataSource
174
- ? "\t\ttitleField: 'title',\n"
175
- : `\t\tsort: {
176
- \t\t\tdirection: 'desc',
177
- \t\t\tfield: 'updatedAt',
178
- \t\t},
179
- \t\ttitleField: 'title',
180
- `;
181
- const additionalFieldsSource = restResource
182
- ? '\t\t// REST-backed screens start with the guaranteed ID column. Add project-owned fields here once they are declared on the REST record type.'
183
- : isTaxonomyCoreDataSource
184
- ? `\t\tcount: {
185
- \t\t\tlabel: __( 'Count', ${quoteTsString(textDomain)} ),
186
- \t\t\tschema: { type: 'integer' },
187
- \t\t},
188
- \t\tdescription: {
189
- \t\t\tlabel: __( 'Description', ${quoteTsString(textDomain)} ),
190
- \t\t\tschema: { type: 'string' },
191
- \t\t},
192
- \t\tlink: {
193
- \t\t\tlabel: __( 'Link', ${quoteTsString(textDomain)} ),
194
- \t\t\tschema: { format: 'uri', type: 'string' },
195
- \t\t},
196
- \t\tname: {
197
- \t\t\tenableGlobalSearch: true,
198
- \t\t\tlabel: __( 'Name', ${quoteTsString(textDomain)} ),
199
- \t\t\tschema: { type: 'string' },
200
- \t\t},
201
- \t\tparent: {
202
- \t\t\tlabel: __( 'Parent', ${quoteTsString(textDomain)} ),
203
- \t\t\tschema: { type: 'integer' },
204
- \t\t},
205
- \t\tslug: {
206
- \t\t\tenableGlobalSearch: true,
207
- \t\t\tlabel: __( 'Slug', ${quoteTsString(textDomain)} ),
208
- \t\t\tschema: { type: 'string' },
209
- \t\t},
210
- \t\ttaxonomy: {
211
- \t\t\tlabel: __( 'Taxonomy', ${quoteTsString(textDomain)} ),
212
- \t\t\tschema: { type: 'string' },
213
- \t\t},`
214
- : isCoreDataSource
215
- ? `\t\tslug: {
216
- \t\t\tenableGlobalSearch: true,
217
- \t\t\tlabel: __( 'Slug', ${quoteTsString(textDomain)} ),
218
- \t\t\tschema: { type: 'string' },
219
- \t\t},
220
- \t\tstatus: {
221
- \t\t\tlabel: __( 'Status', ${quoteTsString(textDomain)} ),
222
- \t\t\tschema: { type: 'string' },
223
- \t\t},
224
- \t\ttitle: {
225
- \t\t\tenableGlobalSearch: true,
226
- \t\t\tlabel: __( 'Name', ${quoteTsString(textDomain)} ),
227
- \t\t\tschema: { type: 'string' },
228
- \t\t},
229
- \t\tupdatedAt: {
230
- \t\t\tlabel: __( 'Updated', ${quoteTsString(textDomain)} ),
231
- \t\t\tschema: { format: 'date-time', type: 'string' },
232
- \t\t\ttype: 'datetime',
233
- \t\t},`
234
- : `\t\towner: {
235
- \t\t\tlabel: __( 'Owner', ${quoteTsString(textDomain)} ),
236
- \t\t\tschema: { type: 'string' },
237
- \t\t},
238
- \t\tstatus: {
239
- \t\t\tfilterBy: { operators: ['isAny', 'isNone'] },
240
- \t\t\tlabel: __( 'Status', ${quoteTsString(textDomain)} ),
241
- \t\t\tschema: {
242
- \t\t\t\tenum: ['draft', 'published'],
243
- \t\t\t\tenumLabels: {
244
- \t\t\t\t\tdraft: __( 'Draft', ${quoteTsString(textDomain)} ),
245
- \t\t\t\t\tpublished: __( 'Published', ${quoteTsString(textDomain)} ),
246
- \t\t\t\t},
247
- \t\t\t\ttype: 'string',
248
- \t\t\t},
249
- \t\t},
250
- \t\ttitle: {
251
- \t\t\tenableGlobalSearch: true,
252
- \t\t\tenableSorting: true,
253
- \t\t\tlabel: __( 'Title', ${quoteTsString(textDomain)} ),
254
- \t\t\tschema: { type: 'string' },
255
- \t\t},
256
- \t\tupdatedAt: {
257
- \t\t\tenableSorting: true,
258
- \t\t\tlabel: __( 'Updated', ${quoteTsString(textDomain)} ),
259
- \t\t\tschema: { format: 'date-time', type: 'string' },
260
- \t\t\ttype: 'datetime',
261
- \t\t},`;
262
- return `import { defineDataViews } from '@wp-typia/dataviews';
263
- import { __ } from '@wordpress/i18n';
264
-
265
- import type { ${itemTypeName} } from './types';
266
-
267
- export const ${dataViewsName} = defineDataViews<${itemTypeName}>({
268
- \tidField: 'id',
269
- \tsearch: ${searchEnabled},
270
- \tsearchLabel: __( 'Search records', ${quoteTsString(textDomain)} ),
271
- ${titleFieldSource}
272
- \tdefaultView: {
273
- \t\tfields: ${defaultViewFields},
274
- \t\tpage: 1,
275
- \t\tperPage: 10,
276
- ${defaultViewEnhancementsSource}
277
- \t\ttype: 'table',
278
- \t},
279
- \tfields: {
280
- \t\tid: {
281
- \t\t\tenableHiding: false,
282
- \t\t\tlabel: __( 'ID', ${quoteTsString(textDomain)} ),
283
- \t\t\treadOnly: true,
284
- \t\t\tschema: { type: 'integer' },
285
- \t\t},
286
- ${additionalFieldsSource}
287
- \t},
288
- });
289
- `;
290
- }
291
- export function buildDefaultAdminViewDataSource(adminViewSlug) {
292
- const pascalName = toPascalCase(adminViewSlug);
293
- const camelName = toCamelCase(adminViewSlug);
294
- const title = toTitleCase(adminViewSlug);
295
- const itemTypeName = `${pascalName}AdminViewItem`;
296
- const dataSetTypeName = `${pascalName}AdminViewDataSet`;
297
- const queryTypeName = `${pascalName}AdminViewQuery`;
298
- const dataViewsName = `${camelName}AdminDataViews`;
299
- const fetchName = `fetch${pascalName}AdminViewData`;
300
- return `import type { DataViewsView } from '@wp-typia/dataviews';
301
-
302
- import { ${dataViewsName} } from './config';
303
- import type { ${dataSetTypeName}, ${itemTypeName} } from './types';
304
-
305
- export interface ${queryTypeName} {
306
- \tpage?: number;
307
- \tperPage?: number;
308
- \tsearch?: string;
309
- }
310
-
311
- const STARTER_ITEMS: ${itemTypeName}[] = [
312
- \t{
313
- \t\tid: 1,
314
- \t\towner: 'Editorial',
315
- \t\tstatus: 'published',
316
- \t\ttitle: ${quoteTsString(`${title} launch checklist`)},
317
- \t\tupdatedAt: '2026-04-01T10:30:00Z',
318
- \t},
319
- \t{
320
- \t\tid: 2,
321
- \t\towner: 'Design',
322
- \t\tstatus: 'draft',
323
- \t\ttitle: ${quoteTsString(`${title} content refresh`)},
324
- \t\tupdatedAt: '2026-04-03T14:15:00Z',
325
- \t},
326
- \t{
327
- \t\tid: 3,
328
- \t\towner: 'Operations',
329
- \t\tstatus: 'published',
330
- \t\ttitle: ${quoteTsString(`${title} support handoff`)},
331
- \t\tupdatedAt: '2026-04-08T08:45:00Z',
332
- \t},
333
- ];
334
-
335
- function matchesSearch(item: ${itemTypeName}, search: string | undefined): boolean {
336
- \tif (!search) {
337
- \t\treturn true;
338
- \t}
339
-
340
- \tconst needle = search.toLowerCase();
341
- \treturn [item.title, item.owner, item.status].some((value) =>
342
- \t\tvalue.toLowerCase().includes(needle),
343
- \t);
344
- }
345
-
346
- export async function ${fetchName}(
347
- \tview: DataViewsView<${itemTypeName}>,
348
- ): Promise<${dataSetTypeName}> {
349
- \tconst query = ${dataViewsName}.toQueryArgs<${queryTypeName}>(view, {
350
- \t\tperPageParam: 'perPage',
351
- \t});
352
- \tconst requestedPage = query.page ?? 1;
353
- \tconst page = requestedPage > 0 ? requestedPage : 1;
354
- \tconst requestedPerPage = query.perPage ?? view.perPage ?? 10;
355
- \tconst perPage = requestedPerPage > 0 ? requestedPerPage : 10;
356
- \tconst filteredItems = STARTER_ITEMS.filter((item) =>
357
- \t\tmatchesSearch(item, query.search),
358
- \t);
359
- \tconst offset = (page - 1) * perPage;
360
- \tconst items = filteredItems.slice(offset, offset + perPage);
361
-
362
- \treturn {
363
- \t\titems,
364
- \t\tpaginationInfo: {
365
- \t\t\ttotalItems: filteredItems.length,
366
- \t\t\ttotalPages: Math.max(1, Math.ceil(filteredItems.length / perPage)),
367
- \t\t},
368
- \t};
369
- }
370
- `;
371
- }
372
- export function buildRestAdminViewDataSource(adminViewSlug, restResource) {
373
- const pascalName = toPascalCase(adminViewSlug);
374
- const restPascalName = toPascalCase(restResource.slug);
375
- const camelName = toCamelCase(adminViewSlug);
376
- const itemTypeName = `${pascalName}AdminViewItem`;
377
- const dataSetTypeName = `${pascalName}AdminViewDataSet`;
378
- const dataViewsName = `${camelName}AdminDataViews`;
379
- const fetchName = `fetch${pascalName}AdminViewData`;
380
- const restApiModule = getAdminViewRelativeModuleSpecifier(adminViewSlug, restResource.apiFile);
381
- const restTypesModule = getAdminViewRelativeModuleSpecifier(adminViewSlug, restResource.typesFile);
382
- return `import type { DataViewsView } from '@wp-typia/dataviews';
383
-
384
- import { listResource } from ${quoteTsString(restApiModule)};
385
- import type { ${restPascalName}ListQuery } from ${quoteTsString(restTypesModule)};
386
- import { ${dataViewsName} } from './config';
387
- import type { ${dataSetTypeName}, ${itemTypeName} } from './types';
388
-
389
- function resolveTotalPages(total: number, perPage: number | undefined): number {
390
- \tconst resolvedPerPage = perPage && perPage > 0 ? perPage : 1;
391
- \treturn Math.max(1, Math.ceil(total / resolvedPerPage));
392
- }
393
-
394
- export async function ${fetchName}(
395
- \tview: DataViewsView<${itemTypeName}>,
396
- ): Promise<${dataSetTypeName}> {
397
- \tconst query = ${dataViewsName}.toQueryArgs<${restPascalName}ListQuery>(view, {
398
- \t\tperPageParam: 'perPage',
399
- \t\tsearchParam: false,
400
- \t});
401
- \tconst result = await listResource({
402
- \t\tpage: query.page,
403
- \t\tperPage: query.perPage,
404
- \t});
405
- \tif (!result.isValid || !result.data) {
406
- \t\tthrow new Error('Unable to load REST resource records.');
407
- \t}
408
-
409
- \tconst response = result.data;
410
-
411
- \treturn {
412
- \t\titems: response.items,
413
- \t\tpaginationInfo: {
414
- \t\t\ttotalItems: response.total,
415
- \t\t\ttotalPages: resolveTotalPages(response.total, response.perPage ?? query.perPage),
416
- \t\t},
417
- \t};
418
- }
419
- `;
420
- }
421
- /**
422
- * Build type aliases for a manual REST settings form admin screen.
423
- */
424
- export function buildRestSettingsAdminViewTypesSource(adminViewSlug, restResource) {
425
- const pascalName = toPascalCase(adminViewSlug);
426
- const restTypesModule = getAdminViewRelativeModuleSpecifier(adminViewSlug, restResource.typesFile);
427
- const formStateTypeName = `${pascalName}SettingsFormState`;
428
- const loadResultTypeName = `${pascalName}SettingsLoadResult`;
429
- return `import type {
430
- \t${restResource.bodyTypeName},
431
- \t${restResource.queryTypeName},
432
- \t${restResource.responseTypeName},
433
- } from ${quoteTsString(restTypesModule)};
434
-
435
- export type ${pascalName}SettingsRequest = ${restResource.bodyTypeName};
436
- export type ${pascalName}SettingsQuery = ${restResource.queryTypeName};
437
- export type ${pascalName}SettingsResponse = ${restResource.responseTypeName};
438
- export type ${formStateTypeName} = Partial<${pascalName}SettingsRequest>;
439
-
440
- export interface ${loadResultTypeName} {
441
- \tform: ${formStateTypeName};
442
- \tresponse: ${pascalName}SettingsResponse | null;
443
- }
444
- `;
445
- }
446
- /**
447
- * Build display metadata for a manual REST settings form admin screen.
448
- */
449
- export function buildRestSettingsAdminViewConfigSource(adminViewSlug, textDomain, restResource) {
450
- const pascalName = toPascalCase(adminViewSlug);
451
- const camelName = toCamelCase(adminViewSlug);
452
- const title = toTitleCase(adminViewSlug);
453
- const configName = `${camelName}SettingsConfig`;
454
- const formStateTypeName = `${pascalName}SettingsFormState`;
455
- const secretFieldSource = restResource.secretFieldName && restResource.secretStateFieldName
456
- ? `\t{
457
- \t\tdescription: __( 'Write-only secret value. Leave blank to keep the existing secret unless your route treats blank values as removal.', ${quoteTsString(textDomain)} ),
458
- \t\tid: ${quoteTsString(restResource.secretFieldName)},
459
- \t\tlabel: __( ${quoteTsString(toTitleCase(restResource.secretFieldName))}, ${quoteTsString(textDomain)} ),
460
- \t\tsecretStateField: ${quoteTsString(restResource.secretStateFieldName)},
461
- \t\ttype: 'secret',
462
- \t},`
463
- : '';
464
- return `import { __ } from '@wordpress/i18n';
465
-
466
- import type { ${formStateTypeName} } from './types';
467
-
468
- export type ${pascalName}SettingsFieldType = 'secret' | 'text' | 'textarea';
469
-
470
- export interface ${pascalName}SettingsField {
471
- \tdescription?: string;
472
- \tid: Extract<keyof ${formStateTypeName}, string> | string;
473
- \tlabel: string;
474
- \tsecretStateField?: string;
475
- \ttype: ${pascalName}SettingsFieldType;
476
- }
477
-
478
- export const ${configName} = {
479
- \tdescription: __( 'This generated settings form is backed by the ${restResource.slug} REST contract. Adjust config.ts and data.ts as the contract becomes product-specific.', ${quoteTsString(textDomain)} ),
480
- \tfields: [
481
- \t\t{
482
- \t\t\tdescription: __( 'Primary settings payload for this integration.', ${quoteTsString(textDomain)} ),
483
- \t\t\tid: 'payload',
484
- \t\t\tlabel: __( 'Payload', ${quoteTsString(textDomain)} ),
485
- \t\t\ttype: 'textarea',
486
- \t\t},
487
- \t\t{
488
- \t\t\tdescription: __( 'Optional operator note included with the save request.', ${quoteTsString(textDomain)} ),
489
- \t\t\tid: 'comment',
490
- \t\t\tlabel: __( 'Comment', ${quoteTsString(textDomain)} ),
491
- \t\t\ttype: 'text',
492
- \t\t},
493
- ${secretFieldSource}
494
- \t] satisfies ${pascalName}SettingsField[],
495
- \tsecretFieldName: ${restResource.secretFieldName ? quoteTsString(restResource.secretFieldName) : 'undefined'},
496
- \tsecretStateFieldName: ${restResource.secretStateFieldName ? quoteTsString(restResource.secretStateFieldName) : 'undefined'},
497
- \ttitle: __( ${quoteTsString(title)}, ${quoteTsString(textDomain)} ),
498
- };
499
- `;
500
- }
501
- /**
502
- * Build data helpers for a manual REST settings form admin screen.
503
- */
504
- export function buildRestSettingsAdminViewDataSource(adminViewSlug, restResource) {
505
- const pascalName = toPascalCase(adminViewSlug);
506
- const restApiModule = getAdminViewRelativeModuleSpecifier(adminViewSlug, restResource.apiFile);
507
- const formStateTypeName = `${pascalName}SettingsFormState`;
508
- const loadResultTypeName = `${pascalName}SettingsLoadResult`;
509
- const loadName = `load${pascalName}Settings`;
510
- const saveName = `save${pascalName}Settings`;
511
- const initialFields = [
512
- '\tpayload: \'\',',
513
- '\tcomment: \'\',',
514
- ].join('\n');
515
- const requestBodySource = restResource.secretFieldName
516
- ? `\tconst requestBody = { ...form } as Record<string, unknown>;
517
- \tif (requestBody[${quoteTsString(restResource.secretFieldName)}] === '') {
518
- \t\tdelete requestBody[${quoteTsString(restResource.secretFieldName)}];
519
- \t}
520
- `
521
- : `\tconst requestBody = form as Record<string, unknown>;
522
- `;
523
- return `import { callManualRestContract } from ${quoteTsString(restApiModule)};
524
- import type {
525
- \t${formStateTypeName},
526
- \t${loadResultTypeName},
527
- \t${pascalName}SettingsQuery,
528
- \t${pascalName}SettingsRequest,
529
- \t${pascalName}SettingsResponse,
530
- } from './types';
531
-
532
- function formatValidationError(prefix: string, errors: unknown): string {
533
- \tif (!Array.isArray(errors) || errors.length === 0) {
534
- \t\treturn prefix;
535
- \t}
536
-
537
- \treturn \`\${prefix} \${JSON.stringify(errors)}\`;
538
- }
539
-
540
- export function createInitial${pascalName}SettingsFormState(): ${formStateTypeName} {
541
- \treturn {
542
- ${initialFields}
543
- \t} as ${formStateTypeName};
544
- }
545
-
546
- export async function ${loadName}(): Promise<${loadResultTypeName}> {
547
- \treturn {
548
- \t\tform: createInitial${pascalName}SettingsFormState(),
549
- \t\tresponse: null,
550
- \t};
551
- }
552
-
553
- export async function ${saveName}(
554
- \tform: ${formStateTypeName},
555
- \tquery: Partial<${pascalName}SettingsQuery> = {},
556
- ): Promise<${pascalName}SettingsResponse> {
557
- ${requestBodySource}
558
- \tconst result = await callManualRestContract({
559
- \t\tbody: requestBody as unknown as ${pascalName}SettingsRequest,
560
- \t\tquery: query as ${pascalName}SettingsQuery,
561
- \t});
562
- \tif (!result.isValid) {
563
- \t\tconst message =
564
- \t\t\tresult.validationTarget === 'request'
565
- \t\t\t\t? 'Settings request failed validation.'
566
- \t\t\t\t: 'Settings response failed validation.';
567
- \t\tthrow new Error(formatValidationError(message, result.errors));
568
- \t}
569
-
570
- \treturn result.data as ${pascalName}SettingsResponse;
571
- }
572
- `;
24
+ if (restResource) {
25
+ return buildRestAdminViewConfigSource(adminViewSlug, textDomain);
26
+ }
27
+ if (isAdminViewCoreDataSource(source)) {
28
+ return buildCoreDataAdminViewConfigSource(adminViewSlug, textDomain, source);
29
+ }
30
+ return buildDefaultAdminViewConfigSource(adminViewSlug, textDomain);
573
31
  }
574
32
  /**
575
- * Build a React settings form screen backed by a manual REST contract.
33
+ * Delegates default admin-view screen generation to buildDefaultAdminViewScreenSource.
34
+ *
35
+ * @param adminViewSlug - Slug for the generated admin view.
36
+ * @param textDomain - WordPress i18n text domain for generated labels.
37
+ * @returns Generated TSX source for the default admin-view screen.
576
38
  */
577
- export function buildRestSettingsAdminViewScreenSource(adminViewSlug, textDomain) {
578
- const pascalName = toPascalCase(adminViewSlug);
579
- const componentName = `${pascalName}AdminViewScreen`;
580
- const formStateTypeName = `${pascalName}SettingsFormState`;
581
- const responseTypeName = `${pascalName}SettingsResponse`;
582
- const camelName = toCamelCase(adminViewSlug);
583
- const configName = `${camelName}SettingsConfig`;
584
- const loadName = `load${pascalName}Settings`;
585
- const saveName = `save${pascalName}Settings`;
586
- return `import {
587
- \tButton,
588
- \tNotice,
589
- \tSpinner,
590
- \tTextControl,
591
- \tTextareaControl,
592
- } from '@wordpress/components';
593
- import { useEffect, useState } from '@wordpress/element';
594
- import { __ } from '@wordpress/i18n';
595
-
596
- import { ${configName} } from './config';
597
- import { ${loadName}, ${saveName} } from './data';
598
- import type { ${formStateTypeName}, ${responseTypeName} } from './types';
599
-
600
- function getFieldValue(form: ${formStateTypeName}, fieldId: string): string {
601
- \tconst value = (form as Record<string, unknown>)[fieldId];
602
- \tif (typeof value === 'string') {
603
- \t\treturn value;
604
- \t}
605
- \tif (value == null) {
606
- \t\treturn '';
607
- \t}
608
-
609
- \treturn String(value);
610
- }
611
-
612
- function getSecretState(response: ${responseTypeName} | null): boolean | null {
613
- \tconst stateField = ${configName}.secretStateFieldName;
614
- \tif (!stateField || !response) {
615
- \t\treturn null;
616
- \t}
617
-
618
- \tconst value = (response as unknown as Record<string, unknown>)[stateField];
619
- \treturn typeof value === 'boolean' ? value : null;
620
- }
621
-
622
- export function ${componentName}() {
623
- \tconst [form, setForm] = useState<${formStateTypeName}>({});
624
- \tconst [response, setResponse] = useState<${responseTypeName} | null>(null);
625
- \tconst [error, setError] = useState<string | null>(null);
626
- \tconst [isLoading, setIsLoading] = useState(true);
627
- \tconst [isSaving, setIsSaving] = useState(false);
628
- \tconst [successMessage, setSuccessMessage] = useState<string | null>(null);
629
-
630
- \tuseEffect(() => {
631
- \t\tlet isCurrent = true;
632
- \t\tsetIsLoading(true);
633
- \t\tsetError(null);
634
-
635
- \t\tvoid ${loadName}()
636
- \t\t\t.then((result) => {
637
- \t\t\t\tif (isCurrent) {
638
- \t\t\t\t\tsetForm(result.form);
639
- \t\t\t\t\tsetResponse(result.response);
640
- \t\t\t\t}
641
- \t\t\t})
642
- \t\t\t.catch((nextError: unknown) => {
643
- \t\t\t\tif (isCurrent) {
644
- \t\t\t\t\tsetError(
645
- \t\t\t\t\t\tnextError instanceof Error
646
- \t\t\t\t\t\t\t? nextError.message
647
- \t\t\t\t\t\t\t: __( 'Unable to prepare settings form.', ${quoteTsString(textDomain)} ),
648
- \t\t\t\t\t);
649
- \t\t\t\t}
650
- \t\t\t})
651
- \t\t\t.finally(() => {
652
- \t\t\t\tif (isCurrent) {
653
- \t\t\t\t\tsetIsLoading(false);
654
- \t\t\t\t}
655
- \t\t\t});
656
-
657
- \t\treturn () => {
658
- \t\t\tisCurrent = false;
659
- \t\t};
660
- \t}, []);
661
-
662
- \tconst setFormValue = (fieldId: string, value: string) => {
663
- \t\tsetForm(
664
- \t\t\t(current) =>
665
- \t\t\t\t({
666
- \t\t\t\t\t...current,
667
- \t\t\t\t\t[fieldId]: value,
668
- \t\t\t\t}) as ${formStateTypeName},
669
- \t\t);
670
- \t};
671
- \tconst secretState = getSecretState(response);
672
-
673
- \tconst handleSubmit = (event: { preventDefault: () => void }) => {
674
- \t\tevent.preventDefault();
675
- \t\tsetError(null);
676
- \t\tsetSuccessMessage(null);
677
- \t\tsetIsSaving(true);
678
-
679
- \t\tvoid ${saveName}(form)
680
- \t\t\t.then((nextResponse) => {
681
- \t\t\t\tsetResponse(nextResponse);
682
- \t\t\t\tsetSuccessMessage(__( 'Settings saved.', ${quoteTsString(textDomain)} ));
683
- \t\t\t})
684
- \t\t\t.catch((nextError: unknown) => {
685
- \t\t\t\tsetError(
686
- \t\t\t\t\tnextError instanceof Error
687
- \t\t\t\t\t\t? nextError.message
688
- \t\t\t\t\t\t: __( 'Unable to save settings.', ${quoteTsString(textDomain)} ),
689
- \t\t\t\t);
690
- \t\t\t})
691
- \t\t\t.finally(() => setIsSaving(false));
692
- \t};
693
-
694
- \treturn (
695
- \t\t<div className="wp-typia-admin-view-screen wp-typia-admin-view-screen--settings">
696
- \t\t\t<header className="wp-typia-admin-view-screen__header">
697
- \t\t\t\t<div>
698
- \t\t\t\t\t<p className="wp-typia-admin-view-screen__eyebrow">
699
- \t\t\t\t\t\t{ __( 'Typed settings screen', ${quoteTsString(textDomain)} ) }
700
- \t\t\t\t\t</p>
701
- \t\t\t\t\t<h1>{ ${configName}.title }</h1>
702
- \t\t\t\t\t<p>{ ${configName}.description }</p>
703
- \t\t\t\t</div>
704
- \t\t\t\t<div className="wp-typia-admin-view-screen__actions">
705
- \t\t\t\t\t{ isLoading || isSaving ? <Spinner /> : null }
706
- \t\t\t\t</div>
707
- \t\t\t</header>
708
- \t\t\t{ error ? (
709
- \t\t\t\t<Notice isDismissible={ false } status="error">
710
- \t\t\t\t\t{ error }
711
- \t\t\t\t</Notice>
712
- \t\t\t) : null }
713
- \t\t\t{ successMessage ? (
714
- \t\t\t\t<Notice isDismissible={ false } status="success">
715
- \t\t\t\t\t{ successMessage }
716
- \t\t\t\t</Notice>
717
- \t\t\t) : null }
718
- \t\t\t{ secretState !== null ? (
719
- \t\t\t\t<Notice isDismissible={ false } status="info">
720
- \t\t\t\t\t{ secretState
721
- \t\t\t\t\t\t? __( 'A secret is currently configured for this integration.', ${quoteTsString(textDomain)} )
722
- \t\t\t\t\t\t: __( 'No secret is currently configured for this integration.', ${quoteTsString(textDomain)} ) }
723
- \t\t\t\t</Notice>
724
- \t\t\t) : null }
725
- \t\t\t<form className="wp-typia-admin-view-screen__settings-form" onSubmit={ handleSubmit }>
726
- \t\t\t\t{ ${configName}.fields.map((field) => (
727
- \t\t\t\t\t<div className="wp-typia-admin-view-screen__field" key={ field.id }>
728
- \t\t\t\t\t\t{ field.type === 'textarea' ? (
729
- \t\t\t\t\t\t\t<TextareaControl
730
- \t\t\t\t\t\t\t\thelp={ field.description }
731
- \t\t\t\t\t\t\t\tlabel={ field.label }
732
- \t\t\t\t\t\t\t\tonChange={ (value) => setFormValue(field.id, value) }
733
- \t\t\t\t\t\t\t\tvalue={ getFieldValue(form, field.id) }
734
- \t\t\t\t\t\t\t/>
735
- \t\t\t\t\t\t) : (
736
- \t\t\t\t\t\t\t<TextControl
737
- \t\t\t\t\t\t\t\thelp={ field.description }
738
- \t\t\t\t\t\t\t\tlabel={ field.label }
739
- \t\t\t\t\t\t\t\tonChange={ (value) => setFormValue(field.id, value) }
740
- \t\t\t\t\t\t\t\ttype={ field.type === 'secret' ? 'password' : 'text' }
741
- \t\t\t\t\t\t\t\tvalue={ getFieldValue(form, field.id) }
742
- \t\t\t\t\t\t\t/>
743
- \t\t\t\t\t\t) }
744
- \t\t\t\t\t</div>
745
- \t\t\t\t)) }
746
- \t\t\t\t<Button
747
- \t\t\t\t\tdisabled={ isLoading || isSaving }
748
- \t\t\t\t\tisBusy={ isSaving }
749
- \t\t\t\t\ttype="submit"
750
- \t\t\t\t\tvariant="primary"
751
- \t\t\t\t>
752
- \t\t\t\t\t{ __( 'Save settings', ${quoteTsString(textDomain)} ) }
753
- \t\t\t\t</Button>
754
- \t\t\t</form>
755
- \t\t</div>
756
- \t);
757
- }
758
- `;
39
+ export function buildAdminViewScreenSource(adminViewSlug, textDomain) {
40
+ return buildDefaultAdminViewScreenSource(adminViewSlug, textDomain);
759
41
  }
760
42
  /**
761
- * Build a core-data-backed admin-view data module for a supported entity family.
43
+ * Delegates admin-view PHP registration generation to buildSharedAdminViewPhpSource.
44
+ *
45
+ * @param adminViewSlug - Slug for the generated admin view.
46
+ * @param workspace - Workspace project metadata for PHP prefixes and paths.
47
+ * @returns Generated PHP source for registering and enqueueing the admin view.
762
48
  */
763
- export function buildCoreDataAdminViewDataSource(adminViewSlug, coreDataSource) {
764
- const pascalName = toPascalCase(adminViewSlug);
765
- const camelName = toCamelCase(adminViewSlug);
766
- const coreDataRecordTypeName = `${pascalName}CoreDataRecord`;
767
- const dataSetTypeName = `${pascalName}AdminViewDataSet`;
768
- const itemTypeName = `${pascalName}AdminViewItem`;
769
- const queryTypeName = `${pascalName}AdminViewQuery`;
770
- const dataViewsName = `${camelName}AdminDataViews`;
771
- const useEntityRecordName = `use${pascalName}EntityRecord`;
772
- const useEntityRecordsName = `use${pascalName}EntityRecords`;
773
- const useAdminViewDataName = `use${pascalName}AdminViewData`;
774
- if (coreDataSource.entityKind === 'taxonomy') {
775
- return `import type { DataViewsView } from '@wp-typia/dataviews';
776
- import { useEntityRecord, useEntityRecords } from '@wordpress/core-data';
777
- import { useMemo } from '@wordpress/element';
778
-
779
- import { ${dataViewsName} } from './config';
780
- import type {
781
- \t${coreDataRecordTypeName},
782
- \t${dataSetTypeName},
783
- \t${itemTypeName},
784
- } from './types';
785
-
786
- export interface ${queryTypeName} {
787
- \tpage?: number;
788
- \tper_page?: number;
789
- \tsearch?: string;
790
- }
791
-
792
- const CORE_DATA_ENTITY_KIND = ${quoteTsString(coreDataSource.entityKind)};
793
- const CORE_DATA_ENTITY_NAME = ${quoteTsString(coreDataSource.entityName)};
794
-
795
- function normalizeCoreDataNumber(value: unknown): number {
796
- \treturn typeof value === 'number' && Number.isFinite(value) ? value : 0;
797
- }
798
-
799
- function normalizeCoreDataString(value: unknown): string {
800
- \treturn typeof value === 'string' ? value : '';
801
- }
802
-
803
- function normalizeTaxonomyRecord(record: ${coreDataRecordTypeName}): ${itemTypeName} {
804
- \treturn {
805
- \t\tcount: normalizeCoreDataNumber(record.count),
806
- \t\tdescription: normalizeCoreDataString(record.description),
807
- \t\tid: record.id,
808
- \t\tlink: normalizeCoreDataString(record.link),
809
- \t\tname: normalizeCoreDataString(record.name) || normalizeCoreDataString(record.slug),
810
- \t\tparent: normalizeCoreDataNumber(record.parent),
811
- \t\traw: record,
812
- \t\tslug: normalizeCoreDataString(record.slug),
813
- \t\ttaxonomy: normalizeCoreDataString(record.taxonomy),
814
- \t};
815
- \t}
816
-
817
- export function ${useEntityRecordName}(recordId: number | undefined) {
818
- \treturn useEntityRecord<${coreDataRecordTypeName}>(
819
- \t\tCORE_DATA_ENTITY_KIND,
820
- \t\tCORE_DATA_ENTITY_NAME,
821
- \t\trecordId ?? 0,
822
- \t\t{ enabled: typeof recordId === 'number' },
823
- \t);
824
- \t}
825
-
826
- export function ${useEntityRecordsName}(view: DataViewsView<${itemTypeName}>) {
827
- \tconst query = ${dataViewsName}.toQueryArgs<${queryTypeName}>(view, {
828
- \t\tperPageParam: 'per_page',
829
- \t});
830
-
831
- \treturn useEntityRecords<${coreDataRecordTypeName}>(
832
- \t\tCORE_DATA_ENTITY_KIND,
833
- \t\tCORE_DATA_ENTITY_NAME,
834
- \t\tquery,
835
- \t);
836
- \t}
837
-
838
- export function ${useAdminViewDataName}(view: DataViewsView<${itemTypeName}>) {
839
- \tconst { hasResolved, isResolving, records, totalItems, totalPages } =
840
- \t\t${useEntityRecordsName}(view);
841
- \tconst items = useMemo(
842
- \t\t() => (records ?? []).map((record) => normalizeTaxonomyRecord(record)),
843
- \t\t[records],
844
- \t);
845
- \tconst dataSet = useMemo<${dataSetTypeName}>(
846
- \t\t() => ({
847
- \t\t\titems,
848
- \t\t\tpaginationInfo: {
849
- \t\t\t\ttotalItems: totalItems ?? items.length,
850
- \t\t\t\ttotalPages: Math.max(1, totalPages ?? 1),
851
- \t\t\t},
852
- \t\t}),
853
- \t\t[items, totalItems, totalPages],
854
- \t);
855
- \tconst error =
856
- \t\t!isResolving && hasResolved && records === null
857
- \t\t\t? 'Unable to load core-data entity records.'
858
- \t\t\t: null;
859
-
860
- \treturn {
861
- \t\tdataSet,
862
- \t\terror,
863
- \t\tisLoading: isResolving,
864
- \t};
865
- \t}
866
- `;
867
- }
868
- return `import type { DataViewsView } from '@wp-typia/dataviews';
869
- import { useEntityRecord, useEntityRecords } from '@wordpress/core-data';
870
- import { useMemo } from '@wordpress/element';
871
-
872
- import { ${dataViewsName} } from './config';
873
- import type {
874
- \t${coreDataRecordTypeName},
875
- \t${dataSetTypeName},
876
- \t${itemTypeName},
877
- } from './types';
878
-
879
- export interface ${queryTypeName} {
880
- \tpage?: number;
881
- \tper_page?: number;
882
- \tsearch?: string;
883
- }
884
-
885
- const CORE_DATA_ENTITY_KIND = ${quoteTsString(coreDataSource.entityKind)};
886
- const CORE_DATA_ENTITY_NAME = ${quoteTsString(coreDataSource.entityName)};
887
-
888
- function normalizeCoreDataString(value: unknown): string {
889
- \treturn typeof value === 'string' ? value : '';
890
- }
891
-
892
- function normalizeCoreDataTitle(record: ${coreDataRecordTypeName}): string {
893
- \tif (typeof record.title === 'string') {
894
- \t\treturn record.title;
895
- \t}
896
- \tif (record.title && typeof record.title === 'object') {
897
- \t\tif (typeof record.title.rendered === 'string') {
898
- \t\t\treturn record.title.rendered;
899
- \t\t}
900
- \t\tif (typeof record.title.raw === 'string') {
901
- \t\t\treturn record.title.raw;
902
- \t\t}
903
- \t}
904
-
905
- \treturn normalizeCoreDataString(record.name) || normalizeCoreDataString(record.slug);
906
- }
907
-
908
- function normalizeCoreDataUpdatedAt(record: ${coreDataRecordTypeName}): string {
909
- \treturn normalizeCoreDataString(record.modified) || normalizeCoreDataString(record.date);
910
- }
911
-
912
- function normalizeCoreDataRecord(record: ${coreDataRecordTypeName}): ${itemTypeName} {
913
- \treturn {
914
- \t\tid: record.id,
915
- \t\traw: record,
916
- \t\tslug: normalizeCoreDataString(record.slug),
917
- \t\tstatus: normalizeCoreDataString(record.status),
918
- \t\ttitle: normalizeCoreDataTitle(record),
919
- \t\tupdatedAt: normalizeCoreDataUpdatedAt(record),
920
- \t};
921
- }
922
-
923
- export function ${useEntityRecordName}(recordId: number | undefined) {
924
- \treturn useEntityRecord<${coreDataRecordTypeName}>(
925
- \t\tCORE_DATA_ENTITY_KIND,
926
- \t\tCORE_DATA_ENTITY_NAME,
927
- \t\trecordId ?? 0,
928
- \t\t{ enabled: typeof recordId === 'number' },
929
- \t);
930
- }
931
-
932
- export function ${useEntityRecordsName}(view: DataViewsView<${itemTypeName}>) {
933
- \tconst query = ${dataViewsName}.toQueryArgs<${queryTypeName}>(view, {
934
- \t\tperPageParam: 'per_page',
935
- \t});
936
-
937
- \treturn useEntityRecords<${coreDataRecordTypeName}>(
938
- \t\tCORE_DATA_ENTITY_KIND,
939
- \t\tCORE_DATA_ENTITY_NAME,
940
- \t\tquery,
941
- \t);
942
- }
943
-
944
- export function ${useAdminViewDataName}(view: DataViewsView<${itemTypeName}>) {
945
- \tconst { hasResolved, isResolving, records, totalItems, totalPages } =
946
- \t\t${useEntityRecordsName}(view);
947
- \tconst items = useMemo(
948
- \t\t() => (records ?? []).map((record) => normalizeCoreDataRecord(record)),
949
- \t\t[records],
950
- \t);
951
- \tconst dataSet = useMemo<${dataSetTypeName}>(
952
- \t\t() => ({
953
- \t\t\titems,
954
- \t\t\tpaginationInfo: {
955
- \t\t\t\ttotalItems: totalItems ?? items.length,
956
- \t\t\t\ttotalPages: Math.max(1, totalPages ?? 1),
957
- \t\t\t},
958
- \t\t}),
959
- \t\t[items, totalItems, totalPages],
960
- \t);
961
- \tconst error =
962
- \t\t!isResolving && hasResolved && records === null
963
- \t\t\t? 'Unable to load core-data entity records.'
964
- \t\t\t: null;
965
-
966
- \treturn {
967
- \t\tdataSet,
968
- \t\terror,
969
- \t\tisLoading: isResolving,
970
- \t};
971
- }
972
- `;
973
- }
974
- export function buildAdminViewScreenSource(adminViewSlug, textDomain) {
975
- const pascalName = toPascalCase(adminViewSlug);
976
- const camelName = toCamelCase(adminViewSlug);
977
- const itemTypeName = `${pascalName}AdminViewItem`;
978
- const dataSetTypeName = `${pascalName}AdminViewDataSet`;
979
- const componentName = `${pascalName}AdminViewScreen`;
980
- const dataViewsName = `${camelName}AdminDataViews`;
981
- const fetchName = `fetch${pascalName}AdminViewData`;
982
- const title = toTitleCase(adminViewSlug);
983
- return `import type { DataViewsConfig, DataViewsView } from '@wp-typia/dataviews';
984
- import { Button, Notice, Spinner } from '@wordpress/components';
985
- import { useEffect, useState } from '@wordpress/element';
986
- import { __ } from '@wordpress/i18n';
987
- import { DataViews } from '@wordpress/dataviews/wp';
988
-
989
- import { ${dataViewsName} } from './config';
990
- import { ${fetchName} } from './data';
991
- import type { ${dataSetTypeName}, ${itemTypeName} } from './types';
992
-
993
- const TypedDataViews = DataViews as unknown as <TItem extends object>(
994
- \tprops: DataViewsConfig<TItem>,
995
- ) => ReturnType<typeof DataViews>;
996
-
997
- const EMPTY_DATA_SET: ${dataSetTypeName} = {
998
- \titems: [],
999
- \tpaginationInfo: {
1000
- \t\ttotalItems: 0,
1001
- \t\ttotalPages: 1,
1002
- \t},
1003
- };
1004
-
1005
- export function ${componentName}() {
1006
- \tconst [view, setView] = useState<DataViewsView<${itemTypeName}>>(
1007
- \t\t${dataViewsName}.defaultView,
1008
- \t);
1009
- \tconst [dataSet, setDataSet] = useState<${dataSetTypeName}>(EMPTY_DATA_SET);
1010
- \tconst [error, setError] = useState<string | null>(null);
1011
- \tconst [isLoading, setIsLoading] = useState(true);
1012
- \tconst [reloadToken, setReloadToken] = useState(0);
1013
-
1014
- \tuseEffect(() => {
1015
- \t\tlet isCurrentRequest = true;
1016
- \t\tsetIsLoading(true);
1017
- \t\tsetError(null);
1018
-
1019
- \t\tvoid ${fetchName}(view)
1020
- \t\t\t.then((nextDataSet) => {
1021
- \t\t\t\tif (isCurrentRequest) {
1022
- \t\t\t\t\tsetDataSet(nextDataSet);
1023
- \t\t\t\t}
1024
- \t\t\t})
1025
- \t\t\t.catch((nextError: unknown) => {
1026
- \t\t\t\tif (isCurrentRequest) {
1027
- \t\t\t\t\tsetError(
1028
- \t\t\t\t\t\tnextError instanceof Error
1029
- \t\t\t\t\t\t\t? nextError.message
1030
- \t\t\t\t\t\t\t: __( 'Unable to load records.', ${quoteTsString(textDomain)} ),
1031
- \t\t\t\t\t);
1032
- \t\t\t\t}
1033
- \t\t\t})
1034
- \t\t\t.finally(() => {
1035
- \t\t\t\tif (isCurrentRequest) {
1036
- \t\t\t\t\tsetIsLoading(false);
1037
- \t\t\t\t}
1038
- \t\t\t});
1039
-
1040
- \t\treturn () => {
1041
- \t\t\tisCurrentRequest = false;
1042
- \t\t};
1043
- \t}, [reloadToken, view]);
1044
-
1045
- \tconst config = ${dataViewsName}.createConfig({
1046
- \t\tdata: dataSet.items,
1047
- \t\tisLoading,
1048
- \t\tonChangeView: setView,
1049
- \t\tpaginationInfo: dataSet.paginationInfo,
1050
- \t\tview,
1051
- \t});
1052
-
1053
- \treturn (
1054
- \t\t<div className="wp-typia-admin-view-screen">
1055
- \t\t\t<header className="wp-typia-admin-view-screen__header">
1056
- \t\t\t\t<div>
1057
- \t\t\t\t\t<p className="wp-typia-admin-view-screen__eyebrow">
1058
- \t\t\t\t\t\t{ __( 'DataViews admin screen', ${quoteTsString(textDomain)} ) }
1059
- \t\t\t\t\t</p>
1060
- \t\t\t\t\t<h1>{ __( ${quoteTsString(title)}, ${quoteTsString(textDomain)} ) }</h1>
1061
- \t\t\t\t\t<p>
1062
- \t\t\t\t\t\t{ __( 'Replace the fetcher in data.ts with your project data source when this screen graduates from scaffold to product UI.', ${quoteTsString(textDomain)} ) }
1063
- \t\t\t\t\t</p>
1064
- \t\t\t\t</div>
1065
- \t\t\t\t<div className="wp-typia-admin-view-screen__actions">
1066
- \t\t\t\t\t{ isLoading ? <Spinner /> : null }
1067
- \t\t\t\t\t<Button
1068
- \t\t\t\t\t\tisBusy={ isLoading }
1069
- \t\t\t\t\t\tonClick={ () => setReloadToken((token) => token + 1) }
1070
- \t\t\t\t\t\tvariant="secondary"
1071
- \t\t\t\t\t>
1072
- \t\t\t\t\t\t{ __( 'Reload', ${quoteTsString(textDomain)} ) }
1073
- \t\t\t\t\t</Button>
1074
- \t\t\t\t</div>
1075
- \t\t\t</header>
1076
- \t\t\t{ error ? (
1077
- \t\t\t\t<Notice isDismissible={ false } status="error">
1078
- \t\t\t\t\t{ error }
1079
- \t\t\t\t</Notice>
1080
- \t\t\t) : null }
1081
- \t\t\t<TypedDataViews<${itemTypeName}> { ...config } />
1082
- \t\t</div>
1083
- \t);
1084
- }
1085
- `;
1086
- }
1087
- export function buildCoreDataAdminViewScreenSource(adminViewSlug, textDomain) {
1088
- const pascalName = toPascalCase(adminViewSlug);
1089
- const camelName = toCamelCase(adminViewSlug);
1090
- const itemTypeName = `${pascalName}AdminViewItem`;
1091
- const dataSetTypeName = `${pascalName}AdminViewDataSet`;
1092
- const componentName = `${pascalName}AdminViewScreen`;
1093
- const dataViewsName = `${camelName}AdminDataViews`;
1094
- const useAdminViewDataName = `use${pascalName}AdminViewData`;
1095
- const title = toTitleCase(adminViewSlug);
1096
- return `import type { DataViewsConfig, DataViewsView } from '@wp-typia/dataviews';
1097
- import { Notice, Spinner } from '@wordpress/components';
1098
- import { useState } from '@wordpress/element';
1099
- import { __ } from '@wordpress/i18n';
1100
- import { DataViews } from '@wordpress/dataviews/wp';
1101
-
1102
- import { ${dataViewsName} } from './config';
1103
- import { ${useAdminViewDataName} } from './data';
1104
- import type { ${dataSetTypeName}, ${itemTypeName} } from './types';
1105
-
1106
- const TypedDataViews = DataViews as unknown as <TItem extends object>(
1107
- \tprops: DataViewsConfig<TItem>,
1108
- ) => ReturnType<typeof DataViews>;
1109
-
1110
- const EMPTY_DATA_SET: ${dataSetTypeName} = {
1111
- \titems: [],
1112
- \tpaginationInfo: {
1113
- \t\ttotalItems: 0,
1114
- \t\ttotalPages: 1,
1115
- \t},
1116
- };
1117
-
1118
- export function ${componentName}() {
1119
- \tconst [view, setView] = useState<DataViewsView<${itemTypeName}>>(
1120
- \t\t${dataViewsName}.defaultView,
1121
- \t);
1122
- \tconst {
1123
- \t\tdataSet = EMPTY_DATA_SET,
1124
- \t\terror,
1125
- \t\tisLoading,
1126
- \t} = ${useAdminViewDataName}(view);
1127
- \tconst config = ${dataViewsName}.createConfig({
1128
- \t\tdata: dataSet.items,
1129
- \t\tisLoading,
1130
- \t\tonChangeView: setView,
1131
- \t\tpaginationInfo: dataSet.paginationInfo,
1132
- \t\tview,
1133
- \t});
1134
-
1135
- \treturn (
1136
- \t\t<div className="wp-typia-admin-view-screen">
1137
- \t\t\t<header className="wp-typia-admin-view-screen__header">
1138
- \t\t\t\t<div>
1139
- \t\t\t\t\t<p className="wp-typia-admin-view-screen__eyebrow">
1140
- \t\t\t\t\t\t{ __( 'DataViews admin screen', ${quoteTsString(textDomain)} ) }
1141
- \t\t\t\t\t</p>
1142
- \t\t\t\t\t<h1>{ __( ${quoteTsString(title)}, ${quoteTsString(textDomain)} ) }</h1>
1143
- \t\t\t\t\t<p>
1144
- \t\t\t\t\t\t{ __( 'This screen reads from the WordPress core-data entity store. Extend data.ts when you need entity-specific field mapping or edit flows.', ${quoteTsString(textDomain)} ) }
1145
- \t\t\t\t\t</p>
1146
- \t\t\t\t</div>
1147
- \t\t\t\t<div className="wp-typia-admin-view-screen__actions">
1148
- \t\t\t\t\t{ isLoading ? <Spinner /> : null }
1149
- \t\t\t\t</div>
1150
- \t\t\t</header>
1151
- \t\t\t{ error ? (
1152
- \t\t\t\t<Notice isDismissible={ false } status="error">
1153
- \t\t\t\t\t{ error }
1154
- \t\t\t\t</Notice>
1155
- \t\t\t) : null }
1156
- \t\t\t<TypedDataViews<${itemTypeName}> { ...config } />
1157
- \t\t</div>
1158
- \t);
1159
- }
1160
- `;
1161
- }
1162
- export function buildAdminViewEntrySource(adminViewSlug, options = {}) {
1163
- const pascalName = toPascalCase(adminViewSlug);
1164
- const componentName = `${pascalName}AdminViewScreen`;
1165
- const rootId = `wp-typia-admin-view-${adminViewSlug}`;
1166
- const dataViewsStyleImport = options.includeDataViewsStyle === false
1167
- ? ''
1168
- : "\nimport '@wordpress/dataviews/build-style/style.css';";
1169
- return `import { createRoot } from '@wordpress/element';
1170
- ${dataViewsStyleImport}
1171
- import { ${componentName} } from './Screen';
1172
- import './style.scss';
1173
-
1174
- const ROOT_ELEMENT_ID = ${quoteTsString(rootId)};
1175
-
1176
- function mountAdminView() {
1177
- \tconst rootElement = document.getElementById(ROOT_ELEMENT_ID);
1178
- \tif (!rootElement) {
1179
- \t\treturn;
1180
- \t}
1181
-
1182
- \tcreateRoot(rootElement).render(<${componentName} />);
1183
- }
1184
-
1185
- if (document.readyState === 'loading') {
1186
- \tdocument.addEventListener('DOMContentLoaded', mountAdminView);
1187
- } else {
1188
- \tmountAdminView();
1189
- }
1190
- `;
1191
- }
1192
- export function buildAdminViewStyleSource() {
1193
- return `.wp-typia-admin-view-screen {
1194
- \tbox-sizing: border-box;
1195
- \tmax-width: 1180px;
1196
- \tpadding: 24px 24px 48px 0;
1197
- }
1198
-
1199
- .wp-typia-admin-view-screen__header {
1200
- \talign-items: flex-start;
1201
- \tdisplay: flex;
1202
- \tgap: 24px;
1203
- \tjustify-content: space-between;
1204
- \tmargin-bottom: 24px;
1205
- }
1206
-
1207
- .wp-typia-admin-view-screen__header h1 {
1208
- \tfont-size: 28px;
1209
- \tline-height: 1.2;
1210
- \tmargin: 0 0 8px;
1211
- }
1212
-
1213
- .wp-typia-admin-view-screen__header p {
1214
- \tmax-width: 680px;
1215
- }
1216
-
1217
- .wp-typia-admin-view-screen__eyebrow {
1218
- \tcolor: #3858e9;
1219
- \tfont-size: 11px;
1220
- \tfont-weight: 600;
1221
- \tletter-spacing: 0.08em;
1222
- \tmargin: 0 0 8px;
1223
- \ttext-transform: uppercase;
1224
- }
1225
-
1226
- .wp-typia-admin-view-screen__actions {
1227
- \talign-items: center;
1228
- \tdisplay: flex;
1229
- \tgap: 12px;
1230
- }
1231
-
1232
- .wp-typia-admin-view-screen__settings-form {
1233
- \tbackground: #fff;
1234
- \tborder: 1px solid #dcdcde;
1235
- \tborder-radius: 2px;
1236
- \tbox-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
1237
- \tdisplay: grid;
1238
- \tgap: 20px;
1239
- \tmax-width: 720px;
1240
- \tpadding: 24px;
1241
- }
1242
-
1243
- .wp-typia-admin-view-screen__field .components-base-control {
1244
- \tmargin-bottom: 0;
1245
- }
1246
- `;
1247
- }
1248
49
  export function buildAdminViewPhpSource(adminViewSlug, workspace) {
1249
- const workspaceBaseName = workspace.packageName.split('/').pop() ?? workspace.packageName;
1250
- const phpSlug = adminViewSlug.replace(/-/g, '_');
1251
- const functionPrefix = `${workspace.workspace.phpPrefix}_${phpSlug}`;
1252
- const menuSlugFunctionName = `${functionPrefix}_admin_view_menu_slug`;
1253
- const renderFunctionName = `${functionPrefix}_render_admin_view`;
1254
- const registerFunctionName = `${functionPrefix}_register_admin_view`;
1255
- const enqueueFunctionName = `${functionPrefix}_enqueue_admin_view`;
1256
- const hookGlobalName = `${functionPrefix}_admin_view_hook`;
1257
- const rootId = `wp-typia-admin-view-${adminViewSlug}`;
1258
- const title = toTitleCase(adminViewSlug);
1259
- return `<?php
1260
- if ( ! defined( 'ABSPATH' ) ) {
1261
- \treturn;
1262
- }
1263
-
1264
- if ( ! function_exists( '${menuSlugFunctionName}' ) ) {
1265
- \tfunction ${menuSlugFunctionName}() : string {
1266
- \t\treturn '${workspaceBaseName}-${adminViewSlug}';
1267
- \t}
1268
- }
1269
-
1270
- if ( ! function_exists( '${renderFunctionName}' ) ) {
1271
- \tfunction ${renderFunctionName}() : void {
1272
- \t\t?>
1273
- \t\t<div class="wrap">
1274
- \t\t\t<div id="${rootId}"></div>
1275
- \t\t</div>
1276
- \t\t<?php
1277
- \t}
1278
- }
1279
-
1280
- if ( ! function_exists( '${registerFunctionName}' ) ) {
1281
- \tfunction ${registerFunctionName}() : void {
1282
- \t\t$GLOBALS['${hookGlobalName}'] = add_submenu_page(
1283
- \t\t\t'tools.php',
1284
- \t\t\t__( ${quotePhpString(title)}, ${quotePhpString(workspace.workspace.textDomain)} ),
1285
- \t\t\t__( ${quotePhpString(title)}, ${quotePhpString(workspace.workspace.textDomain)} ),
1286
- \t\t\t'edit_posts',
1287
- \t\t\t${menuSlugFunctionName}(),
1288
- \t\t\t'${renderFunctionName}'
1289
- \t\t);
1290
- \t}
1291
- }
1292
-
1293
- if ( ! function_exists( '${enqueueFunctionName}' ) ) {
1294
- \tfunction ${enqueueFunctionName}( string $hook_suffix ) : void {
1295
- \t\t$page_hook = isset( $GLOBALS['${hookGlobalName}'] ) && is_string( $GLOBALS['${hookGlobalName}'] )
1296
- \t\t\t? $GLOBALS['${hookGlobalName}']
1297
- \t\t\t: '';
1298
-
1299
- \t\tif ( $page_hook !== $hook_suffix ) {
1300
- \t\t\treturn;
1301
- \t\t}
1302
-
1303
- \t\t$plugin_file = dirname( __DIR__, 2 ) . '/${workspaceBaseName}.php';
1304
- \t\t$script_path = dirname( __DIR__, 2 ) . '/${ADMIN_VIEWS_SCRIPT}';
1305
- \t\t$asset_path = dirname( __DIR__, 2 ) . '/${ADMIN_VIEWS_ASSET}';
1306
- \t\t$style_path = dirname( __DIR__, 2 ) . '/${ADMIN_VIEWS_STYLE}';
1307
- \t\t$style_rtl_path = dirname( __DIR__, 2 ) . '/${ADMIN_VIEWS_STYLE_RTL}';
1308
-
1309
- \t\tif ( ! file_exists( $script_path ) || ! file_exists( $asset_path ) ) {
1310
- \t\t\treturn;
1311
- \t\t}
1312
-
1313
- \t\t$asset = require $asset_path;
1314
- \t\tif ( ! is_array( $asset ) ) {
1315
- \t\t\t$asset = array();
1316
- \t\t}
1317
-
1318
- \t\t$dependencies = isset( $asset['dependencies'] ) && is_array( $asset['dependencies'] )
1319
- \t\t\t? $asset['dependencies']
1320
- \t\t\t: array();
1321
-
1322
- \t\twp_enqueue_script(
1323
- \t\t\t'${workspaceBaseName}-${adminViewSlug}-admin-view',
1324
- \t\t\tplugins_url( '${ADMIN_VIEWS_SCRIPT}', $plugin_file ),
1325
- \t\t\t$dependencies,
1326
- \t\t\tisset( $asset['version'] ) ? $asset['version'] : filemtime( $script_path ),
1327
- \t\t\ttrue
1328
- \t\t);
1329
-
1330
- \t\tif ( file_exists( $style_path ) ) {
1331
- \t\t\twp_enqueue_style(
1332
- \t\t\t\t'${workspaceBaseName}-${adminViewSlug}-admin-view',
1333
- \t\t\t\tplugins_url( '${ADMIN_VIEWS_STYLE}', $plugin_file ),
1334
- \t\t\t\tarray( 'wp-components' ),
1335
- \t\t\t\tisset( $asset['version'] ) ? $asset['version'] : filemtime( $style_path )
1336
- \t\t\t);
1337
- \t\t\tif ( file_exists( $style_rtl_path ) ) {
1338
- \t\t\t\twp_style_add_data( '${workspaceBaseName}-${adminViewSlug}-admin-view', 'rtl', 'replace' );
1339
- \t\t\t}
1340
- \t\t}
1341
- \t}
1342
- }
1343
-
1344
- add_action( 'admin_menu', '${registerFunctionName}' );
1345
- add_action( 'admin_enqueue_scripts', '${enqueueFunctionName}' );
1346
- `;
50
+ return buildSharedAdminViewPhpSource(adminViewSlug, workspace);
1347
51
  }