@wp-typia/project-tools 0.15.3 → 0.16.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.
- package/dist/runtime/cli-add.js +5 -1
- package/dist/runtime/cli-scaffold.d.ts +2 -1
- package/dist/runtime/cli-scaffold.js +23 -2
- package/dist/runtime/scaffold-onboarding.d.ts +2 -1
- package/dist/runtime/scaffold-onboarding.js +37 -8
- package/dist/runtime/scaffold.js +5 -2
- package/package.json +1 -1
- package/templates/_shared/base/package.json.mustache +4 -3
- package/templates/_shared/base/scripts/sync-project.ts.mustache +103 -0
- package/templates/_shared/compound/core/package.json.mustache +4 -3
- package/templates/_shared/compound/core/scripts/sync-project.ts.mustache +103 -0
- package/templates/_shared/compound/persistence/package.json.mustache +4 -3
- package/templates/_shared/compound/persistence/scripts/sync-project.ts.mustache +103 -0
- package/templates/_shared/compound/persistence/scripts/sync-rest-contracts.ts.mustache +28 -0
- package/templates/_shared/persistence/core/package.json.mustache +4 -3
- package/templates/_shared/persistence/core/scripts/sync-project.ts.mustache +103 -0
- package/templates/_shared/persistence/core/scripts/sync-rest-contracts.ts.mustache +28 -0
- package/templates/interactivity/package.json.mustache +4 -3
package/dist/runtime/cli-add.js
CHANGED
|
@@ -648,6 +648,7 @@ const LEGACY_ASSERT_PATTERN = /assert:\s*typia\.createAssert</u;
|
|
|
648
648
|
const LEGACY_MANIFEST_PATTERN = /\r?\n[ \t]*manifest:\s*currentManifest,/u;
|
|
649
649
|
const LEGACY_TOOLKIT_CALL_PATTERN = /createTemplateValidatorToolkit<\s*(?<typeName>[A-Za-z0-9_]+)\s*>\s*\(\s*\{/u;
|
|
650
650
|
const LEGACY_VALIDATOR_TOOLKIT_IMPORT_PATTERN = /from\s*["']\.\.\/\.\.\/validator-toolkit["']/u;
|
|
651
|
+
const TYPIA_IMPORT_PATTERN = /^[\uFEFF \t]*import\s+typia\s+from\s*["']typia["'];?/mu;
|
|
651
652
|
const COMPATIBLE_COMPOUND_TOOLKIT_PATTERNS = [
|
|
652
653
|
/interface\s+TemplateValidatorFunctions\s*<\s*T\s+extends\s+object\s*>\s*\{/u,
|
|
653
654
|
/\bassert\s*:\s*ScaffoldValidatorToolkitOptions\s*<\s*T\s*>\s*\[\s*["']assert["']\s*\]/u,
|
|
@@ -667,6 +668,9 @@ function isLegacyCompoundValidatorSource(source) {
|
|
|
667
668
|
LEGACY_VALIDATOR_TOOLKIT_IMPORT_PATTERN.test(source) &&
|
|
668
669
|
!LEGACY_ASSERT_PATTERN.test(source));
|
|
669
670
|
}
|
|
671
|
+
function hasTypiaImport(source) {
|
|
672
|
+
return TYPIA_IMPORT_PATTERN.test(source.replace(/\/\*[\s\S]*?\*\//gu, ""));
|
|
673
|
+
}
|
|
670
674
|
function upgradeLegacyCompoundValidatorSource(source) {
|
|
671
675
|
const typeNameMatch = source.match(LEGACY_TOOLKIT_CALL_PATTERN);
|
|
672
676
|
const typeName = typeNameMatch?.groups?.typeName;
|
|
@@ -674,7 +678,7 @@ function upgradeLegacyCompoundValidatorSource(source) {
|
|
|
674
678
|
throw new Error("Unable to upgrade a legacy compound validator without a generated type import.");
|
|
675
679
|
}
|
|
676
680
|
let nextSource = source;
|
|
677
|
-
if (!nextSource
|
|
681
|
+
if (!hasTypiaImport(nextSource)) {
|
|
678
682
|
nextSource = `import typia from 'typia';\n${nextSource}`;
|
|
679
683
|
}
|
|
680
684
|
nextSource = nextSource.replace(LEGACY_TOOLKIT_CALL_PATTERN, [
|
|
@@ -10,6 +10,7 @@ interface GetNextStepsOptions {
|
|
|
10
10
|
templateId: string;
|
|
11
11
|
}
|
|
12
12
|
interface GetOptionalOnboardingOptions {
|
|
13
|
+
availableScripts?: string[];
|
|
13
14
|
packageManager: PackageManagerId;
|
|
14
15
|
templateId: string;
|
|
15
16
|
compoundPersistenceEnabled?: boolean;
|
|
@@ -60,7 +61,7 @@ export declare function getNextSteps({ projectInput, projectDir, packageManager,
|
|
|
60
61
|
* @param options Package-manager and template context for optional guidance.
|
|
61
62
|
* @returns Optional onboarding note and step list.
|
|
62
63
|
*/
|
|
63
|
-
export declare function getOptionalOnboarding({ packageManager, templateId, compoundPersistenceEnabled, }: GetOptionalOnboardingOptions): OptionalOnboardingGuidance;
|
|
64
|
+
export declare function getOptionalOnboarding({ availableScripts, packageManager, templateId, compoundPersistenceEnabled, }: GetOptionalOnboardingOptions): OptionalOnboardingGuidance;
|
|
64
65
|
/**
|
|
65
66
|
* Resolve scaffold options, prompts, and follow-up steps for one CLI run.
|
|
66
67
|
*
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
import { collectScaffoldAnswers, DATA_STORAGE_MODES, PERSISTENCE_POLICIES, isDataStorageMode, isPersistencePolicy, resolvePackageManagerId, resolveTemplateId, scaffoldProject, } from "./scaffold.js";
|
|
3
4
|
import { formatInstallCommand, formatRunScript, } from "./package-managers.js";
|
|
@@ -79,10 +80,14 @@ export function getNextSteps({ projectInput, projectDir, packageManager, noInsta
|
|
|
79
80
|
* @param options Package-manager and template context for optional guidance.
|
|
80
81
|
* @returns Optional onboarding note and step list.
|
|
81
82
|
*/
|
|
82
|
-
export function getOptionalOnboarding({ packageManager, templateId, compoundPersistenceEnabled = false, }) {
|
|
83
|
+
export function getOptionalOnboarding({ availableScripts, packageManager, templateId, compoundPersistenceEnabled = false, }) {
|
|
83
84
|
return {
|
|
84
|
-
note: getOptionalOnboardingNote(packageManager, templateId
|
|
85
|
+
note: getOptionalOnboardingNote(packageManager, templateId, {
|
|
86
|
+
availableScripts,
|
|
87
|
+
compoundPersistenceEnabled,
|
|
88
|
+
}),
|
|
85
89
|
steps: getOptionalOnboardingSteps(packageManager, templateId, {
|
|
90
|
+
availableScripts,
|
|
86
91
|
compoundPersistenceEnabled,
|
|
87
92
|
}),
|
|
88
93
|
};
|
|
@@ -185,8 +190,24 @@ export async function runScaffoldFlow({ projectInput, cwd = process.cwd(), templ
|
|
|
185
190
|
withTestPreset: resolvedWithTestPreset,
|
|
186
191
|
withWpEnv: resolvedWithWpEnv,
|
|
187
192
|
});
|
|
193
|
+
let availableScripts;
|
|
194
|
+
try {
|
|
195
|
+
const parsedPackageJson = JSON.parse(fs.readFileSync(path.join(projectDir, "package.json"), "utf8"));
|
|
196
|
+
const scripts = parsedPackageJson.scripts &&
|
|
197
|
+
typeof parsedPackageJson.scripts === "object" &&
|
|
198
|
+
!Array.isArray(parsedPackageJson.scripts)
|
|
199
|
+
? parsedPackageJson.scripts
|
|
200
|
+
: {};
|
|
201
|
+
availableScripts = Object.entries(scripts)
|
|
202
|
+
.filter(([, value]) => typeof value === "string")
|
|
203
|
+
.map(([scriptName]) => scriptName);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
availableScripts = undefined;
|
|
207
|
+
}
|
|
188
208
|
return {
|
|
189
209
|
optionalOnboarding: getOptionalOnboarding({
|
|
210
|
+
availableScripts,
|
|
190
211
|
packageManager: resolvedPackageManager,
|
|
191
212
|
templateId: resolvedTemplateId,
|
|
192
213
|
compoundPersistenceEnabled: result.variables.compoundPersistenceEnabled === "true",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PackageManagerId } from "./package-managers.js";
|
|
2
2
|
interface SyncOnboardingOptions {
|
|
3
|
+
availableScripts?: string[];
|
|
3
4
|
compoundPersistenceEnabled?: boolean;
|
|
4
5
|
}
|
|
5
6
|
interface PhpRestExtensionOptions extends SyncOnboardingOptions {
|
|
@@ -16,7 +17,7 @@ export declare function getOptionalOnboardingSteps(packageManager: PackageManage
|
|
|
16
17
|
/**
|
|
17
18
|
* Returns the onboarding note explaining when manual sync is optional.
|
|
18
19
|
*/
|
|
19
|
-
export declare function getOptionalOnboardingNote(packageManager: PackageManagerId, templateId?: string): string;
|
|
20
|
+
export declare function getOptionalOnboardingNote(packageManager: PackageManagerId, templateId?: string, options?: SyncOnboardingOptions): string;
|
|
20
21
|
/**
|
|
21
22
|
* Returns source-of-truth guidance for generated artifacts by template mode.
|
|
22
23
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { formatRunScript } from "./package-managers.js";
|
|
2
2
|
import { getPrimaryDevelopmentScript } from "./local-dev-presets.js";
|
|
3
|
+
import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, isBuiltInTemplateId, } from "./template-registry.js";
|
|
3
4
|
function templateHasPersistenceSync(templateId, { compoundPersistenceEnabled = false } = {}) {
|
|
4
5
|
return templateId === "persistence" || (templateId === "compound" && compoundPersistenceEnabled);
|
|
5
6
|
}
|
|
@@ -7,9 +8,19 @@ function templateHasPersistenceSync(templateId, { compoundPersistenceEnabled = f
|
|
|
7
8
|
* Returns the optional sync script names to suggest for a template.
|
|
8
9
|
*/
|
|
9
10
|
export function getOptionalSyncScriptNames(templateId, options = {}) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
const availableScripts = new Set(options.availableScripts ?? []);
|
|
12
|
+
if (availableScripts.has("sync")) {
|
|
13
|
+
return ["sync"];
|
|
14
|
+
}
|
|
15
|
+
const fallbackScripts = ["sync-types", "sync-rest"].filter((scriptName) => availableScripts.has(scriptName));
|
|
16
|
+
if (fallbackScripts.length > 0) {
|
|
17
|
+
return fallbackScripts;
|
|
18
|
+
}
|
|
19
|
+
if (!isBuiltInTemplateId(templateId) &&
|
|
20
|
+
templateId !== OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
return ["sync"];
|
|
13
24
|
}
|
|
14
25
|
/**
|
|
15
26
|
* Formats optional onboarding sync commands for the selected package manager.
|
|
@@ -20,15 +31,33 @@ export function getOptionalOnboardingSteps(packageManager, templateId, options =
|
|
|
20
31
|
/**
|
|
21
32
|
* Returns the onboarding note explaining when manual sync is optional.
|
|
22
33
|
*/
|
|
23
|
-
export function getOptionalOnboardingNote(packageManager, templateId = "basic") {
|
|
34
|
+
export function getOptionalOnboardingNote(packageManager, templateId = "basic", options = {}) {
|
|
35
|
+
const optionalSyncScripts = getOptionalSyncScriptNames(templateId, options);
|
|
36
|
+
const hasUnifiedSync = optionalSyncScripts.includes("sync");
|
|
37
|
+
const syncSteps = optionalSyncScripts.map((scriptName) => formatRunScript(packageManager, scriptName));
|
|
24
38
|
const developmentScript = getPrimaryDevelopmentScript(templateId);
|
|
39
|
+
const syncCommand = formatRunScript(packageManager, hasUnifiedSync ? "sync" : "sync-types");
|
|
40
|
+
const syncCheckCommand = formatRunScript(packageManager, hasUnifiedSync ? "sync" : "sync-types", "--check");
|
|
25
41
|
const failOnLossySyncCommand = formatRunScript(packageManager, "sync-types", "--fail-on-lossy");
|
|
26
42
|
const syncTypesCommand = formatRunScript(packageManager, "sync-types");
|
|
43
|
+
const syncRestCommand = formatRunScript(packageManager, "sync-rest");
|
|
27
44
|
const typecheckCommand = formatRunScript(packageManager, "typecheck");
|
|
28
45
|
const strictSyncCommand = formatRunScript(packageManager, "sync-types", "--strict --report json");
|
|
46
|
+
const advancedPersistenceNote = templateHasPersistenceSync(templateId, options)
|
|
47
|
+
? ` ${syncRestCommand} remains available for advanced REST-only refreshes, but it now fails fast when type-derived artifacts are stale; run \`${syncCommand}\` or \`${syncTypesCommand}\` first.`
|
|
48
|
+
: "";
|
|
49
|
+
const fallbackCustomTemplateNote = !hasUnifiedSync &&
|
|
50
|
+
syncSteps.length > 0 &&
|
|
51
|
+
!isBuiltInTemplateId(templateId) &&
|
|
52
|
+
templateId !== OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE
|
|
53
|
+
? `Run ${syncSteps.join(" then ")} manually before build, typecheck, or commit. ${syncCheckCommand} verifies the current type-derived artifacts without rewriting them.${optionalSyncScripts.includes("sync-rest") ? ` ${syncRestCommand} remains available for REST-only refreshes after ${syncTypesCommand}.` : ""}`
|
|
54
|
+
: null;
|
|
55
|
+
if (fallbackCustomTemplateNote) {
|
|
56
|
+
return fallbackCustomTemplateNote;
|
|
57
|
+
}
|
|
29
58
|
return `${formatRunScript(packageManager, developmentScript)} ${developmentScript === "dev"
|
|
30
59
|
? "watches the relevant sync scripts during local development."
|
|
31
|
-
: "remains the primary local entry point."} ${formatRunScript(packageManager, "start")} still runs one-shot syncs before starting, while ${formatRunScript(packageManager, "build")} and ${typecheckCommand} verify that generated metadata/schema artifacts are already current
|
|
60
|
+
: "remains the primary local entry point."} ${formatRunScript(packageManager, "start")} still runs one-shot syncs before starting, while ${formatRunScript(packageManager, "build")} and ${typecheckCommand} verify that generated metadata/schema artifacts are already current through ${syncCheckCommand}. Run ${syncCommand} manually when you want to refresh generated artifacts before build, typecheck, or commit. ${syncTypesCommand} stays warn-only by default; use \`${failOnLossySyncCommand}\` to fail only on lossy WordPress projections, or \`${strictSyncCommand}\` for a CI-friendly JSON report that fails on all warnings.${advancedPersistenceNote} They do not create migration history.`;
|
|
32
61
|
}
|
|
33
62
|
/**
|
|
34
63
|
* Returns source-of-truth guidance for generated artifacts by template mode.
|
|
@@ -37,12 +66,12 @@ export function getTemplateSourceOfTruthNote(templateId, { compoundPersistenceEn
|
|
|
37
66
|
if (templateId === "compound") {
|
|
38
67
|
const compoundBase = "`src/blocks/*/types.ts` files remain the source of truth for each block's `block.json`, `typia.manifest.json`, and `typia-validator.php`. Fresh scaffolds include starter `typia.manifest.json` files so editor imports resolve before the first sync.";
|
|
39
68
|
if (compoundPersistenceEnabled) {
|
|
40
|
-
return `${compoundBase} For persistence-enabled parents, \`src/blocks/*/api-types.ts\` files remain the source of truth for \`src/blocks/*/api-schemas/*\` when you run \`sync-rest\`, while \`src/blocks/*/transport.ts\` is the first-class transport seam for editor and frontend requests.`;
|
|
69
|
+
return `${compoundBase} For persistence-enabled parents, \`src/blocks/*/api-types.ts\` files remain the source of truth for \`src/blocks/*/api-schemas/*\` when you run \`sync\` or \`sync-rest\`, while \`src/blocks/*/transport.ts\` is the first-class transport seam for editor and frontend requests.`;
|
|
41
70
|
}
|
|
42
71
|
return compoundBase;
|
|
43
72
|
}
|
|
44
73
|
if (templateId === "persistence") {
|
|
45
|
-
return "`src/types.ts` remains the source of truth for `block.json`, `typia.manifest.json`, and `typia-validator.php`. Fresh scaffolds include a starter `typia.manifest.json` so editor imports resolve before the first sync. `src/api-types.ts` remains the source of truth for `src/api-schemas/*` when you run `sync-rest`, while `src/transport.ts` is the first-class transport seam for editor and frontend requests. This scaffold is intentionally server-rendered: `src/render.php` is the canonical frontend entry, `src/save.tsx` returns `null`, and session-only write data now refreshes through the dedicated `/bootstrap` endpoint after hydration instead of being frozen into markup.";
|
|
74
|
+
return "`src/types.ts` remains the source of truth for `block.json`, `typia.manifest.json`, and `typia-validator.php`. Fresh scaffolds include a starter `typia.manifest.json` so editor imports resolve before the first sync. `src/api-types.ts` remains the source of truth for `src/api-schemas/*` when you run `sync` or `sync-rest`, while `src/transport.ts` is the first-class transport seam for editor and frontend requests. This scaffold is intentionally server-rendered: `src/render.php` is the canonical frontend entry, `src/save.tsx` returns `null`, and session-only write data now refreshes through the dedicated `/bootstrap` endpoint after hydration instead of being frozen into markup.";
|
|
46
75
|
}
|
|
47
76
|
return "`src/types.ts` remains the source of truth for `block.json`, `typia.manifest.json`, and `typia-validator.php`. Fresh scaffolds include a starter `typia.manifest.json` so editor imports resolve before the first sync. The basic scaffold stays static by design: `src/render.php` is only an opt-in server placeholder, `src/save.tsx` remains the canonical frontend output, and the generated webpack config keeps the current `@wordpress/scripts` CommonJS baseline unless you intentionally add `render` to `block.json`.";
|
|
48
77
|
}
|
|
@@ -69,7 +98,7 @@ function formatPhpRestExtensionPointsSection({ apiTypesPath, extraNote, mainPhpP
|
|
|
69
98
|
`- Edit \`${mainPhpPath}\` when you need to ${mainPhpScope}.`,
|
|
70
99
|
"- Edit `inc/rest-auth.php` or `inc/rest-public.php` when you need to customize write permissions or token/request-id/nonce checks for the selected policy.",
|
|
71
100
|
`- Edit \`${transportPath}\` when you need to switch between direct WordPress REST and a contract-compatible proxy or BFF without changing the endpoint contracts.`,
|
|
72
|
-
`- Keep \`${apiTypesPath}\` as the source of truth for request and response contracts, then regenerate \`${schemaJsonGlob}\`, per-contract \`${perContractOpenApiGlob}\`, and \`${aggregateOpenApiPath}\` with \`sync-rest
|
|
101
|
+
`- Keep \`${apiTypesPath}\` as the source of truth for request and response contracts, then regenerate \`${schemaJsonGlob}\`, per-contract \`${perContractOpenApiGlob}\`, and \`${aggregateOpenApiPath}\` with \`sync\` (or \`sync-rest\` after \`sync-types\` when you only need the REST layer).`,
|
|
73
102
|
"- Avoid hand-editing generated schema and OpenAPI artifacts unless you are debugging generated output; they are meant to be regenerated from TypeScript contracts.",
|
|
74
103
|
];
|
|
75
104
|
if (typeof extraNote === "string" && extraNote.length > 0) {
|
package/dist/runtime/scaffold.js
CHANGED
|
@@ -317,12 +317,13 @@ function buildReadme(templateId, variables, packageManager, { withMigrationUi =
|
|
|
317
317
|
const sourceOfTruthNote = getTemplateSourceOfTruthNote(templateId, {
|
|
318
318
|
compoundPersistenceEnabled: variables.compoundPersistenceEnabled === "true",
|
|
319
319
|
});
|
|
320
|
+
const compoundPersistenceEnabled = variables.compoundPersistenceEnabled === "true";
|
|
320
321
|
const publicPersistencePolicyNote = variables.isPublicPersistencePolicy === "true"
|
|
321
322
|
? "Public persistence writes use signed short-lived tokens, per-request ids, and coarse rate limiting by default. Add application-specific abuse controls before using the same pattern for high-value metrics or experiments."
|
|
322
323
|
: null;
|
|
323
324
|
const compoundExtensionWorkflowSection = getCompoundExtensionWorkflowSection(packageManager, templateId);
|
|
324
325
|
const phpRestExtensionPointsSection = getPhpRestExtensionPointsSection(templateId, {
|
|
325
|
-
compoundPersistenceEnabled
|
|
326
|
+
compoundPersistenceEnabled,
|
|
326
327
|
slug: variables.slug,
|
|
327
328
|
});
|
|
328
329
|
const developmentScript = getPrimaryDevelopmentScript(templateId);
|
|
@@ -362,7 +363,9 @@ ${formatRunScript(packageManager, "build")}
|
|
|
362
363
|
${optionalOnboardingSteps.join("\n")}
|
|
363
364
|
\`\`\`
|
|
364
365
|
|
|
365
|
-
${getOptionalOnboardingNote(packageManager, templateId
|
|
366
|
+
${getOptionalOnboardingNote(packageManager, templateId, {
|
|
367
|
+
compoundPersistenceEnabled,
|
|
368
|
+
})}
|
|
366
369
|
|
|
367
370
|
${sourceOfTruthNote}${publicPersistencePolicyNote ? `\n\n${publicPersistencePolicyNote}` : ""}${migrationSection ? `\n\n${migrationSection}` : ""}${compoundExtensionWorkflowSection ? `\n\n${compoundExtensionWorkflowSection}` : ""}${wpEnvSection ? `\n\n${wpEnvSection}` : ""}${testPresetSection ? `\n\n${testPresetSection}` : ""}${phpRestExtensionPointsSection ? `\n\n${phpRestExtensionPointsSection}` : ""}
|
|
368
371
|
`;
|
package/package.json
CHANGED
|
@@ -7,15 +7,16 @@
|
|
|
7
7
|
"license": "GPL-2.0-or-later",
|
|
8
8
|
"main": "build/index.js",
|
|
9
9
|
"scripts": {
|
|
10
|
+
"sync": "tsx scripts/sync-project.ts",
|
|
10
11
|
"sync-types": "tsx scripts/sync-types-to-block-json.ts",
|
|
11
|
-
"build": "bun run sync
|
|
12
|
-
"start": "bun run sync
|
|
12
|
+
"build": "bun run sync --check && wp-scripts build --experimental-modules",
|
|
13
|
+
"start": "bun run sync && wp-scripts start --experimental-modules",
|
|
13
14
|
"dev": "bun run start",
|
|
14
15
|
"lint:js": "wp-scripts lint-js",
|
|
15
16
|
"lint:css": "wp-scripts lint-style",
|
|
16
17
|
"lint": "bun run lint:js && bun run lint:css",
|
|
17
18
|
"format": "wp-scripts format",
|
|
18
|
-
"typecheck": "bun run sync
|
|
19
|
+
"typecheck": "bun run sync --check && tsc --noEmit"
|
|
19
20
|
},
|
|
20
21
|
"devDependencies": {
|
|
21
22
|
"@wp-typia/block-runtime": "{{blockRuntimePackageVersion}}",
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
interface SyncCliOptions {
|
|
7
|
+
check: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function parseCliOptions( argv: string[] ): SyncCliOptions {
|
|
11
|
+
const options: SyncCliOptions = {
|
|
12
|
+
check: false,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
for ( const argument of argv ) {
|
|
16
|
+
if ( argument === '--check' ) {
|
|
17
|
+
options.check = true;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
throw new Error( `Unknown sync flag: ${ argument }` );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return options;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getSyncScriptEnv() {
|
|
28
|
+
const binaryDirectory = path.join( process.cwd(), 'node_modules', '.bin' );
|
|
29
|
+
const inheritedPath =
|
|
30
|
+
process.env.PATH ??
|
|
31
|
+
process.env.Path ??
|
|
32
|
+
Object.entries( process.env ).find(
|
|
33
|
+
( [ key ] ) => key.toLowerCase() === 'path'
|
|
34
|
+
)?.[ 1 ] ??
|
|
35
|
+
'';
|
|
36
|
+
const nextPath = fs.existsSync( binaryDirectory )
|
|
37
|
+
? `${ binaryDirectory }${ path.delimiter }${ inheritedPath }`
|
|
38
|
+
: inheritedPath;
|
|
39
|
+
const env: NodeJS.ProcessEnv = {
|
|
40
|
+
...process.env,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
for ( const key of Object.keys( env ) ) {
|
|
44
|
+
if ( key.toLowerCase() === 'path' ) {
|
|
45
|
+
delete env[ key ];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
env.PATH = nextPath;
|
|
50
|
+
|
|
51
|
+
return env;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function runSyncScript( scriptPath: string, options: SyncCliOptions ) {
|
|
55
|
+
const args = [ scriptPath ];
|
|
56
|
+
if ( options.check ) {
|
|
57
|
+
args.push( '--check' );
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = spawnSync( 'tsx', args, {
|
|
61
|
+
cwd: process.cwd(),
|
|
62
|
+
env: getSyncScriptEnv(),
|
|
63
|
+
shell: process.platform === 'win32',
|
|
64
|
+
stdio: 'inherit',
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
if ( result.error ) {
|
|
68
|
+
if ( ( result.error as NodeJS.ErrnoException ).code === 'ENOENT' ) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
'Unable to resolve `tsx` for project sync. Install project dependencies or rerun the command through your package manager.'
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
throw result.error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if ( result.status !== 0 ) {
|
|
78
|
+
throw new Error( `Sync script failed: ${ scriptPath }` );
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function main() {
|
|
83
|
+
const options = parseCliOptions( process.argv.slice( 2 ) );
|
|
84
|
+
const syncTypesScriptPath = path.join( 'scripts', 'sync-types-to-block-json.ts' );
|
|
85
|
+
const syncRestScriptPath = path.join( 'scripts', 'sync-rest-contracts.ts' );
|
|
86
|
+
|
|
87
|
+
runSyncScript( syncTypesScriptPath, options );
|
|
88
|
+
|
|
89
|
+
if ( fs.existsSync( path.resolve( process.cwd(), syncRestScriptPath ) ) ) {
|
|
90
|
+
runSyncScript( syncRestScriptPath, options );
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(
|
|
94
|
+
options.check
|
|
95
|
+
? '✅ Generated project metadata and REST artifacts are already synchronized.'
|
|
96
|
+
: '✅ Generated project metadata and REST artifacts were synchronized.'
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
main().catch( ( error ) => {
|
|
101
|
+
console.error( '❌ Project sync failed:', error );
|
|
102
|
+
process.exit( 1 );
|
|
103
|
+
} );
|
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
"main": "build/index.js",
|
|
9
9
|
"scripts": {
|
|
10
10
|
"add-child": "tsx scripts/add-compound-child.ts",
|
|
11
|
+
"sync": "tsx scripts/sync-project.ts",
|
|
11
12
|
"sync-types": "tsx scripts/sync-types-to-block-json.ts",
|
|
12
|
-
"build": "bun run sync
|
|
13
|
-
"start": "bun run sync
|
|
13
|
+
"build": "bun run sync --check && wp-scripts build --experimental-modules",
|
|
14
|
+
"start": "bun run sync && wp-scripts start --experimental-modules",
|
|
14
15
|
"dev": "bun run start",
|
|
15
|
-
"typecheck": "bun run sync
|
|
16
|
+
"typecheck": "bun run sync --check && tsc --noEmit",
|
|
16
17
|
"lint:js": "wp-scripts lint-js",
|
|
17
18
|
"lint:css": "wp-scripts lint-style",
|
|
18
19
|
"lint": "bun run lint:js && bun run lint:css",
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
interface SyncCliOptions {
|
|
7
|
+
check: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function parseCliOptions( argv: string[] ): SyncCliOptions {
|
|
11
|
+
const options: SyncCliOptions = {
|
|
12
|
+
check: false,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
for ( const argument of argv ) {
|
|
16
|
+
if ( argument === '--check' ) {
|
|
17
|
+
options.check = true;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
throw new Error( `Unknown sync flag: ${ argument }` );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return options;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getSyncScriptEnv() {
|
|
28
|
+
const binaryDirectory = path.join( process.cwd(), 'node_modules', '.bin' );
|
|
29
|
+
const inheritedPath =
|
|
30
|
+
process.env.PATH ??
|
|
31
|
+
process.env.Path ??
|
|
32
|
+
Object.entries( process.env ).find(
|
|
33
|
+
( [ key ] ) => key.toLowerCase() === 'path'
|
|
34
|
+
)?.[ 1 ] ??
|
|
35
|
+
'';
|
|
36
|
+
const nextPath = fs.existsSync( binaryDirectory )
|
|
37
|
+
? `${ binaryDirectory }${ path.delimiter }${ inheritedPath }`
|
|
38
|
+
: inheritedPath;
|
|
39
|
+
const env: NodeJS.ProcessEnv = {
|
|
40
|
+
...process.env,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
for ( const key of Object.keys( env ) ) {
|
|
44
|
+
if ( key.toLowerCase() === 'path' ) {
|
|
45
|
+
delete env[ key ];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
env.PATH = nextPath;
|
|
50
|
+
|
|
51
|
+
return env;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function runSyncScript( scriptPath: string, options: SyncCliOptions ) {
|
|
55
|
+
const args = [ scriptPath ];
|
|
56
|
+
if ( options.check ) {
|
|
57
|
+
args.push( '--check' );
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = spawnSync( 'tsx', args, {
|
|
61
|
+
cwd: process.cwd(),
|
|
62
|
+
env: getSyncScriptEnv(),
|
|
63
|
+
shell: process.platform === 'win32',
|
|
64
|
+
stdio: 'inherit',
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
if ( result.error ) {
|
|
68
|
+
if ( ( result.error as NodeJS.ErrnoException ).code === 'ENOENT' ) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
'Unable to resolve `tsx` for project sync. Install project dependencies or rerun the command through your package manager.'
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
throw result.error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if ( result.status !== 0 ) {
|
|
78
|
+
throw new Error( `Sync script failed: ${ scriptPath }` );
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function main() {
|
|
83
|
+
const options = parseCliOptions( process.argv.slice( 2 ) );
|
|
84
|
+
const syncTypesScriptPath = path.join( 'scripts', 'sync-types-to-block-json.ts' );
|
|
85
|
+
const syncRestScriptPath = path.join( 'scripts', 'sync-rest-contracts.ts' );
|
|
86
|
+
|
|
87
|
+
runSyncScript( syncTypesScriptPath, options );
|
|
88
|
+
|
|
89
|
+
if ( fs.existsSync( path.resolve( process.cwd(), syncRestScriptPath ) ) ) {
|
|
90
|
+
runSyncScript( syncRestScriptPath, options );
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(
|
|
94
|
+
options.check
|
|
95
|
+
? '✅ Generated project metadata and REST artifacts are already synchronized.'
|
|
96
|
+
: '✅ Generated project metadata and REST artifacts were synchronized.'
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
main().catch( ( error ) => {
|
|
101
|
+
console.error( '❌ Project sync failed:', error );
|
|
102
|
+
process.exit( 1 );
|
|
103
|
+
} );
|
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
"main": "build/index.js",
|
|
9
9
|
"scripts": {
|
|
10
10
|
"add-child": "tsx scripts/add-compound-child.ts",
|
|
11
|
+
"sync": "tsx scripts/sync-project.ts",
|
|
11
12
|
"sync-types": "tsx scripts/sync-types-to-block-json.ts",
|
|
12
13
|
"sync-rest": "tsx scripts/sync-rest-contracts.ts",
|
|
13
|
-
"build": "bun run sync
|
|
14
|
-
"start": "bun run sync
|
|
14
|
+
"build": "bun run sync --check && wp-scripts build --experimental-modules",
|
|
15
|
+
"start": "bun run sync && wp-scripts start --experimental-modules",
|
|
15
16
|
"dev": "bun run start",
|
|
16
|
-
"typecheck": "bun run sync
|
|
17
|
+
"typecheck": "bun run sync --check && tsc --noEmit",
|
|
17
18
|
"lint:js": "wp-scripts lint-js",
|
|
18
19
|
"lint:css": "wp-scripts lint-style",
|
|
19
20
|
"lint": "bun run lint:js && bun run lint:css",
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
interface SyncCliOptions {
|
|
7
|
+
check: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function parseCliOptions( argv: string[] ): SyncCliOptions {
|
|
11
|
+
const options: SyncCliOptions = {
|
|
12
|
+
check: false,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
for ( const argument of argv ) {
|
|
16
|
+
if ( argument === '--check' ) {
|
|
17
|
+
options.check = true;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
throw new Error( `Unknown sync flag: ${ argument }` );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return options;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getSyncScriptEnv() {
|
|
28
|
+
const binaryDirectory = path.join( process.cwd(), 'node_modules', '.bin' );
|
|
29
|
+
const inheritedPath =
|
|
30
|
+
process.env.PATH ??
|
|
31
|
+
process.env.Path ??
|
|
32
|
+
Object.entries( process.env ).find(
|
|
33
|
+
( [ key ] ) => key.toLowerCase() === 'path'
|
|
34
|
+
)?.[ 1 ] ??
|
|
35
|
+
'';
|
|
36
|
+
const nextPath = fs.existsSync( binaryDirectory )
|
|
37
|
+
? `${ binaryDirectory }${ path.delimiter }${ inheritedPath }`
|
|
38
|
+
: inheritedPath;
|
|
39
|
+
const env: NodeJS.ProcessEnv = {
|
|
40
|
+
...process.env,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
for ( const key of Object.keys( env ) ) {
|
|
44
|
+
if ( key.toLowerCase() === 'path' ) {
|
|
45
|
+
delete env[ key ];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
env.PATH = nextPath;
|
|
50
|
+
|
|
51
|
+
return env;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function runSyncScript( scriptPath: string, options: SyncCliOptions ) {
|
|
55
|
+
const args = [ scriptPath ];
|
|
56
|
+
if ( options.check ) {
|
|
57
|
+
args.push( '--check' );
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = spawnSync( 'tsx', args, {
|
|
61
|
+
cwd: process.cwd(),
|
|
62
|
+
env: getSyncScriptEnv(),
|
|
63
|
+
shell: process.platform === 'win32',
|
|
64
|
+
stdio: 'inherit',
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
if ( result.error ) {
|
|
68
|
+
if ( ( result.error as NodeJS.ErrnoException ).code === 'ENOENT' ) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
'Unable to resolve `tsx` for project sync. Install project dependencies or rerun the command through your package manager.'
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
throw result.error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if ( result.status !== 0 ) {
|
|
78
|
+
throw new Error( `Sync script failed: ${ scriptPath }` );
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function main() {
|
|
83
|
+
const options = parseCliOptions( process.argv.slice( 2 ) );
|
|
84
|
+
const syncTypesScriptPath = path.join( 'scripts', 'sync-types-to-block-json.ts' );
|
|
85
|
+
const syncRestScriptPath = path.join( 'scripts', 'sync-rest-contracts.ts' );
|
|
86
|
+
|
|
87
|
+
runSyncScript( syncTypesScriptPath, options );
|
|
88
|
+
|
|
89
|
+
if ( fs.existsSync( path.resolve( process.cwd(), syncRestScriptPath ) ) ) {
|
|
90
|
+
runSyncScript( syncRestScriptPath, options );
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(
|
|
94
|
+
options.check
|
|
95
|
+
? '✅ Generated project metadata and REST artifacts are already synchronized.'
|
|
96
|
+
: '✅ Generated project metadata and REST artifacts were synchronized.'
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
main().catch( ( error ) => {
|
|
101
|
+
console.error( '❌ Project sync failed:', error );
|
|
102
|
+
process.exit( 1 );
|
|
103
|
+
} );
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
runSyncBlockMetadata,
|
|
5
6
|
syncEndpointClient,
|
|
6
7
|
syncRestOpenApi,
|
|
7
8
|
syncTypeSchemas,
|
|
@@ -26,6 +27,31 @@ function parseCliOptions( argv: string[] ) {
|
|
|
26
27
|
return options;
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
async function assertTypeArtifactsCurrent( block: ( typeof BLOCKS )[ number ] ) {
|
|
31
|
+
const report = await runSyncBlockMetadata( {
|
|
32
|
+
blockJsonFile: path.join( 'src', 'blocks', block.slug, 'block.json' ),
|
|
33
|
+
jsonSchemaFile: path.join( 'src', 'blocks', block.slug, 'typia.schema.json' ),
|
|
34
|
+
manifestFile: path.join( 'src', 'blocks', block.slug, 'typia.manifest.json' ),
|
|
35
|
+
openApiFile: path.join( 'src', 'blocks', block.slug, 'typia.openapi.json' ),
|
|
36
|
+
sourceTypeName: block.attributeTypeName,
|
|
37
|
+
typesFile: block.typesFile,
|
|
38
|
+
}, {
|
|
39
|
+
check: true,
|
|
40
|
+
} );
|
|
41
|
+
|
|
42
|
+
if ( report.failure?.code === 'stale-generated-artifact' ) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`${ block.slug }: ${ report.failure.message }\nRun \`sync\` or \`sync-types\` first to refresh type-derived artifacts before rerunning \`sync-rest\`.`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if ( report.failure ) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`${ block.slug }: type-derived artifact preflight failed before sync-rest.\n${ report.failure.message }`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
29
55
|
async function main() {
|
|
30
56
|
const options = parseCliOptions( process.argv.slice( 2 ) );
|
|
31
57
|
|
|
@@ -39,6 +65,8 @@ async function main() {
|
|
|
39
65
|
continue;
|
|
40
66
|
}
|
|
41
67
|
|
|
68
|
+
await assertTypeArtifactsCurrent( block );
|
|
69
|
+
|
|
42
70
|
for ( const [ baseName, contract ] of Object.entries( block.restManifest.contracts ) ) {
|
|
43
71
|
await syncTypeSchemas( {
|
|
44
72
|
jsonSchemaFile: path.join(
|
|
@@ -7,16 +7,17 @@
|
|
|
7
7
|
"license": "GPL-2.0-or-later",
|
|
8
8
|
"main": "build/index.js",
|
|
9
9
|
"scripts": {
|
|
10
|
+
"sync": "tsx scripts/sync-project.ts",
|
|
10
11
|
"sync-types": "tsx scripts/sync-types-to-block-json.ts",
|
|
11
12
|
"sync-rest": "tsx scripts/sync-rest-contracts.ts",
|
|
12
|
-
"build": "bun run sync
|
|
13
|
-
"start": "bun run sync
|
|
13
|
+
"build": "bun run sync --check && wp-scripts build --experimental-modules",
|
|
14
|
+
"start": "bun run sync && wp-scripts start --experimental-modules",
|
|
14
15
|
"dev": "bun run start",
|
|
15
16
|
"lint:js": "wp-scripts lint-js",
|
|
16
17
|
"lint:css": "wp-scripts lint-style",
|
|
17
18
|
"lint": "bun run lint:js && bun run lint:css",
|
|
18
19
|
"format": "wp-scripts format",
|
|
19
|
-
"typecheck": "bun run sync
|
|
20
|
+
"typecheck": "bun run sync --check && tsc --noEmit"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
|
22
23
|
"@wp-typia/block-runtime": "{{blockRuntimePackageVersion}}",
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
interface SyncCliOptions {
|
|
7
|
+
check: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function parseCliOptions( argv: string[] ): SyncCliOptions {
|
|
11
|
+
const options: SyncCliOptions = {
|
|
12
|
+
check: false,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
for ( const argument of argv ) {
|
|
16
|
+
if ( argument === '--check' ) {
|
|
17
|
+
options.check = true;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
throw new Error( `Unknown sync flag: ${ argument }` );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return options;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getSyncScriptEnv() {
|
|
28
|
+
const binaryDirectory = path.join( process.cwd(), 'node_modules', '.bin' );
|
|
29
|
+
const inheritedPath =
|
|
30
|
+
process.env.PATH ??
|
|
31
|
+
process.env.Path ??
|
|
32
|
+
Object.entries( process.env ).find(
|
|
33
|
+
( [ key ] ) => key.toLowerCase() === 'path'
|
|
34
|
+
)?.[ 1 ] ??
|
|
35
|
+
'';
|
|
36
|
+
const nextPath = fs.existsSync( binaryDirectory )
|
|
37
|
+
? `${ binaryDirectory }${ path.delimiter }${ inheritedPath }`
|
|
38
|
+
: inheritedPath;
|
|
39
|
+
const env: NodeJS.ProcessEnv = {
|
|
40
|
+
...process.env,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
for ( const key of Object.keys( env ) ) {
|
|
44
|
+
if ( key.toLowerCase() === 'path' ) {
|
|
45
|
+
delete env[ key ];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
env.PATH = nextPath;
|
|
50
|
+
|
|
51
|
+
return env;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function runSyncScript( scriptPath: string, options: SyncCliOptions ) {
|
|
55
|
+
const args = [ scriptPath ];
|
|
56
|
+
if ( options.check ) {
|
|
57
|
+
args.push( '--check' );
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = spawnSync( 'tsx', args, {
|
|
61
|
+
cwd: process.cwd(),
|
|
62
|
+
env: getSyncScriptEnv(),
|
|
63
|
+
shell: process.platform === 'win32',
|
|
64
|
+
stdio: 'inherit',
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
if ( result.error ) {
|
|
68
|
+
if ( ( result.error as NodeJS.ErrnoException ).code === 'ENOENT' ) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
'Unable to resolve `tsx` for project sync. Install project dependencies or rerun the command through your package manager.'
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
throw result.error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if ( result.status !== 0 ) {
|
|
78
|
+
throw new Error( `Sync script failed: ${ scriptPath }` );
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function main() {
|
|
83
|
+
const options = parseCliOptions( process.argv.slice( 2 ) );
|
|
84
|
+
const syncTypesScriptPath = path.join( 'scripts', 'sync-types-to-block-json.ts' );
|
|
85
|
+
const syncRestScriptPath = path.join( 'scripts', 'sync-rest-contracts.ts' );
|
|
86
|
+
|
|
87
|
+
runSyncScript( syncTypesScriptPath, options );
|
|
88
|
+
|
|
89
|
+
if ( fs.existsSync( path.resolve( process.cwd(), syncRestScriptPath ) ) ) {
|
|
90
|
+
runSyncScript( syncRestScriptPath, options );
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(
|
|
94
|
+
options.check
|
|
95
|
+
? '✅ Generated project metadata and REST artifacts are already synchronized.'
|
|
96
|
+
: '✅ Generated project metadata and REST artifacts were synchronized.'
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
main().catch( ( error ) => {
|
|
101
|
+
console.error( '❌ Project sync failed:', error );
|
|
102
|
+
process.exit( 1 );
|
|
103
|
+
} );
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import {
|
|
3
3
|
defineEndpointManifest,
|
|
4
|
+
runSyncBlockMetadata,
|
|
4
5
|
syncEndpointClient,
|
|
5
6
|
syncRestOpenApi,
|
|
6
7
|
syncTypeSchemas,
|
|
@@ -23,6 +24,31 @@ function parseCliOptions( argv: string[] ) {
|
|
|
23
24
|
return options;
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
async function assertTypeArtifactsCurrent() {
|
|
28
|
+
const report = await runSyncBlockMetadata( {
|
|
29
|
+
blockJsonFile: 'src/block.json',
|
|
30
|
+
jsonSchemaFile: 'src/typia.schema.json',
|
|
31
|
+
manifestFile: 'src/typia.manifest.json',
|
|
32
|
+
openApiFile: 'src/typia.openapi.json',
|
|
33
|
+
sourceTypeName: '{{pascalCase}}Attributes',
|
|
34
|
+
typesFile: 'src/types.ts',
|
|
35
|
+
}, {
|
|
36
|
+
check: true,
|
|
37
|
+
} );
|
|
38
|
+
|
|
39
|
+
if ( report.failure?.code === 'stale-generated-artifact' ) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`${ report.failure.message }\nRun \`sync\` or \`sync-types\` first to refresh type-derived artifacts before rerunning \`sync-rest\`.`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if ( report.failure ) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Type-derived artifact preflight failed before sync-rest.\n${ report.failure.message }`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
26
52
|
const REST_ENDPOINT_MANIFEST = defineEndpointManifest( {
|
|
27
53
|
contracts: {
|
|
28
54
|
'state-query': {
|
|
@@ -85,6 +111,8 @@ const REST_ENDPOINT_MANIFEST = defineEndpointManifest( {
|
|
|
85
111
|
async function main() {
|
|
86
112
|
const options = parseCliOptions( process.argv.slice( 2 ) );
|
|
87
113
|
|
|
114
|
+
await assertTypeArtifactsCurrent();
|
|
115
|
+
|
|
88
116
|
for ( const [ baseName, contract ] of Object.entries( REST_ENDPOINT_MANIFEST.contracts ) ) {
|
|
89
117
|
await syncTypeSchemas( {
|
|
90
118
|
jsonSchemaFile: `src/api-schemas/${ baseName }.schema.json`,
|
|
@@ -7,15 +7,16 @@
|
|
|
7
7
|
"license": "GPL-2.0-or-later",
|
|
8
8
|
"main": "build/index.js",
|
|
9
9
|
"scripts": {
|
|
10
|
+
"sync": "tsx scripts/sync-project.ts",
|
|
10
11
|
"sync-types": "tsx scripts/sync-types-to-block-json.ts",
|
|
11
|
-
"build": "bun run sync
|
|
12
|
-
"start": "bun run sync
|
|
12
|
+
"build": "bun run sync --check && wp-scripts build --experimental-modules",
|
|
13
|
+
"start": "bun run sync && wp-scripts start --experimental-modules",
|
|
13
14
|
"dev": "bun run start",
|
|
14
15
|
"lint:js": "wp-scripts lint-js",
|
|
15
16
|
"lint:css": "wp-scripts lint-style",
|
|
16
17
|
"lint": "bun run lint:js && bun run lint:css",
|
|
17
18
|
"format": "wp-scripts format",
|
|
18
|
-
"typecheck": "bun run sync
|
|
19
|
+
"typecheck": "bun run sync --check && tsc --noEmit"
|
|
19
20
|
},
|
|
20
21
|
"devDependencies": {
|
|
21
22
|
"@wp-typia/block-runtime": "{{blockRuntimePackageVersion}}",
|