@wp-typia/project-tools 0.16.10 → 0.16.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/dist/runtime/built-in-block-artifact-documents.d.ts +3 -0
- package/dist/runtime/built-in-block-artifact-documents.js +2 -0
- package/dist/runtime/built-in-block-artifact-types.d.ts +51 -0
- package/dist/runtime/built-in-block-artifact-types.js +304 -0
- package/dist/runtime/built-in-block-artifacts.js +4 -803
- package/dist/runtime/built-in-block-attribute-emitters.d.ts +71 -0
- package/dist/runtime/built-in-block-attribute-emitters.js +176 -0
- package/dist/runtime/built-in-block-attribute-specs.d.ts +38 -0
- package/dist/runtime/built-in-block-attribute-specs.js +358 -0
- package/dist/runtime/built-in-block-code-templates/basic.d.ts +4 -0
- package/dist/runtime/built-in-block-code-templates/basic.js +249 -0
- package/dist/runtime/built-in-block-code-templates/compound-child.d.ts +4 -0
- package/dist/runtime/built-in-block-code-templates/compound-child.js +138 -0
- package/dist/runtime/built-in-block-code-templates/compound-parent.d.ts +6 -0
- package/dist/runtime/built-in-block-code-templates/compound-parent.js +227 -0
- package/dist/runtime/built-in-block-code-templates/compound-persistence.d.ts +4 -0
- package/dist/runtime/built-in-block-code-templates/compound-persistence.js +478 -0
- package/dist/runtime/built-in-block-code-templates/compound.d.ts +3 -0
- package/dist/runtime/built-in-block-code-templates/compound.js +3 -0
- package/dist/runtime/built-in-block-code-templates/interactivity.d.ts +5 -0
- package/dist/runtime/built-in-block-code-templates/interactivity.js +547 -0
- package/dist/runtime/built-in-block-code-templates/persistence.d.ts +5 -0
- package/dist/runtime/built-in-block-code-templates/persistence.js +550 -0
- package/dist/runtime/built-in-block-code-templates/shared.d.ts +16 -0
- package/dist/runtime/built-in-block-code-templates/shared.js +53 -0
- package/dist/runtime/built-in-block-code-templates.d.ts +5 -32
- package/dist/runtime/built-in-block-code-templates.js +5 -2230
- package/dist/runtime/cli-add-block-config.d.ts +6 -0
- package/dist/runtime/cli-add-block-config.js +143 -0
- package/dist/runtime/cli-add-block-legacy-validator.d.ts +4 -0
- package/dist/runtime/cli-add-block-legacy-validator.js +168 -0
- package/dist/runtime/cli-add-block.js +3 -301
- package/dist/runtime/cli-add-workspace-assets.d.ts +38 -0
- package/dist/runtime/cli-add-workspace-assets.js +399 -0
- package/dist/runtime/cli-add-workspace.d.ts +2 -38
- package/dist/runtime/cli-add-workspace.js +5 -396
- package/dist/runtime/cli-doctor-environment.d.ts +12 -0
- package/dist/runtime/cli-doctor-environment.js +123 -0
- package/dist/runtime/cli-doctor-workspace.d.ts +14 -0
- package/dist/runtime/cli-doctor-workspace.js +296 -0
- package/dist/runtime/cli-doctor.d.ts +4 -2
- package/dist/runtime/cli-doctor.js +10 -405
- package/dist/runtime/cli-help.js +1 -1
- package/dist/runtime/cli-scaffold.js +1 -1
- package/dist/runtime/migration-command-surface.d.ts +67 -0
- package/dist/runtime/migration-command-surface.js +189 -0
- package/dist/runtime/migration-diff-rename.d.ts +13 -0
- package/dist/runtime/migration-diff-rename.js +192 -0
- package/dist/runtime/migration-diff-transform.d.ts +14 -0
- package/dist/runtime/migration-diff-transform.js +105 -0
- package/dist/runtime/migration-diff.js +12 -297
- package/dist/runtime/migration-generated-artifacts.d.ts +3 -0
- package/dist/runtime/migration-generated-artifacts.js +41 -0
- package/dist/runtime/migration-maintenance.d.ts +51 -0
- package/dist/runtime/migration-maintenance.js +380 -0
- package/dist/runtime/migration-planning.d.ts +23 -0
- package/dist/runtime/migration-planning.js +131 -0
- package/dist/runtime/migration-project-config-source.d.ts +6 -0
- package/dist/runtime/migration-project-config-source.js +424 -0
- package/dist/runtime/migration-project-layout-discovery.d.ts +61 -0
- package/dist/runtime/migration-project-layout-discovery.js +337 -0
- package/dist/runtime/migration-project-layout-paths.d.ts +135 -0
- package/dist/runtime/migration-project-layout-paths.js +288 -0
- package/dist/runtime/migration-project-layout.d.ts +3 -0
- package/dist/runtime/migration-project-layout.js +2 -0
- package/dist/runtime/migration-project-workspace.d.ts +47 -0
- package/dist/runtime/migration-project-workspace.js +212 -0
- package/dist/runtime/migration-project.d.ts +4 -94
- package/dist/runtime/migration-project.js +3 -1101
- package/dist/runtime/migration-render-diff-rule.d.ts +5 -0
- package/dist/runtime/migration-render-diff-rule.js +120 -0
- package/dist/runtime/migration-render-execution.d.ts +3 -0
- package/dist/runtime/migration-render-execution.js +428 -0
- package/dist/runtime/migration-render-generated.d.ts +27 -0
- package/dist/runtime/migration-render-generated.js +230 -0
- package/dist/runtime/migration-render-support.d.ts +3 -0
- package/dist/runtime/migration-render-support.js +16 -0
- package/dist/runtime/migration-render.d.ts +3 -33
- package/dist/runtime/migration-render.js +3 -789
- package/dist/runtime/migration-ui-capability.js +1 -1
- package/dist/runtime/migrations.d.ts +24 -118
- package/dist/runtime/migrations.js +12 -700
- package/dist/runtime/scaffold-bootstrap.d.ts +45 -0
- package/dist/runtime/scaffold-bootstrap.js +185 -0
- package/dist/runtime/scaffold-package-manager-files.d.ts +35 -0
- package/dist/runtime/scaffold-package-manager-files.js +79 -0
- package/dist/runtime/scaffold.d.ts +1 -12
- package/dist/runtime/scaffold.js +10 -393
- package/dist/runtime/template-source-contracts.d.ts +81 -0
- package/dist/runtime/template-source-contracts.js +1 -0
- package/dist/runtime/template-source-external.d.ts +21 -0
- package/dist/runtime/template-source-external.js +184 -0
- package/dist/runtime/template-source-locators.d.ts +4 -0
- package/dist/runtime/template-source-locators.js +72 -0
- package/dist/runtime/template-source-normalization.d.ts +7 -0
- package/dist/runtime/template-source-normalization.js +53 -0
- package/dist/runtime/template-source-remote.d.ts +23 -0
- package/dist/runtime/template-source-remote.js +336 -0
- package/dist/runtime/template-source-seeds.d.ts +12 -0
- package/dist/runtime/template-source-seeds.js +243 -0
- package/dist/runtime/template-source.d.ts +4 -86
- package/dist/runtime/template-source.js +9 -828
- package/package.json +5 -5
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { MigrationBlockConfig } from "./migration-types.js";
|
|
2
|
+
import type { ScaffoldTemplateVariables } from "./scaffold.js";
|
|
3
|
+
import { type AddBlockTemplateId } from "./cli-add-shared.js";
|
|
4
|
+
export declare function buildServerTemplateRoot(persistencePolicy: string | undefined): string;
|
|
5
|
+
export declare function buildConfigEntries(templateId: AddBlockTemplateId, variables: ScaffoldTemplateVariables): string[];
|
|
6
|
+
export declare function buildMigrationBlocks(templateId: AddBlockTemplateId, variables: ScaffoldTemplateVariables): MigrationBlockConfig[];
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { SHARED_WORKSPACE_TEMPLATE_ROOT, } from "./template-registry.js";
|
|
3
|
+
import { quoteTsString, } from "./cli-add-shared.js";
|
|
4
|
+
export function buildServerTemplateRoot(persistencePolicy) {
|
|
5
|
+
return path.join(SHARED_WORKSPACE_TEMPLATE_ROOT, persistencePolicy === "public" ? "persistence-public" : "persistence-auth");
|
|
6
|
+
}
|
|
7
|
+
function buildSingleBlockConfigEntry(variables) {
|
|
8
|
+
return [
|
|
9
|
+
"\t{",
|
|
10
|
+
`\t\tslug: ${quoteTsString(variables.slugKebabCase)},`,
|
|
11
|
+
`\t\tattributeTypeName: ${quoteTsString(`${variables.pascalCase}Attributes`)},`,
|
|
12
|
+
`\t\ttypesFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}/types.ts`)},`,
|
|
13
|
+
"\t},",
|
|
14
|
+
].join("\n");
|
|
15
|
+
}
|
|
16
|
+
function buildPersistenceBlockConfigEntry(variables) {
|
|
17
|
+
return [
|
|
18
|
+
"\t{",
|
|
19
|
+
`\t\tapiTypesFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}/api-types.ts`)},`,
|
|
20
|
+
`\t\tattributeTypeName: ${quoteTsString(`${variables.pascalCase}Attributes`)},`,
|
|
21
|
+
"\t\trestManifest: defineEndpointManifest( {",
|
|
22
|
+
"\t\t\tcontracts: {",
|
|
23
|
+
"\t\t\t\t'bootstrap-query': {",
|
|
24
|
+
`\t\t\t\t\tsourceTypeName: ${quoteTsString(`${variables.pascalCase}BootstrapQuery`)},`,
|
|
25
|
+
"\t\t\t\t},",
|
|
26
|
+
"\t\t\t\t'bootstrap-response': {",
|
|
27
|
+
`\t\t\t\t\tsourceTypeName: ${quoteTsString(`${variables.pascalCase}BootstrapResponse`)},`,
|
|
28
|
+
"\t\t\t\t},",
|
|
29
|
+
"\t\t\t\t'state-query': {",
|
|
30
|
+
`\t\t\t\t\tsourceTypeName: ${quoteTsString(`${variables.pascalCase}StateQuery`)},`,
|
|
31
|
+
"\t\t\t\t},",
|
|
32
|
+
"\t\t\t\t'state-response': {",
|
|
33
|
+
`\t\t\t\t\tsourceTypeName: ${quoteTsString(`${variables.pascalCase}StateResponse`)},`,
|
|
34
|
+
"\t\t\t\t},",
|
|
35
|
+
"\t\t\t\t'write-state-request': {",
|
|
36
|
+
`\t\t\t\t\tsourceTypeName: ${quoteTsString(`${variables.pascalCase}WriteStateRequest`)},`,
|
|
37
|
+
"\t\t\t\t},",
|
|
38
|
+
"\t\t\t},",
|
|
39
|
+
"\t\t\tendpoints: [",
|
|
40
|
+
"\t\t\t\t{",
|
|
41
|
+
"\t\t\t\t\tauth: 'public',",
|
|
42
|
+
"\t\t\t\t\tmethod: 'GET',",
|
|
43
|
+
`\t\t\t\t\toperationId: ${quoteTsString(`get${variables.pascalCase}State`)},`,
|
|
44
|
+
`\t\t\t\t\tpath: ${quoteTsString(`/${variables.namespace}/v1/${variables.slugKebabCase}/state`)},`,
|
|
45
|
+
"\t\t\t\t\tqueryContract: 'state-query',",
|
|
46
|
+
"\t\t\t\t\tresponseContract: 'state-response',",
|
|
47
|
+
`\t\t\t\t\tsummary: 'Read the current persisted state.',`,
|
|
48
|
+
`\t\t\t\t\ttags: [ ${quoteTsString(variables.title)} ],`,
|
|
49
|
+
"\t\t\t\t},",
|
|
50
|
+
"\t\t\t\t{",
|
|
51
|
+
`\t\t\t\t\tauth: ${quoteTsString(variables.restWriteAuthIntent)},`,
|
|
52
|
+
"\t\t\t\t\tbodyContract: 'write-state-request',",
|
|
53
|
+
"\t\t\t\t\tmethod: 'POST',",
|
|
54
|
+
`\t\t\t\t\toperationId: ${quoteTsString(`write${variables.pascalCase}State`)},`,
|
|
55
|
+
`\t\t\t\t\tpath: ${quoteTsString(`/${variables.namespace}/v1/${variables.slugKebabCase}/state`)},`,
|
|
56
|
+
"\t\t\t\t\tresponseContract: 'state-response',",
|
|
57
|
+
`\t\t\t\t\tsummary: 'Write the current persisted state.',`,
|
|
58
|
+
`\t\t\t\t\ttags: [ ${quoteTsString(variables.title)} ],`,
|
|
59
|
+
"\t\t\t\t\twordpressAuth: {",
|
|
60
|
+
`\t\t\t\t\t\tmechanism: ${quoteTsString(variables.restWriteAuthMechanism)},`,
|
|
61
|
+
"\t\t\t\t\t},",
|
|
62
|
+
"\t\t\t\t},",
|
|
63
|
+
"\t\t\t\t{",
|
|
64
|
+
"\t\t\t\t\tauth: 'public',",
|
|
65
|
+
"\t\t\t\t\tmethod: 'GET',",
|
|
66
|
+
`\t\t\t\t\toperationId: ${quoteTsString(`get${variables.pascalCase}Bootstrap`)},`,
|
|
67
|
+
`\t\t\t\t\tpath: ${quoteTsString(`/${variables.namespace}/v1/${variables.slugKebabCase}/bootstrap`)},`,
|
|
68
|
+
"\t\t\t\t\tqueryContract: 'bootstrap-query',",
|
|
69
|
+
"\t\t\t\t\tresponseContract: 'bootstrap-response',",
|
|
70
|
+
`\t\t\t\t\tsummary: 'Read fresh session bootstrap state for the current viewer.',`,
|
|
71
|
+
`\t\t\t\t\ttags: [ ${quoteTsString(variables.title)} ],`,
|
|
72
|
+
"\t\t\t\t},",
|
|
73
|
+
"\t\t\t],",
|
|
74
|
+
"\t\t\tinfo: {",
|
|
75
|
+
`\t\t\t\ttitle: ${quoteTsString(`${variables.title} REST API`)},`,
|
|
76
|
+
"\t\t\t\tversion: '1.0.0',",
|
|
77
|
+
"\t\t\t},",
|
|
78
|
+
"\t\t} ),",
|
|
79
|
+
`\t\topenApiFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}/api.openapi.json`)},`,
|
|
80
|
+
`\t\tslug: ${quoteTsString(variables.slugKebabCase)},`,
|
|
81
|
+
`\t\ttypesFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}/types.ts`)},`,
|
|
82
|
+
"\t},",
|
|
83
|
+
].join("\n");
|
|
84
|
+
}
|
|
85
|
+
function buildCompoundChildConfigEntry(variables) {
|
|
86
|
+
return [
|
|
87
|
+
"\t{",
|
|
88
|
+
`\t\tslug: ${quoteTsString(`${variables.slugKebabCase}-item`)},`,
|
|
89
|
+
`\t\tattributeTypeName: ${quoteTsString(`${variables.pascalCase}ItemAttributes`)},`,
|
|
90
|
+
`\t\ttypesFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}-item/types.ts`)},`,
|
|
91
|
+
"\t},",
|
|
92
|
+
].join("\n");
|
|
93
|
+
}
|
|
94
|
+
export function buildConfigEntries(templateId, variables) {
|
|
95
|
+
if (templateId === "basic" || templateId === "interactivity") {
|
|
96
|
+
return [buildSingleBlockConfigEntry(variables)];
|
|
97
|
+
}
|
|
98
|
+
if (templateId === "persistence") {
|
|
99
|
+
return [buildPersistenceBlockConfigEntry(variables)];
|
|
100
|
+
}
|
|
101
|
+
if (variables.compoundPersistenceEnabled === "true") {
|
|
102
|
+
return [
|
|
103
|
+
buildPersistenceBlockConfigEntry(variables),
|
|
104
|
+
buildCompoundChildConfigEntry(variables),
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
return [
|
|
108
|
+
buildSingleBlockConfigEntry(variables),
|
|
109
|
+
buildCompoundChildConfigEntry(variables),
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
export function buildMigrationBlocks(templateId, variables) {
|
|
113
|
+
if (templateId === "compound") {
|
|
114
|
+
return [
|
|
115
|
+
{
|
|
116
|
+
blockJsonFile: `src/blocks/${variables.slugKebabCase}/block.json`,
|
|
117
|
+
blockName: `${variables.namespace}/${variables.slugKebabCase}`,
|
|
118
|
+
key: variables.slugKebabCase,
|
|
119
|
+
manifestFile: `src/blocks/${variables.slugKebabCase}/typia.manifest.json`,
|
|
120
|
+
saveFile: `src/blocks/${variables.slugKebabCase}/save.tsx`,
|
|
121
|
+
typesFile: `src/blocks/${variables.slugKebabCase}/types.ts`,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
blockJsonFile: `src/blocks/${variables.slugKebabCase}-item/block.json`,
|
|
125
|
+
blockName: `${variables.namespace}/${variables.slugKebabCase}-item`,
|
|
126
|
+
key: `${variables.slugKebabCase}-item`,
|
|
127
|
+
manifestFile: `src/blocks/${variables.slugKebabCase}-item/typia.manifest.json`,
|
|
128
|
+
saveFile: `src/blocks/${variables.slugKebabCase}-item/save.tsx`,
|
|
129
|
+
typesFile: `src/blocks/${variables.slugKebabCase}-item/types.ts`,
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
}
|
|
133
|
+
return [
|
|
134
|
+
{
|
|
135
|
+
blockJsonFile: `src/blocks/${variables.slugKebabCase}/block.json`,
|
|
136
|
+
blockName: `${variables.namespace}/${variables.slugKebabCase}`,
|
|
137
|
+
key: variables.slugKebabCase,
|
|
138
|
+
manifestFile: `src/blocks/${variables.slugKebabCase}/typia.manifest.json`,
|
|
139
|
+
saveFile: `src/blocks/${variables.slugKebabCase}/save.tsx`,
|
|
140
|
+
typesFile: `src/blocks/${variables.slugKebabCase}/types.ts`,
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const COMPOUND_SHARED_SUPPORT_FILES: readonly ["hooks.ts", "validator-toolkit.ts"];
|
|
2
|
+
export declare function ensureBlockConfigCanAddRestManifests(source: string): string;
|
|
3
|
+
export declare function collectLegacyCompoundValidatorPaths(projectDir: string): Promise<string[]>;
|
|
4
|
+
export declare function ensureCompoundWorkspaceSupportFiles(projectDir: string, tempProjectDir: string, legacyValidatorPaths: readonly string[]): Promise<void>;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { promises as fsp } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { readOptionalFile } from "./cli-add-shared.js";
|
|
5
|
+
export const COMPOUND_SHARED_SUPPORT_FILES = [
|
|
6
|
+
"hooks.ts",
|
|
7
|
+
"validator-toolkit.ts",
|
|
8
|
+
];
|
|
9
|
+
const LEGACY_ASSERT_PATTERN = /assert:\s*typia\.createAssert</u;
|
|
10
|
+
const LEGACY_MANIFEST_PATTERN = /\r?\n[ \t]*manifest:\s*currentManifest,/u;
|
|
11
|
+
const LEGACY_VALIDATOR_MANIFEST_IMPORT_PATTERN = /^[\uFEFF \t]*import\s+currentManifest\s+from\s*["']\.\/typia\.manifest\.json["'];?$/u;
|
|
12
|
+
const LEGACY_TOOLKIT_CALL_PATTERN = /createTemplateValidatorToolkit<\s*(?<typeName>[A-Za-z0-9_]+)\s*>\s*\(\s*\{/u;
|
|
13
|
+
const LEGACY_VALIDATOR_TOOLKIT_IMPORT_PATTERN = /from\s*["']\.\.\/\.\.\/validator-toolkit["']/u;
|
|
14
|
+
const TYPIA_IMPORT_PATTERN = /^[\uFEFF \t]*import\s+typia\s+from\s*["']typia["'];?/mu;
|
|
15
|
+
const COMPATIBLE_COMPOUND_TOOLKIT_PATTERNS = [
|
|
16
|
+
/interface\s+TemplateValidatorFunctions\s*<\s*T\s+extends\s+object\s*>\s*\{/u,
|
|
17
|
+
/\bassert\s*:\s*ScaffoldValidatorToolkitOptions\s*<\s*T\s*>\s*\[\s*["']assert["']\s*\]/u,
|
|
18
|
+
/\bclone\s*:\s*ScaffoldValidatorToolkitOptions\s*<\s*T\s*>\s*\[\s*["']clone["']\s*\]/u,
|
|
19
|
+
/\bis\s*:\s*ScaffoldValidatorToolkitOptions\s*<\s*T\s*>\s*\[\s*["']is["']\s*\]/u,
|
|
20
|
+
/\bprune\s*:\s*ScaffoldValidatorToolkitOptions\s*<\s*T\s*>\s*\[\s*["']prune["']\s*\]/u,
|
|
21
|
+
/\brandom\s*:\s*ScaffoldValidatorToolkitOptions\s*<\s*T\s*>\s*\[\s*["']random["']\s*\]/u,
|
|
22
|
+
/\bvalidate\s*:\s*ScaffoldValidatorToolkitOptions\s*<\s*T\s*>\s*\[\s*["']validate["']\s*\]/u,
|
|
23
|
+
/createTemplateValidatorToolkit\s*<\s*T\s+extends\s+object\s*>\s*\(\s*\{/u,
|
|
24
|
+
];
|
|
25
|
+
const REST_MANIFEST_IMPORT_PATTERN = /import\s*\{[^}]*\bdefineEndpointManifest\b[^}]*\}\s*from\s*["']@wp-typia\/block-runtime\/metadata-core["'];?/m;
|
|
26
|
+
export function ensureBlockConfigCanAddRestManifests(source) {
|
|
27
|
+
const importLine = "import { defineEndpointManifest } from '@wp-typia/block-runtime/metadata-core';";
|
|
28
|
+
if (REST_MANIFEST_IMPORT_PATTERN.test(source)) {
|
|
29
|
+
return source;
|
|
30
|
+
}
|
|
31
|
+
return `${importLine}\n\n${source}`;
|
|
32
|
+
}
|
|
33
|
+
function shouldRefreshCompoundValidatorToolkit(source) {
|
|
34
|
+
return (source === null ||
|
|
35
|
+
!COMPATIBLE_COMPOUND_TOOLKIT_PATTERNS.every((pattern) => pattern.test(source)));
|
|
36
|
+
}
|
|
37
|
+
function isLegacyCompoundValidatorSource(source) {
|
|
38
|
+
return (typeof source === "string" &&
|
|
39
|
+
LEGACY_VALIDATOR_TOOLKIT_IMPORT_PATTERN.test(source) &&
|
|
40
|
+
!LEGACY_ASSERT_PATTERN.test(source));
|
|
41
|
+
}
|
|
42
|
+
function hasTypiaImport(source) {
|
|
43
|
+
return TYPIA_IMPORT_PATTERN.test(source.replace(/\/\*[\s\S]*?\*\//gu, ""));
|
|
44
|
+
}
|
|
45
|
+
function replaceFirstNonCommentLine(source, pattern, replacement) {
|
|
46
|
+
const lineEnding = source.includes("\r\n") ? "\r\n" : "\n";
|
|
47
|
+
const lines = source.split(/\r?\n/);
|
|
48
|
+
let inBlockComment = false;
|
|
49
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
50
|
+
const line = lines[index] ?? "";
|
|
51
|
+
const trimmed = line.trimStart();
|
|
52
|
+
if (inBlockComment) {
|
|
53
|
+
if (trimmed.includes("*/")) {
|
|
54
|
+
inBlockComment = false;
|
|
55
|
+
}
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (trimmed.startsWith("//")) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (trimmed.startsWith("/*")) {
|
|
62
|
+
if (!trimmed.includes("*/")) {
|
|
63
|
+
inBlockComment = true;
|
|
64
|
+
}
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (!pattern.test(line)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
lines[index] = replacement;
|
|
71
|
+
return lines.join(lineEnding);
|
|
72
|
+
}
|
|
73
|
+
return source;
|
|
74
|
+
}
|
|
75
|
+
function upgradeLegacyCompoundValidatorSource(source) {
|
|
76
|
+
const typeNameMatch = source.match(LEGACY_TOOLKIT_CALL_PATTERN);
|
|
77
|
+
const typeName = typeNameMatch?.groups?.typeName;
|
|
78
|
+
if (!typeName) {
|
|
79
|
+
throw new Error("Unable to upgrade a legacy compound validator without a generated type import.");
|
|
80
|
+
}
|
|
81
|
+
let nextSource = source;
|
|
82
|
+
if (!hasTypiaImport(nextSource)) {
|
|
83
|
+
nextSource = `import typia from 'typia';\n${nextSource}`;
|
|
84
|
+
}
|
|
85
|
+
nextSource = replaceFirstNonCommentLine(nextSource, LEGACY_VALIDATOR_MANIFEST_IMPORT_PATTERN, "import currentManifest from './manifest-defaults-document';");
|
|
86
|
+
nextSource = nextSource.replace(LEGACY_TOOLKIT_CALL_PATTERN, [
|
|
87
|
+
`createTemplateValidatorToolkit< ${typeName} >( {`,
|
|
88
|
+
`\tassert: typia.createAssert< ${typeName} >(),`,
|
|
89
|
+
`\tclone: typia.misc.createClone< ${typeName} >() as (`,
|
|
90
|
+
`\t\tvalue: ${typeName},`,
|
|
91
|
+
`\t) => ${typeName},`,
|
|
92
|
+
`\tis: typia.createIs< ${typeName} >(),`,
|
|
93
|
+
].join("\n") + "\n");
|
|
94
|
+
const replacedManifest = nextSource.replace(LEGACY_MANIFEST_PATTERN, [
|
|
95
|
+
"",
|
|
96
|
+
"\tmanifest: currentManifest,",
|
|
97
|
+
`\tprune: typia.misc.createPrune< ${typeName} >(),`,
|
|
98
|
+
`\trandom: typia.createRandom< ${typeName} >() as (`,
|
|
99
|
+
"\t\t...args: unknown[]",
|
|
100
|
+
`\t) => ${typeName},`,
|
|
101
|
+
`\tvalidate: typia.createValidate< ${typeName} >(),`,
|
|
102
|
+
].join("\n"));
|
|
103
|
+
if (replacedManifest === nextSource) {
|
|
104
|
+
throw new Error("Unable to upgrade legacy compound validator: manifest anchor not found.");
|
|
105
|
+
}
|
|
106
|
+
return replacedManifest;
|
|
107
|
+
}
|
|
108
|
+
function renderLegacyManifestDefaultsWrapperSource() {
|
|
109
|
+
return [
|
|
110
|
+
"import rawCurrentManifest from './typia.manifest.json';",
|
|
111
|
+
"import { defineManifestDefaultsDocument } from '@wp-typia/block-runtime/defaults';",
|
|
112
|
+
"",
|
|
113
|
+
"const currentManifest = defineManifestDefaultsDocument( rawCurrentManifest );",
|
|
114
|
+
"",
|
|
115
|
+
"export default currentManifest;",
|
|
116
|
+
"",
|
|
117
|
+
].join("\n");
|
|
118
|
+
}
|
|
119
|
+
async function ensureLegacyCompoundValidatorManifestDefaultsWrapper(validatorPath) {
|
|
120
|
+
const validatorDir = path.dirname(validatorPath);
|
|
121
|
+
const wrapperPath = path.join(validatorDir, "manifest-defaults-document.ts");
|
|
122
|
+
const manifestPath = path.join(validatorDir, "typia.manifest.json");
|
|
123
|
+
if (fs.existsSync(wrapperPath) || !fs.existsSync(manifestPath)) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
await fsp.writeFile(wrapperPath, renderLegacyManifestDefaultsWrapperSource(), "utf8");
|
|
127
|
+
}
|
|
128
|
+
export async function collectLegacyCompoundValidatorPaths(projectDir) {
|
|
129
|
+
const blocksDir = path.join(projectDir, "src", "blocks");
|
|
130
|
+
if (!fs.existsSync(blocksDir)) {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
const blockEntries = await fsp.readdir(blocksDir, { withFileTypes: true });
|
|
134
|
+
const validatorPaths = await Promise.all(blockEntries
|
|
135
|
+
.filter((entry) => entry.isDirectory())
|
|
136
|
+
.map(async (entry) => {
|
|
137
|
+
const validatorPath = path.join(blocksDir, entry.name, "validators.ts");
|
|
138
|
+
const validatorSource = await readOptionalFile(validatorPath);
|
|
139
|
+
return isLegacyCompoundValidatorSource(validatorSource)
|
|
140
|
+
? validatorPath
|
|
141
|
+
: null;
|
|
142
|
+
}));
|
|
143
|
+
return validatorPaths.filter((validatorPath) => validatorPath !== null);
|
|
144
|
+
}
|
|
145
|
+
export async function ensureCompoundWorkspaceSupportFiles(projectDir, tempProjectDir, legacyValidatorPaths) {
|
|
146
|
+
for (const fileName of COMPOUND_SHARED_SUPPORT_FILES) {
|
|
147
|
+
const sourcePath = path.join(tempProjectDir, "src", fileName);
|
|
148
|
+
if (!fs.existsSync(sourcePath)) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const targetPath = path.join(projectDir, "src", fileName);
|
|
152
|
+
const currentSource = await readOptionalFile(targetPath);
|
|
153
|
+
if (fileName === "validator-toolkit.ts"
|
|
154
|
+
? shouldRefreshCompoundValidatorToolkit(currentSource)
|
|
155
|
+
: currentSource === null) {
|
|
156
|
+
await fsp.mkdir(path.dirname(targetPath), { recursive: true });
|
|
157
|
+
await fsp.copyFile(sourcePath, targetPath);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
for (const validatorPath of legacyValidatorPaths) {
|
|
161
|
+
const currentSource = await readOptionalFile(validatorPath);
|
|
162
|
+
if (!isLegacyCompoundValidatorSource(currentSource)) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
await ensureLegacyCompoundValidatorManifestDefaultsWrapper(validatorPath);
|
|
166
|
+
await fsp.writeFile(validatorPath, upgradeLegacyCompoundValidatorSource(currentSource), "utf8");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -8,155 +8,15 @@ import { syncPersistenceRestArtifacts, } from "./persistence-rest-artifacts.js";
|
|
|
8
8
|
import { snapshotProjectVersion } from "./migrations.js";
|
|
9
9
|
import { getDefaultAnswers, scaffoldProject } from "./scaffold.js";
|
|
10
10
|
import { copyInterpolatedDirectory, listInterpolatedDirectoryOutputs, } from "./template-render.js";
|
|
11
|
-
import { SHARED_WORKSPACE_TEMPLATE_ROOT, } from "./template-registry.js";
|
|
12
11
|
import { appendWorkspaceInventoryEntries, } from "./workspace-inventory.js";
|
|
13
12
|
import { resolveWorkspaceProject, } from "./workspace-project.js";
|
|
14
|
-
import { ADD_BLOCK_TEMPLATE_IDS, buildWorkspacePhpPrefix, isAddBlockTemplateId, normalizeBlockSlug, patchFile,
|
|
13
|
+
import { ADD_BLOCK_TEMPLATE_IDS, buildWorkspacePhpPrefix, isAddBlockTemplateId, normalizeBlockSlug, patchFile, readOptionalFile, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
|
|
14
|
+
import { buildConfigEntries, buildMigrationBlocks, buildServerTemplateRoot, } from "./cli-add-block-config.js";
|
|
15
|
+
import { COMPOUND_SHARED_SUPPORT_FILES, collectLegacyCompoundValidatorPaths, ensureBlockConfigCanAddRestManifests, ensureCompoundWorkspaceSupportFiles, } from "./cli-add-block-legacy-validator.js";
|
|
15
16
|
import { parseTemplateLocator, resolveTemplateSeed, } from "./template-source.js";
|
|
16
17
|
import { resolveExternalTemplateLayers, } from "./template-layers.js";
|
|
17
18
|
import { resolveOptionalInteractiveExternalLayerId, } from "./external-layer-selection.js";
|
|
18
19
|
const COLLECTION_IMPORT_LINE = "import '../../collection';";
|
|
19
|
-
const REST_MANIFEST_IMPORT_PATTERN = /import\s*\{[^}]*\bdefineEndpointManifest\b[^}]*\}\s*from\s*["']@wp-typia\/block-runtime\/metadata-core["'];?/m;
|
|
20
|
-
function buildServerTemplateRoot(persistencePolicy) {
|
|
21
|
-
return path.join(SHARED_WORKSPACE_TEMPLATE_ROOT, persistencePolicy === "public" ? "persistence-public" : "persistence-auth");
|
|
22
|
-
}
|
|
23
|
-
function buildSingleBlockConfigEntry(variables) {
|
|
24
|
-
return [
|
|
25
|
-
"\t{",
|
|
26
|
-
`\t\tslug: ${quoteTsString(variables.slugKebabCase)},`,
|
|
27
|
-
`\t\tattributeTypeName: ${quoteTsString(`${variables.pascalCase}Attributes`)},`,
|
|
28
|
-
`\t\ttypesFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}/types.ts`)},`,
|
|
29
|
-
"\t},",
|
|
30
|
-
].join("\n");
|
|
31
|
-
}
|
|
32
|
-
function buildPersistenceBlockConfigEntry(variables) {
|
|
33
|
-
return [
|
|
34
|
-
"\t{",
|
|
35
|
-
`\t\tapiTypesFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}/api-types.ts`)},`,
|
|
36
|
-
`\t\tattributeTypeName: ${quoteTsString(`${variables.pascalCase}Attributes`)},`,
|
|
37
|
-
"\t\trestManifest: defineEndpointManifest( {",
|
|
38
|
-
"\t\t\tcontracts: {",
|
|
39
|
-
"\t\t\t\t'bootstrap-query': {",
|
|
40
|
-
`\t\t\t\t\tsourceTypeName: ${quoteTsString(`${variables.pascalCase}BootstrapQuery`)},`,
|
|
41
|
-
"\t\t\t\t},",
|
|
42
|
-
"\t\t\t\t'bootstrap-response': {",
|
|
43
|
-
`\t\t\t\t\tsourceTypeName: ${quoteTsString(`${variables.pascalCase}BootstrapResponse`)},`,
|
|
44
|
-
"\t\t\t\t},",
|
|
45
|
-
"\t\t\t\t'state-query': {",
|
|
46
|
-
`\t\t\t\t\tsourceTypeName: ${quoteTsString(`${variables.pascalCase}StateQuery`)},`,
|
|
47
|
-
"\t\t\t\t},",
|
|
48
|
-
"\t\t\t\t'state-response': {",
|
|
49
|
-
`\t\t\t\t\tsourceTypeName: ${quoteTsString(`${variables.pascalCase}StateResponse`)},`,
|
|
50
|
-
"\t\t\t\t},",
|
|
51
|
-
"\t\t\t\t'write-state-request': {",
|
|
52
|
-
`\t\t\t\t\tsourceTypeName: ${quoteTsString(`${variables.pascalCase}WriteStateRequest`)},`,
|
|
53
|
-
"\t\t\t\t},",
|
|
54
|
-
"\t\t\t},",
|
|
55
|
-
"\t\t\tendpoints: [",
|
|
56
|
-
"\t\t\t\t{",
|
|
57
|
-
"\t\t\t\t\tauth: 'public',",
|
|
58
|
-
"\t\t\t\t\tmethod: 'GET',",
|
|
59
|
-
`\t\t\t\t\toperationId: ${quoteTsString(`get${variables.pascalCase}State`)},`,
|
|
60
|
-
`\t\t\t\t\tpath: ${quoteTsString(`/${variables.namespace}/v1/${variables.slugKebabCase}/state`)},`,
|
|
61
|
-
"\t\t\t\t\tqueryContract: 'state-query',",
|
|
62
|
-
"\t\t\t\t\tresponseContract: 'state-response',",
|
|
63
|
-
`\t\t\t\t\tsummary: 'Read the current persisted state.',`,
|
|
64
|
-
`\t\t\t\t\ttags: [ ${quoteTsString(variables.title)} ],`,
|
|
65
|
-
"\t\t\t\t},",
|
|
66
|
-
"\t\t\t\t{",
|
|
67
|
-
`\t\t\t\t\tauth: ${quoteTsString(variables.restWriteAuthIntent)},`,
|
|
68
|
-
"\t\t\t\t\tbodyContract: 'write-state-request',",
|
|
69
|
-
"\t\t\t\t\tmethod: 'POST',",
|
|
70
|
-
`\t\t\t\t\toperationId: ${quoteTsString(`write${variables.pascalCase}State`)},`,
|
|
71
|
-
`\t\t\t\t\tpath: ${quoteTsString(`/${variables.namespace}/v1/${variables.slugKebabCase}/state`)},`,
|
|
72
|
-
"\t\t\t\t\tresponseContract: 'state-response',",
|
|
73
|
-
`\t\t\t\t\tsummary: 'Write the current persisted state.',`,
|
|
74
|
-
`\t\t\t\t\ttags: [ ${quoteTsString(variables.title)} ],`,
|
|
75
|
-
"\t\t\t\t\twordpressAuth: {",
|
|
76
|
-
`\t\t\t\t\t\tmechanism: ${quoteTsString(variables.restWriteAuthMechanism)},`,
|
|
77
|
-
"\t\t\t\t\t},",
|
|
78
|
-
"\t\t\t\t},",
|
|
79
|
-
"\t\t\t\t{",
|
|
80
|
-
"\t\t\t\t\tauth: 'public',",
|
|
81
|
-
"\t\t\t\t\tmethod: 'GET',",
|
|
82
|
-
`\t\t\t\t\toperationId: ${quoteTsString(`get${variables.pascalCase}Bootstrap`)},`,
|
|
83
|
-
`\t\t\t\t\tpath: ${quoteTsString(`/${variables.namespace}/v1/${variables.slugKebabCase}/bootstrap`)},`,
|
|
84
|
-
"\t\t\t\t\tqueryContract: 'bootstrap-query',",
|
|
85
|
-
"\t\t\t\t\tresponseContract: 'bootstrap-response',",
|
|
86
|
-
`\t\t\t\t\tsummary: 'Read fresh session bootstrap state for the current viewer.',`,
|
|
87
|
-
`\t\t\t\t\ttags: [ ${quoteTsString(variables.title)} ],`,
|
|
88
|
-
"\t\t\t\t},",
|
|
89
|
-
"\t\t\t],",
|
|
90
|
-
"\t\t\tinfo: {",
|
|
91
|
-
`\t\t\t\ttitle: ${quoteTsString(`${variables.title} REST API`)},`,
|
|
92
|
-
"\t\t\t\tversion: '1.0.0',",
|
|
93
|
-
"\t\t\t},",
|
|
94
|
-
"\t\t} ),",
|
|
95
|
-
`\t\topenApiFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}/api.openapi.json`)},`,
|
|
96
|
-
`\t\tslug: ${quoteTsString(variables.slugKebabCase)},`,
|
|
97
|
-
`\t\ttypesFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}/types.ts`)},`,
|
|
98
|
-
"\t},",
|
|
99
|
-
].join("\n");
|
|
100
|
-
}
|
|
101
|
-
function buildCompoundChildConfigEntry(variables) {
|
|
102
|
-
return [
|
|
103
|
-
"\t{",
|
|
104
|
-
`\t\tslug: ${quoteTsString(`${variables.slugKebabCase}-item`)},`,
|
|
105
|
-
`\t\tattributeTypeName: ${quoteTsString(`${variables.pascalCase}ItemAttributes`)},`,
|
|
106
|
-
`\t\ttypesFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}-item/types.ts`)},`,
|
|
107
|
-
"\t},",
|
|
108
|
-
].join("\n");
|
|
109
|
-
}
|
|
110
|
-
function buildConfigEntries(templateId, variables) {
|
|
111
|
-
if (templateId === "basic" || templateId === "interactivity") {
|
|
112
|
-
return [buildSingleBlockConfigEntry(variables)];
|
|
113
|
-
}
|
|
114
|
-
if (templateId === "persistence") {
|
|
115
|
-
return [buildPersistenceBlockConfigEntry(variables)];
|
|
116
|
-
}
|
|
117
|
-
if (variables.compoundPersistenceEnabled === "true") {
|
|
118
|
-
return [
|
|
119
|
-
buildPersistenceBlockConfigEntry(variables),
|
|
120
|
-
buildCompoundChildConfigEntry(variables),
|
|
121
|
-
];
|
|
122
|
-
}
|
|
123
|
-
return [
|
|
124
|
-
buildSingleBlockConfigEntry(variables),
|
|
125
|
-
buildCompoundChildConfigEntry(variables),
|
|
126
|
-
];
|
|
127
|
-
}
|
|
128
|
-
function buildMigrationBlocks(templateId, variables) {
|
|
129
|
-
if (templateId === "compound") {
|
|
130
|
-
return [
|
|
131
|
-
{
|
|
132
|
-
blockJsonFile: `src/blocks/${variables.slugKebabCase}/block.json`,
|
|
133
|
-
blockName: `${variables.namespace}/${variables.slugKebabCase}`,
|
|
134
|
-
key: variables.slugKebabCase,
|
|
135
|
-
manifestFile: `src/blocks/${variables.slugKebabCase}/typia.manifest.json`,
|
|
136
|
-
saveFile: `src/blocks/${variables.slugKebabCase}/save.tsx`,
|
|
137
|
-
typesFile: `src/blocks/${variables.slugKebabCase}/types.ts`,
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
blockJsonFile: `src/blocks/${variables.slugKebabCase}-item/block.json`,
|
|
141
|
-
blockName: `${variables.namespace}/${variables.slugKebabCase}-item`,
|
|
142
|
-
key: `${variables.slugKebabCase}-item`,
|
|
143
|
-
manifestFile: `src/blocks/${variables.slugKebabCase}-item/typia.manifest.json`,
|
|
144
|
-
saveFile: `src/blocks/${variables.slugKebabCase}-item/save.tsx`,
|
|
145
|
-
typesFile: `src/blocks/${variables.slugKebabCase}-item/types.ts`,
|
|
146
|
-
},
|
|
147
|
-
];
|
|
148
|
-
}
|
|
149
|
-
return [
|
|
150
|
-
{
|
|
151
|
-
blockJsonFile: `src/blocks/${variables.slugKebabCase}/block.json`,
|
|
152
|
-
blockName: `${variables.namespace}/${variables.slugKebabCase}`,
|
|
153
|
-
key: variables.slugKebabCase,
|
|
154
|
-
manifestFile: `src/blocks/${variables.slugKebabCase}/typia.manifest.json`,
|
|
155
|
-
saveFile: `src/blocks/${variables.slugKebabCase}/save.tsx`,
|
|
156
|
-
typesFile: `src/blocks/${variables.slugKebabCase}/types.ts`,
|
|
157
|
-
},
|
|
158
|
-
];
|
|
159
|
-
}
|
|
160
20
|
async function ensureCollectionImport(filePath) {
|
|
161
21
|
await patchFile(filePath, (source) => {
|
|
162
22
|
if (source.includes(COLLECTION_IMPORT_LINE)) {
|
|
@@ -183,13 +43,6 @@ async function addCollectionImportsForTemplate(projectDir, templateId, variables
|
|
|
183
43
|
}
|
|
184
44
|
await ensureCollectionImport(path.join(projectDir, "src", "blocks", variables.slugKebabCase, "index.tsx"));
|
|
185
45
|
}
|
|
186
|
-
function ensureBlockConfigCanAddRestManifests(source) {
|
|
187
|
-
const importLine = "import { defineEndpointManifest } from '@wp-typia/block-runtime/metadata-core';";
|
|
188
|
-
if (REST_MANIFEST_IMPORT_PATTERN.test(source)) {
|
|
189
|
-
return source;
|
|
190
|
-
}
|
|
191
|
-
return `${importLine}\n\n${source}`;
|
|
192
|
-
}
|
|
193
46
|
async function appendBlockConfigEntries(projectDir, entries, needsRestManifestImport) {
|
|
194
47
|
await appendWorkspaceInventoryEntries(projectDir, {
|
|
195
48
|
blockEntries: entries,
|
|
@@ -201,23 +54,6 @@ async function renderWorkspacePersistenceServerModule(projectDir, variables) {
|
|
|
201
54
|
const templateDir = buildServerTemplateRoot(variables.persistencePolicy);
|
|
202
55
|
await copyInterpolatedDirectory(templateDir, targetDir, variables);
|
|
203
56
|
}
|
|
204
|
-
const COMPOUND_SHARED_SUPPORT_FILES = ["hooks.ts", "validator-toolkit.ts"];
|
|
205
|
-
const LEGACY_ASSERT_PATTERN = /assert:\s*typia\.createAssert</u;
|
|
206
|
-
const LEGACY_MANIFEST_PATTERN = /\r?\n[ \t]*manifest:\s*currentManifest,/u;
|
|
207
|
-
const LEGACY_VALIDATOR_MANIFEST_IMPORT_PATTERN = /^[\uFEFF \t]*import\s+currentManifest\s+from\s*["']\.\/typia\.manifest\.json["'];?$/u;
|
|
208
|
-
const LEGACY_TOOLKIT_CALL_PATTERN = /createTemplateValidatorToolkit<\s*(?<typeName>[A-Za-z0-9_]+)\s*>\s*\(\s*\{/u;
|
|
209
|
-
const LEGACY_VALIDATOR_TOOLKIT_IMPORT_PATTERN = /from\s*["']\.\.\/\.\.\/validator-toolkit["']/u;
|
|
210
|
-
const TYPIA_IMPORT_PATTERN = /^[\uFEFF \t]*import\s+typia\s+from\s*["']typia["'];?/mu;
|
|
211
|
-
const COMPATIBLE_COMPOUND_TOOLKIT_PATTERNS = [
|
|
212
|
-
/interface\s+TemplateValidatorFunctions\s*<\s*T\s+extends\s+object\s*>\s*\{/u,
|
|
213
|
-
/\bassert\s*:\s*ScaffoldValidatorToolkitOptions\s*<\s*T\s*>\s*\[\s*["']assert["']\s*\]/u,
|
|
214
|
-
/\bclone\s*:\s*ScaffoldValidatorToolkitOptions\s*<\s*T\s*>\s*\[\s*["']clone["']\s*\]/u,
|
|
215
|
-
/\bis\s*:\s*ScaffoldValidatorToolkitOptions\s*<\s*T\s*>\s*\[\s*["']is["']\s*\]/u,
|
|
216
|
-
/\bprune\s*:\s*ScaffoldValidatorToolkitOptions\s*<\s*T\s*>\s*\[\s*["']prune["']\s*\]/u,
|
|
217
|
-
/\brandom\s*:\s*ScaffoldValidatorToolkitOptions\s*<\s*T\s*>\s*\[\s*["']random["']\s*\]/u,
|
|
218
|
-
/\bvalidate\s*:\s*ScaffoldValidatorToolkitOptions\s*<\s*T\s*>\s*\[\s*["']validate["']\s*\]/u,
|
|
219
|
-
/createTemplateValidatorToolkit\s*<\s*T\s+extends\s+object\s*>\s*\(\s*\{/u,
|
|
220
|
-
];
|
|
221
57
|
function normalizeExternalLayerOption(value) {
|
|
222
58
|
if (typeof value !== "string") {
|
|
223
59
|
return undefined;
|
|
@@ -233,140 +69,6 @@ function resolveExternalLayerSourceFromCaller(source, callerCwd) {
|
|
|
233
69
|
}
|
|
234
70
|
return path.resolve(callerCwd, source);
|
|
235
71
|
}
|
|
236
|
-
function shouldRefreshCompoundValidatorToolkit(source) {
|
|
237
|
-
return (source === null ||
|
|
238
|
-
!COMPATIBLE_COMPOUND_TOOLKIT_PATTERNS.every((pattern) => pattern.test(source)));
|
|
239
|
-
}
|
|
240
|
-
function isLegacyCompoundValidatorSource(source) {
|
|
241
|
-
return (typeof source === "string" &&
|
|
242
|
-
LEGACY_VALIDATOR_TOOLKIT_IMPORT_PATTERN.test(source) &&
|
|
243
|
-
!LEGACY_ASSERT_PATTERN.test(source));
|
|
244
|
-
}
|
|
245
|
-
function hasTypiaImport(source) {
|
|
246
|
-
return TYPIA_IMPORT_PATTERN.test(source.replace(/\/\*[\s\S]*?\*\//gu, ""));
|
|
247
|
-
}
|
|
248
|
-
function replaceFirstNonCommentLine(source, pattern, replacement) {
|
|
249
|
-
const lineEnding = source.includes("\r\n") ? "\r\n" : "\n";
|
|
250
|
-
const lines = source.split(/\r?\n/);
|
|
251
|
-
let inBlockComment = false;
|
|
252
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
253
|
-
const line = lines[index] ?? "";
|
|
254
|
-
const trimmed = line.trimStart();
|
|
255
|
-
if (inBlockComment) {
|
|
256
|
-
if (trimmed.includes("*/")) {
|
|
257
|
-
inBlockComment = false;
|
|
258
|
-
}
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
if (trimmed.startsWith("//")) {
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
if (trimmed.startsWith("/*")) {
|
|
265
|
-
if (!trimmed.includes("*/")) {
|
|
266
|
-
inBlockComment = true;
|
|
267
|
-
}
|
|
268
|
-
continue;
|
|
269
|
-
}
|
|
270
|
-
if (!pattern.test(line)) {
|
|
271
|
-
continue;
|
|
272
|
-
}
|
|
273
|
-
lines[index] = replacement;
|
|
274
|
-
return lines.join(lineEnding);
|
|
275
|
-
}
|
|
276
|
-
return source;
|
|
277
|
-
}
|
|
278
|
-
function upgradeLegacyCompoundValidatorSource(source) {
|
|
279
|
-
const typeNameMatch = source.match(LEGACY_TOOLKIT_CALL_PATTERN);
|
|
280
|
-
const typeName = typeNameMatch?.groups?.typeName;
|
|
281
|
-
if (!typeName) {
|
|
282
|
-
throw new Error("Unable to upgrade a legacy compound validator without a generated type import.");
|
|
283
|
-
}
|
|
284
|
-
let nextSource = source;
|
|
285
|
-
if (!hasTypiaImport(nextSource)) {
|
|
286
|
-
nextSource = `import typia from 'typia';\n${nextSource}`;
|
|
287
|
-
}
|
|
288
|
-
nextSource = replaceFirstNonCommentLine(nextSource, LEGACY_VALIDATOR_MANIFEST_IMPORT_PATTERN, "import currentManifest from './manifest-defaults-document';");
|
|
289
|
-
nextSource = nextSource.replace(LEGACY_TOOLKIT_CALL_PATTERN, [
|
|
290
|
-
`createTemplateValidatorToolkit< ${typeName} >( {`,
|
|
291
|
-
`\tassert: typia.createAssert< ${typeName} >(),`,
|
|
292
|
-
`\tclone: typia.misc.createClone< ${typeName} >() as (`,
|
|
293
|
-
`\t\tvalue: ${typeName},`,
|
|
294
|
-
`\t) => ${typeName},`,
|
|
295
|
-
`\tis: typia.createIs< ${typeName} >(),`,
|
|
296
|
-
].join("\n") + "\n");
|
|
297
|
-
const replacedManifest = nextSource.replace(LEGACY_MANIFEST_PATTERN, [
|
|
298
|
-
"",
|
|
299
|
-
"\tmanifest: currentManifest,",
|
|
300
|
-
`\tprune: typia.misc.createPrune< ${typeName} >(),`,
|
|
301
|
-
`\trandom: typia.createRandom< ${typeName} >() as (`,
|
|
302
|
-
"\t\t...args: unknown[]",
|
|
303
|
-
`\t) => ${typeName},`,
|
|
304
|
-
`\tvalidate: typia.createValidate< ${typeName} >(),`,
|
|
305
|
-
].join("\n"));
|
|
306
|
-
if (replacedManifest === nextSource) {
|
|
307
|
-
throw new Error("Unable to upgrade legacy compound validator: manifest anchor not found.");
|
|
308
|
-
}
|
|
309
|
-
return replacedManifest;
|
|
310
|
-
}
|
|
311
|
-
function renderLegacyManifestDefaultsWrapperSource() {
|
|
312
|
-
return [
|
|
313
|
-
"import rawCurrentManifest from './typia.manifest.json';",
|
|
314
|
-
"import { defineManifestDefaultsDocument } from '@wp-typia/block-runtime/defaults';",
|
|
315
|
-
"",
|
|
316
|
-
"const currentManifest = defineManifestDefaultsDocument( rawCurrentManifest );",
|
|
317
|
-
"",
|
|
318
|
-
"export default currentManifest;",
|
|
319
|
-
"",
|
|
320
|
-
].join("\n");
|
|
321
|
-
}
|
|
322
|
-
async function ensureLegacyCompoundValidatorManifestDefaultsWrapper(validatorPath) {
|
|
323
|
-
const validatorDir = path.dirname(validatorPath);
|
|
324
|
-
const wrapperPath = path.join(validatorDir, "manifest-defaults-document.ts");
|
|
325
|
-
const manifestPath = path.join(validatorDir, "typia.manifest.json");
|
|
326
|
-
if (fs.existsSync(wrapperPath) || !fs.existsSync(manifestPath)) {
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
await fsp.writeFile(wrapperPath, renderLegacyManifestDefaultsWrapperSource(), "utf8");
|
|
330
|
-
}
|
|
331
|
-
async function collectLegacyCompoundValidatorPaths(projectDir) {
|
|
332
|
-
const blocksDir = path.join(projectDir, "src", "blocks");
|
|
333
|
-
if (!fs.existsSync(blocksDir)) {
|
|
334
|
-
return [];
|
|
335
|
-
}
|
|
336
|
-
const blockEntries = await fsp.readdir(blocksDir, { withFileTypes: true });
|
|
337
|
-
const validatorPaths = await Promise.all(blockEntries
|
|
338
|
-
.filter((entry) => entry.isDirectory())
|
|
339
|
-
.map(async (entry) => {
|
|
340
|
-
const validatorPath = path.join(blocksDir, entry.name, "validators.ts");
|
|
341
|
-
const validatorSource = await readOptionalFile(validatorPath);
|
|
342
|
-
return isLegacyCompoundValidatorSource(validatorSource) ? validatorPath : null;
|
|
343
|
-
}));
|
|
344
|
-
return validatorPaths.filter((validatorPath) => validatorPath !== null);
|
|
345
|
-
}
|
|
346
|
-
async function ensureCompoundWorkspaceSupportFiles(projectDir, tempProjectDir, legacyValidatorPaths) {
|
|
347
|
-
for (const fileName of COMPOUND_SHARED_SUPPORT_FILES) {
|
|
348
|
-
const sourcePath = path.join(tempProjectDir, "src", fileName);
|
|
349
|
-
if (!fs.existsSync(sourcePath)) {
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
|
-
const targetPath = path.join(projectDir, "src", fileName);
|
|
353
|
-
const currentSource = await readOptionalFile(targetPath);
|
|
354
|
-
if (fileName === "validator-toolkit.ts"
|
|
355
|
-
? shouldRefreshCompoundValidatorToolkit(currentSource)
|
|
356
|
-
: currentSource === null) {
|
|
357
|
-
await fsp.mkdir(path.dirname(targetPath), { recursive: true });
|
|
358
|
-
await fsp.copyFile(sourcePath, targetPath);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
for (const validatorPath of legacyValidatorPaths) {
|
|
362
|
-
const currentSource = await readOptionalFile(validatorPath);
|
|
363
|
-
if (!isLegacyCompoundValidatorSource(currentSource)) {
|
|
364
|
-
continue;
|
|
365
|
-
}
|
|
366
|
-
await ensureLegacyCompoundValidatorManifestDefaultsWrapper(validatorPath);
|
|
367
|
-
await fsp.writeFile(validatorPath, upgradeLegacyCompoundValidatorSource(currentSource), "utf8");
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
72
|
async function copyScaffoldedBlockSlice(projectDir, templateId, tempProjectDir, variables, legacyValidatorPaths = []) {
|
|
371
73
|
if (templateId === "compound") {
|
|
372
74
|
await ensureCompoundWorkspaceSupportFiles(projectDir, tempProjectDir, legacyValidatorPaths);
|