@wp-typia/project-tools 0.19.1 → 0.19.3
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/built-in-block-non-ts-artifacts.js +1 -1039
- package/dist/runtime/built-in-block-non-ts-family-artifacts.d.ts +30 -0
- package/dist/runtime/built-in-block-non-ts-family-artifacts.js +1035 -0
- package/dist/runtime/built-in-block-non-ts-render-utils.d.ts +27 -0
- package/dist/runtime/built-in-block-non-ts-render-utils.js +51 -0
- package/dist/runtime/cli-add-workspace-rest-anchors.d.ts +3 -0
- package/dist/runtime/cli-add-workspace-rest-anchors.js +188 -0
- package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +7 -0
- package/dist/runtime/cli-add-workspace-rest-source-emitters.js +379 -0
- package/dist/runtime/cli-add-workspace-rest.js +5 -564
- package/dist/runtime/cli-diagnostics.d.ts +2 -0
- package/dist/runtime/cli-diagnostics.js +10 -1
- package/dist/runtime/cli-help.js +3 -0
- package/dist/runtime/cli-init.d.ts +49 -0
- package/dist/runtime/cli-init.js +354 -0
- package/dist/runtime/cli-scaffold.d.ts +1 -0
- package/dist/runtime/cli-scaffold.js +5 -1
- package/dist/runtime/cli-templates.js +36 -6
- package/dist/runtime/external-template-guards.d.ts +31 -0
- package/dist/runtime/external-template-guards.js +132 -0
- package/dist/runtime/package-managers.d.ts +8 -0
- package/dist/runtime/package-managers.js +13 -0
- package/dist/runtime/package-versions.d.ts +8 -0
- package/dist/runtime/package-versions.js +84 -19
- package/dist/runtime/scaffold-documents.js +19 -1
- package/dist/runtime/scaffold-onboarding.d.ts +4 -0
- package/dist/runtime/scaffold-onboarding.js +25 -1
- package/dist/runtime/scaffold.js +2 -5
- package/dist/runtime/template-registry.d.ts +23 -1
- package/dist/runtime/template-registry.js +37 -3
- package/dist/runtime/template-source-external.js +9 -3
- package/dist/runtime/template-source-remote.js +5 -0
- package/dist/runtime/template-source-seeds.js +40 -6
- package/package.json +7 -2
- package/templates/_shared/base/package.json.mustache +0 -1
- package/templates/_shared/compound/core/package.json.mustache +0 -1
- package/templates/_shared/compound/persistence/package.json.mustache +0 -1
- package/templates/_shared/persistence/core/package.json.mustache +0 -1
- package/templates/interactivity/package.json.mustache +0 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type PackageManagerId } from "./package-managers.js";
|
|
2
|
+
type InitPlanAction = "add" | "update";
|
|
3
|
+
type InitPlanStatus = "already-initialized" | "preview";
|
|
4
|
+
type InitPlanLayoutKind = "generated-project" | "multi-block" | "official-workspace" | "single-block" | "unsupported";
|
|
5
|
+
interface InitDependencyChange {
|
|
6
|
+
action: InitPlanAction;
|
|
7
|
+
currentValue?: string;
|
|
8
|
+
name: string;
|
|
9
|
+
requiredValue: string;
|
|
10
|
+
}
|
|
11
|
+
interface InitScriptChange {
|
|
12
|
+
action: InitPlanAction;
|
|
13
|
+
currentValue?: string;
|
|
14
|
+
name: string;
|
|
15
|
+
requiredValue: string;
|
|
16
|
+
}
|
|
17
|
+
interface InitPackageManagerFieldChange {
|
|
18
|
+
action: InitPlanAction;
|
|
19
|
+
currentValue?: string;
|
|
20
|
+
requiredValue: string;
|
|
21
|
+
}
|
|
22
|
+
interface InitFilePlan {
|
|
23
|
+
path: string;
|
|
24
|
+
purpose: string;
|
|
25
|
+
}
|
|
26
|
+
export interface RetrofitInitPlan {
|
|
27
|
+
commandMode: "preview-only";
|
|
28
|
+
detectedLayout: {
|
|
29
|
+
blockNames: string[];
|
|
30
|
+
description: string;
|
|
31
|
+
kind: InitPlanLayoutKind;
|
|
32
|
+
};
|
|
33
|
+
generatedArtifacts: string[];
|
|
34
|
+
nextSteps: string[];
|
|
35
|
+
notes: string[];
|
|
36
|
+
packageChanges: {
|
|
37
|
+
addDevDependencies: InitDependencyChange[];
|
|
38
|
+
packageManagerField?: InitPackageManagerFieldChange;
|
|
39
|
+
scripts: InitScriptChange[];
|
|
40
|
+
};
|
|
41
|
+
plannedFiles: InitFilePlan[];
|
|
42
|
+
packageManager: PackageManagerId;
|
|
43
|
+
projectDir: string;
|
|
44
|
+
projectName: string;
|
|
45
|
+
status: InitPlanStatus;
|
|
46
|
+
summary: string;
|
|
47
|
+
}
|
|
48
|
+
export declare function getInitPlan(projectDir: string): RetrofitInitPlan;
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { discoverMigrationInitLayout } from "./migration-project.js";
|
|
4
|
+
import { formatAddDevDependenciesCommand, formatPackageExecCommand, formatRunScript, getPackageManager, transformPackageManagerText, } from "./package-managers.js";
|
|
5
|
+
import { getPackageVersions } from "./package-versions.js";
|
|
6
|
+
import { parseWorkspacePackageManagerId, tryResolveWorkspaceProject, } from "./workspace-project.js";
|
|
7
|
+
const SUPPORTED_RETROFIT_LAYOUT_NOTE = "Supported retrofit layouts currently mirror the migration bootstrap detector: `src/block.json` + `src/types.ts` + `src/save.tsx`, legacy root `block.json` + `src/types.ts` + `src/save.tsx`, or multi-block `src/blocks/*/block.json` workspaces.";
|
|
8
|
+
const BASE_RETROFIT_SCRIPTS = {
|
|
9
|
+
sync: "tsx scripts/sync-project.ts",
|
|
10
|
+
"sync-types": "tsx scripts/sync-types-to-block-json.ts",
|
|
11
|
+
typecheck: "bun run sync --check && tsc --noEmit",
|
|
12
|
+
};
|
|
13
|
+
const BASE_RETROFIT_DEV_DEPENDENCIES = [
|
|
14
|
+
"@typia/unplugin",
|
|
15
|
+
"@wp-typia/block-runtime",
|
|
16
|
+
"@wp-typia/block-types",
|
|
17
|
+
"tsx",
|
|
18
|
+
"typescript",
|
|
19
|
+
"typia",
|
|
20
|
+
];
|
|
21
|
+
function normalizeRelativePath(value) {
|
|
22
|
+
return value.replace(/\\/gu, "/");
|
|
23
|
+
}
|
|
24
|
+
function readProjectPackageJson(projectDir) {
|
|
25
|
+
const packageJsonPath = path.join(projectDir, "package.json");
|
|
26
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
30
|
+
}
|
|
31
|
+
function inferInitPackageManager(projectDir, packageJson) {
|
|
32
|
+
if (packageJson?.packageManager) {
|
|
33
|
+
return parseWorkspacePackageManagerId(packageJson.packageManager);
|
|
34
|
+
}
|
|
35
|
+
if (fs.existsSync(path.join(projectDir, "bun.lock")) ||
|
|
36
|
+
fs.existsSync(path.join(projectDir, "bun.lockb"))) {
|
|
37
|
+
return "bun";
|
|
38
|
+
}
|
|
39
|
+
if (fs.existsSync(path.join(projectDir, "pnpm-lock.yaml"))) {
|
|
40
|
+
return "pnpm";
|
|
41
|
+
}
|
|
42
|
+
if (fs.existsSync(path.join(projectDir, "yarn.lock")) ||
|
|
43
|
+
fs.existsSync(path.join(projectDir, ".yarnrc.yml"))) {
|
|
44
|
+
return "yarn";
|
|
45
|
+
}
|
|
46
|
+
return "npm";
|
|
47
|
+
}
|
|
48
|
+
function getWpTypiaCliSpecifier() {
|
|
49
|
+
const versions = getPackageVersions();
|
|
50
|
+
return versions.wpTypiaPackageExactVersion === "0.0.0"
|
|
51
|
+
? "wp-typia"
|
|
52
|
+
: `wp-typia@${versions.wpTypiaPackageExactVersion}`;
|
|
53
|
+
}
|
|
54
|
+
function buildRequiredDevDependencyMap() {
|
|
55
|
+
const versions = getPackageVersions();
|
|
56
|
+
return {
|
|
57
|
+
"@typia/unplugin": "^12.0.1",
|
|
58
|
+
"@wp-typia/block-runtime": versions.blockRuntimePackageVersion,
|
|
59
|
+
"@wp-typia/block-types": versions.blockTypesPackageVersion,
|
|
60
|
+
tsx: "^4.20.5",
|
|
61
|
+
typescript: "^5.9.2",
|
|
62
|
+
typia: "^12.0.1",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function getExistingDependencyVersion(packageJson, name) {
|
|
66
|
+
return packageJson?.devDependencies?.[name] ?? packageJson?.dependencies?.[name];
|
|
67
|
+
}
|
|
68
|
+
function buildDependencyChanges(packageJson) {
|
|
69
|
+
const requiredDependencies = buildRequiredDevDependencyMap();
|
|
70
|
+
return BASE_RETROFIT_DEV_DEPENDENCIES.flatMap((name) => {
|
|
71
|
+
const requiredValue = requiredDependencies[name];
|
|
72
|
+
const currentValue = getExistingDependencyVersion(packageJson, name);
|
|
73
|
+
if (currentValue === requiredValue) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
return [
|
|
77
|
+
{
|
|
78
|
+
action: currentValue ? "update" : "add",
|
|
79
|
+
...(currentValue ? { currentValue } : {}),
|
|
80
|
+
name,
|
|
81
|
+
requiredValue,
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function buildScriptChanges(packageJson, packageManager) {
|
|
87
|
+
const scripts = packageJson?.scripts ?? {};
|
|
88
|
+
return Object.entries(BASE_RETROFIT_SCRIPTS).flatMap(([name, commandSource]) => {
|
|
89
|
+
const requiredValue = transformPackageManagerText(commandSource, packageManager);
|
|
90
|
+
const currentValue = scripts[name];
|
|
91
|
+
if (currentValue === requiredValue) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
return [
|
|
95
|
+
{
|
|
96
|
+
action: typeof currentValue === "string" ? "update" : "add",
|
|
97
|
+
...(typeof currentValue === "string" ? { currentValue } : {}),
|
|
98
|
+
name,
|
|
99
|
+
requiredValue,
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function buildPackageManagerFieldChange(packageJson, packageManager) {
|
|
105
|
+
if (packageManager === "npm") {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
const requiredValue = getPackageManager(packageManager).packageManagerField;
|
|
109
|
+
const currentValue = packageJson?.packageManager;
|
|
110
|
+
if (currentValue === requiredValue) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
action: typeof currentValue === "string" ? "update" : "add",
|
|
115
|
+
...(typeof currentValue === "string" ? { currentValue } : {}),
|
|
116
|
+
requiredValue,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function buildGeneratedArtifactPaths(blockJsonFile, manifestFile) {
|
|
120
|
+
const manifestDir = path.dirname(manifestFile);
|
|
121
|
+
const artifactPaths = [
|
|
122
|
+
blockJsonFile,
|
|
123
|
+
manifestFile,
|
|
124
|
+
path.join(manifestDir, "typia.schema.json"),
|
|
125
|
+
path.join(manifestDir, "typia-validator.php"),
|
|
126
|
+
path.join(manifestDir, "typia.openapi.json"),
|
|
127
|
+
];
|
|
128
|
+
return Array.from(new Set(artifactPaths.map((filePath) => normalizeRelativePath(filePath))));
|
|
129
|
+
}
|
|
130
|
+
function buildLayoutDetails(projectDir) {
|
|
131
|
+
try {
|
|
132
|
+
const discoveredLayout = discoverMigrationInitLayout(projectDir);
|
|
133
|
+
if (discoveredLayout.mode === "multi") {
|
|
134
|
+
return {
|
|
135
|
+
blockNames: discoveredLayout.blocks.map((block) => block.blockName),
|
|
136
|
+
description: `Detected a supported multi-block retrofit candidate (${discoveredLayout.blocks.length} targets).`,
|
|
137
|
+
generatedArtifacts: discoveredLayout.blocks.flatMap((block) => buildGeneratedArtifactPaths(block.blockJsonFile, block.manifestFile)),
|
|
138
|
+
kind: "multi-block",
|
|
139
|
+
notes: [
|
|
140
|
+
"Migration bootstrap can stay optional. Add it later with `wp-typia migrate init --current-migration-version v1` once the typed sync surface is in place.",
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
blockNames: [discoveredLayout.block.blockName],
|
|
146
|
+
description: "Detected a supported single-block retrofit candidate.",
|
|
147
|
+
generatedArtifacts: buildGeneratedArtifactPaths(discoveredLayout.block.blockJsonFile, discoveredLayout.block.manifestFile),
|
|
148
|
+
kind: "single-block",
|
|
149
|
+
notes: discoveredLayout.block.blockJsonFile === "block.json"
|
|
150
|
+
? [
|
|
151
|
+
"Legacy root `block.json` layouts are still supported for retrofit planning, but newer scaffolds keep generated block metadata under `src/`.",
|
|
152
|
+
]
|
|
153
|
+
: [],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
158
|
+
return {
|
|
159
|
+
blockNames: [],
|
|
160
|
+
description: "No supported retrofit layout was auto-detected yet.",
|
|
161
|
+
generatedArtifacts: [],
|
|
162
|
+
kind: "unsupported",
|
|
163
|
+
notes: [message, SUPPORTED_RETROFIT_LAYOUT_NOTE],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function hasExistingWpTypiaProjectSurface(packageJson) {
|
|
168
|
+
const scripts = packageJson?.scripts ?? {};
|
|
169
|
+
const hasSyncSurface = typeof scripts.sync === "string" || typeof scripts["sync-types"] === "string";
|
|
170
|
+
const hasRuntimeDeps = typeof getExistingDependencyVersion(packageJson, "@wp-typia/block-runtime") ===
|
|
171
|
+
"string" &&
|
|
172
|
+
typeof getExistingDependencyVersion(packageJson, "@wp-typia/block-types") ===
|
|
173
|
+
"string";
|
|
174
|
+
return hasSyncSurface && hasRuntimeDeps;
|
|
175
|
+
}
|
|
176
|
+
function buildPlannedFiles(layoutKind) {
|
|
177
|
+
const plannedFiles = [
|
|
178
|
+
{
|
|
179
|
+
path: "scripts/sync-types-to-block-json.ts",
|
|
180
|
+
purpose: "Generate block.json and Typia metadata artifacts from the current TypeScript source of truth.",
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
path: "scripts/sync-project.ts",
|
|
184
|
+
purpose: "Provide one shared sync entrypoint that can grow into sync-rest or workspace-aware refresh steps later.",
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
if (layoutKind === "unsupported") {
|
|
188
|
+
plannedFiles.unshift({
|
|
189
|
+
path: "package.json",
|
|
190
|
+
purpose: "Add the minimum wp-typia devDependencies and scripts once the project matches a supported retrofit layout.",
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return plannedFiles;
|
|
194
|
+
}
|
|
195
|
+
function buildChangeSummary(changes) {
|
|
196
|
+
const lines = [];
|
|
197
|
+
for (const dependencyChange of changes.packageChanges.addDevDependencies) {
|
|
198
|
+
lines.push(`devDependency ${dependencyChange.action} ${dependencyChange.name} -> ${dependencyChange.requiredValue}`);
|
|
199
|
+
}
|
|
200
|
+
if (changes.packageChanges.packageManagerField) {
|
|
201
|
+
lines.push(`packageManager ${changes.packageChanges.packageManagerField.action} -> ${changes.packageChanges.packageManagerField.requiredValue}`);
|
|
202
|
+
}
|
|
203
|
+
for (const scriptChange of changes.packageChanges.scripts) {
|
|
204
|
+
lines.push(`script ${scriptChange.action} ${scriptChange.name} -> ${scriptChange.requiredValue}`);
|
|
205
|
+
}
|
|
206
|
+
for (const filePlan of changes.plannedFiles) {
|
|
207
|
+
lines.push(`file add ${filePlan.path} (${filePlan.purpose})`);
|
|
208
|
+
}
|
|
209
|
+
for (const artifactPath of changes.generatedArtifacts) {
|
|
210
|
+
lines.push(`generated artifact ${artifactPath}`);
|
|
211
|
+
}
|
|
212
|
+
return lines;
|
|
213
|
+
}
|
|
214
|
+
function buildNextSteps(options) {
|
|
215
|
+
const cliSpecifier = getWpTypiaCliSpecifier();
|
|
216
|
+
const syncTypesRun = formatRunScript(options.packageManager, "sync-types");
|
|
217
|
+
const syncRun = formatRunScript(options.packageManager, "sync");
|
|
218
|
+
const doctorRun = formatPackageExecCommand(options.packageManager, cliSpecifier, "doctor");
|
|
219
|
+
const migrationInitRun = formatPackageExecCommand(options.packageManager, cliSpecifier, "migrate init --current-migration-version v1");
|
|
220
|
+
const dependencyInstallCommand = formatAddDevDependenciesCommand(options.packageManager, buildRequiredDevDependencyMapEntries());
|
|
221
|
+
if (options.layoutKind === "unsupported") {
|
|
222
|
+
return [
|
|
223
|
+
"Align the project to one of the supported retrofit layouts listed below, then rerun `wp-typia init`.",
|
|
224
|
+
dependencyInstallCommand,
|
|
225
|
+
syncTypesRun,
|
|
226
|
+
doctorRun,
|
|
227
|
+
];
|
|
228
|
+
}
|
|
229
|
+
const steps = [
|
|
230
|
+
...(options.changeSummaryLines.length > 0
|
|
231
|
+
? [
|
|
232
|
+
"Apply the planned package.json changes and helper files listed below.",
|
|
233
|
+
dependencyInstallCommand,
|
|
234
|
+
]
|
|
235
|
+
: []),
|
|
236
|
+
syncRun,
|
|
237
|
+
doctorRun,
|
|
238
|
+
`Optional migration bootstrap: ${migrationInitRun}`,
|
|
239
|
+
];
|
|
240
|
+
return steps;
|
|
241
|
+
}
|
|
242
|
+
function buildRequiredDevDependencyMapEntries() {
|
|
243
|
+
return Object.entries(buildRequiredDevDependencyMap()).map(([name, version]) => `${name}@${version.replace(/^workspace:/u, "")}`);
|
|
244
|
+
}
|
|
245
|
+
export function getInitPlan(projectDir) {
|
|
246
|
+
const resolvedProjectDir = path.resolve(projectDir);
|
|
247
|
+
const packageJson = readProjectPackageJson(resolvedProjectDir);
|
|
248
|
+
const packageManager = inferInitPackageManager(resolvedProjectDir, packageJson);
|
|
249
|
+
const workspace = tryResolveWorkspaceProject(resolvedProjectDir);
|
|
250
|
+
if (workspace) {
|
|
251
|
+
return {
|
|
252
|
+
commandMode: "preview-only",
|
|
253
|
+
detectedLayout: {
|
|
254
|
+
blockNames: [],
|
|
255
|
+
description: "Already an official wp-typia workspace.",
|
|
256
|
+
kind: "official-workspace",
|
|
257
|
+
},
|
|
258
|
+
generatedArtifacts: [],
|
|
259
|
+
nextSteps: [
|
|
260
|
+
formatPackageExecCommand(packageManager, getWpTypiaCliSpecifier(), "doctor"),
|
|
261
|
+
"Use `wp-typia add ...` to extend this workspace instead of rerunning init.",
|
|
262
|
+
],
|
|
263
|
+
notes: [
|
|
264
|
+
"The official workspace template already owns inventory, doctor, and add-command workflows.",
|
|
265
|
+
],
|
|
266
|
+
packageChanges: {
|
|
267
|
+
addDevDependencies: [],
|
|
268
|
+
scripts: [],
|
|
269
|
+
},
|
|
270
|
+
plannedFiles: [],
|
|
271
|
+
packageManager,
|
|
272
|
+
projectDir: workspace.projectDir,
|
|
273
|
+
projectName: workspace.packageName,
|
|
274
|
+
status: "already-initialized",
|
|
275
|
+
summary: "This directory is already an official wp-typia workspace. No retrofit bootstrap is needed.",
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const projectName = typeof packageJson?.name === "string" && packageJson.name.length > 0
|
|
279
|
+
? packageJson.name
|
|
280
|
+
: path.basename(resolvedProjectDir);
|
|
281
|
+
const layout = buildLayoutDetails(resolvedProjectDir);
|
|
282
|
+
const dependencyChanges = buildDependencyChanges(packageJson);
|
|
283
|
+
const scriptChanges = buildScriptChanges(packageJson, packageManager);
|
|
284
|
+
const packageManagerFieldChange = buildPackageManagerFieldChange(packageJson, packageManager);
|
|
285
|
+
const rawPlannedFiles = layout.kind === "generated-project" || layout.kind === "official-workspace"
|
|
286
|
+
? []
|
|
287
|
+
: buildPlannedFiles(layout.kind);
|
|
288
|
+
const hasExistingSurface = hasExistingWpTypiaProjectSurface(packageJson);
|
|
289
|
+
const status = hasExistingSurface &&
|
|
290
|
+
dependencyChanges.length === 0 &&
|
|
291
|
+
scriptChanges.length === 0 &&
|
|
292
|
+
packageManagerFieldChange === undefined
|
|
293
|
+
? "already-initialized"
|
|
294
|
+
: "preview";
|
|
295
|
+
const plannedFiles = status === "already-initialized" ? [] : rawPlannedFiles;
|
|
296
|
+
const detectedLayout = status === "already-initialized" && hasExistingSurface
|
|
297
|
+
? {
|
|
298
|
+
blockNames: layout.blockNames,
|
|
299
|
+
description: layout.kind === "unsupported"
|
|
300
|
+
? "Already exposes the minimum wp-typia sync surface."
|
|
301
|
+
: `Already exposes the minimum wp-typia sync surface for ${layout.kind === "multi-block" ? "a multi-block project" : "a single-block project"}.`,
|
|
302
|
+
kind: "generated-project",
|
|
303
|
+
}
|
|
304
|
+
: {
|
|
305
|
+
blockNames: layout.blockNames,
|
|
306
|
+
description: layout.description,
|
|
307
|
+
kind: layout.kind,
|
|
308
|
+
};
|
|
309
|
+
const plan = {
|
|
310
|
+
commandMode: "preview-only",
|
|
311
|
+
detectedLayout,
|
|
312
|
+
generatedArtifacts: status === "already-initialized" && detectedLayout.kind === "generated-project"
|
|
313
|
+
? []
|
|
314
|
+
: layout.generatedArtifacts,
|
|
315
|
+
nextSteps: buildNextSteps({
|
|
316
|
+
changeSummaryLines: buildChangeSummary({
|
|
317
|
+
generatedArtifacts: status === "already-initialized" &&
|
|
318
|
+
detectedLayout.kind === "generated-project"
|
|
319
|
+
? []
|
|
320
|
+
: layout.generatedArtifacts,
|
|
321
|
+
packageChanges: {
|
|
322
|
+
addDevDependencies: dependencyChanges,
|
|
323
|
+
...(packageManagerFieldChange
|
|
324
|
+
? { packageManagerField: packageManagerFieldChange }
|
|
325
|
+
: {}),
|
|
326
|
+
scripts: scriptChanges,
|
|
327
|
+
},
|
|
328
|
+
plannedFiles,
|
|
329
|
+
}),
|
|
330
|
+
layoutKind: detectedLayout.kind,
|
|
331
|
+
packageManager,
|
|
332
|
+
}),
|
|
333
|
+
notes: Array.from(new Set([
|
|
334
|
+
"Preview only: `wp-typia init` does not write files yet.",
|
|
335
|
+
...layout.notes,
|
|
336
|
+
])),
|
|
337
|
+
packageChanges: {
|
|
338
|
+
addDevDependencies: dependencyChanges,
|
|
339
|
+
...(packageManagerFieldChange
|
|
340
|
+
? { packageManagerField: packageManagerFieldChange }
|
|
341
|
+
: {}),
|
|
342
|
+
scripts: scriptChanges,
|
|
343
|
+
},
|
|
344
|
+
plannedFiles,
|
|
345
|
+
packageManager,
|
|
346
|
+
projectDir: resolvedProjectDir,
|
|
347
|
+
projectName,
|
|
348
|
+
status,
|
|
349
|
+
summary: status === "already-initialized"
|
|
350
|
+
? "This project already exposes the minimum wp-typia retrofit surface."
|
|
351
|
+
: "This command previews the minimum wp-typia adoption layer for the current project without rewriting it into a full scaffold.",
|
|
352
|
+
};
|
|
353
|
+
return plan;
|
|
354
|
+
}
|
|
@@ -7,7 +7,7 @@ import { parseCompoundInnerBlocksPreset } from "./compound-inner-blocks.js";
|
|
|
7
7
|
import { formatInstallCommand, formatRunScript, } from "./package-managers.js";
|
|
8
8
|
import { getPrimaryDevelopmentScript } from "./local-dev-presets.js";
|
|
9
9
|
import { createManagedTempRoot } from "./temp-roots.js";
|
|
10
|
-
import { getOptionalOnboardingNote, getOptionalOnboardingSteps, } from "./scaffold-onboarding.js";
|
|
10
|
+
import { getOptionalOnboardingNote, getOptionalOnboardingShortNote, getOptionalOnboardingSteps, } from "./scaffold-onboarding.js";
|
|
11
11
|
import { formatNonEmptyTargetDirectoryError } from "./scaffold-bootstrap.js";
|
|
12
12
|
import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, isBuiltInTemplateId, } from "./template-registry.js";
|
|
13
13
|
import { resolveOptionalInteractiveExternalLayerId, } from "./external-layer-selection.js";
|
|
@@ -254,6 +254,10 @@ export function getOptionalOnboarding({ availableScripts, packageManager, templa
|
|
|
254
254
|
availableScripts,
|
|
255
255
|
compoundPersistenceEnabled,
|
|
256
256
|
}),
|
|
257
|
+
shortNote: getOptionalOnboardingShortNote(packageManager, templateId, {
|
|
258
|
+
availableScripts,
|
|
259
|
+
compoundPersistenceEnabled,
|
|
260
|
+
}),
|
|
257
261
|
steps: getOptionalOnboardingSteps(packageManager, templateId, {
|
|
258
262
|
availableScripts,
|
|
259
263
|
compoundPersistenceEnabled,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, getTemplateById, getTemplateSelectOptions, isBuiltInTemplateId, listTemplates, } from "./template-registry.js";
|
|
2
|
-
const WORKSPACE_TEMPLATE_ALIAS = "workspace";
|
|
1
|
+
import { OFFICIAL_WORKSPACE_TEMPLATE_ALIAS, OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, getUserFacingTemplateId, getTemplateById, getTemplateSelectOptions, isBuiltInTemplateId, listTemplates, } from "./template-registry.js";
|
|
3
2
|
/**
|
|
4
3
|
* Format one line of template list output for a built-in template.
|
|
5
4
|
*
|
|
@@ -7,7 +6,7 @@ const WORKSPACE_TEMPLATE_ALIAS = "workspace";
|
|
|
7
6
|
* @returns One-line summary text for `templates list`.
|
|
8
7
|
*/
|
|
9
8
|
export function formatTemplateSummary(template) {
|
|
10
|
-
return `${template.id.padEnd(14)} ${template.description}`;
|
|
9
|
+
return `${getUserFacingTemplateId(template.id).padEnd(14)} ${template.description}`;
|
|
11
10
|
}
|
|
12
11
|
/**
|
|
13
12
|
* Format the feature and capability hint lines shown under a template summary.
|
|
@@ -17,6 +16,10 @@ export function formatTemplateSummary(template) {
|
|
|
17
16
|
*/
|
|
18
17
|
export function formatTemplateFeatures(template) {
|
|
19
18
|
const lines = [` Features: ${template.features.join(" • ")}`];
|
|
19
|
+
const bestForHint = getTemplateBestForHint(template);
|
|
20
|
+
if (bestForHint) {
|
|
21
|
+
lines.push(` Best for: ${bestForHint}`);
|
|
22
|
+
}
|
|
20
23
|
const capabilityHints = getTemplateCapabilityHints(template);
|
|
21
24
|
if (capabilityHints.length > 0) {
|
|
22
25
|
lines.push(` Supports: ${capabilityHints.join(" • ")}`);
|
|
@@ -26,7 +29,7 @@ export function formatTemplateFeatures(template) {
|
|
|
26
29
|
lines.push(` Notes: ${specialNotes.join(" • ")}`);
|
|
27
30
|
}
|
|
28
31
|
if (template.id === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE) {
|
|
29
|
-
lines.push(` Alias: ${
|
|
32
|
+
lines.push(` Alias: ${OFFICIAL_WORKSPACE_TEMPLATE_ALIAS} (\`--template ${OFFICIAL_WORKSPACE_TEMPLATE_ALIAS}\`)`);
|
|
30
33
|
}
|
|
31
34
|
return lines.join("\n");
|
|
32
35
|
}
|
|
@@ -43,8 +46,9 @@ export function formatTemplateFeatures(template) {
|
|
|
43
46
|
*/
|
|
44
47
|
export function formatTemplateDetails(template) {
|
|
45
48
|
const detailLines = [
|
|
46
|
-
template.id,
|
|
49
|
+
getUserFacingTemplateId(template.id),
|
|
47
50
|
`Summary: ${template.description}`,
|
|
51
|
+
`Best for: ${getTemplateBestForHint(template)}`,
|
|
48
52
|
...getTemplateIdentityLines(template),
|
|
49
53
|
`Category: ${template.defaultCategory}`,
|
|
50
54
|
];
|
|
@@ -69,6 +73,24 @@ export function formatTemplateDetails(template) {
|
|
|
69
73
|
detailLines.push(`Features: ${template.features.join(", ")}`);
|
|
70
74
|
return detailLines.join("\n");
|
|
71
75
|
}
|
|
76
|
+
function getTemplateBestForHint(template) {
|
|
77
|
+
if (template.id === "basic") {
|
|
78
|
+
return "minimal static-first block scaffolds with Typia validation and the lightest default surface";
|
|
79
|
+
}
|
|
80
|
+
if (template.id === "interactivity") {
|
|
81
|
+
return "interactive single-block experiences that keep client-side state and actions inside one scaffold";
|
|
82
|
+
}
|
|
83
|
+
if (template.id === "persistence") {
|
|
84
|
+
return "typed REST-backed blocks that need persistence-aware reads, writes, and schema refresh workflows";
|
|
85
|
+
}
|
|
86
|
+
if (template.id === "compound") {
|
|
87
|
+
return "parent-and-child block families that own nested authoring conventions and optional persistence wiring";
|
|
88
|
+
}
|
|
89
|
+
if (template.id === "query-loop") {
|
|
90
|
+
return "create-time `core/query` variations with connected starter patterns instead of `add block` families";
|
|
91
|
+
}
|
|
92
|
+
return "official multi-block workspaces that extend through `wp-typia add ...` and workspace doctor flows";
|
|
93
|
+
}
|
|
72
94
|
function getTemplateCapabilityHints(template) {
|
|
73
95
|
if (template.id === "persistence" || template.id === "compound") {
|
|
74
96
|
return [
|
|
@@ -99,11 +121,19 @@ function getTemplateIdentityLines(template) {
|
|
|
99
121
|
if (template.id === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE) {
|
|
100
122
|
return [
|
|
101
123
|
"Identity:",
|
|
124
|
+
` - User-facing alias: ${OFFICIAL_WORKSPACE_TEMPLATE_ALIAS} (\`--template ${OFFICIAL_WORKSPACE_TEMPLATE_ALIAS}\`)`,
|
|
102
125
|
` - Official package: ${OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE}`,
|
|
103
|
-
` - Alias: ${WORKSPACE_TEMPLATE_ALIAS} (\`--template ${WORKSPACE_TEMPLATE_ALIAS}\`)`,
|
|
104
126
|
"Type: official workspace scaffold",
|
|
105
127
|
];
|
|
106
128
|
}
|
|
129
|
+
if (template.id === "query-loop") {
|
|
130
|
+
return [
|
|
131
|
+
"Identity:",
|
|
132
|
+
" - Built-in template id: query-loop",
|
|
133
|
+
"Type: create-time core/query variation scaffold",
|
|
134
|
+
"Output model: variation-only scaffold; does not generate block.json or Typia manifests",
|
|
135
|
+
];
|
|
136
|
+
}
|
|
107
137
|
return [
|
|
108
138
|
"Identity:",
|
|
109
139
|
` - Built-in template id: ${template.id}`,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare const TEMPLATE_SOURCE_TIMEOUT_CODE: "template-source-timeout";
|
|
2
|
+
export declare const TEMPLATE_SOURCE_TOO_LARGE_CODE: "template-source-too-large";
|
|
3
|
+
export declare function getExternalTemplateTimeoutMs(): number;
|
|
4
|
+
export declare function getExternalTemplateConfigMaxBytes(): number;
|
|
5
|
+
export declare function getExternalTemplatePackageJsonMaxBytes(): number;
|
|
6
|
+
export declare function getExternalTemplateMetadataMaxBytes(): number;
|
|
7
|
+
export declare function getExternalTemplateTarballMaxBytes(): number;
|
|
8
|
+
export declare function createExternalTemplateTimeoutError(label: string, timeoutMs: number): Error & {
|
|
9
|
+
code: typeof TEMPLATE_SOURCE_TIMEOUT_CODE;
|
|
10
|
+
};
|
|
11
|
+
export declare function createExternalTemplateTooLargeError(label: string, maxBytes: number): Error & {
|
|
12
|
+
code: typeof TEMPLATE_SOURCE_TOO_LARGE_CODE;
|
|
13
|
+
};
|
|
14
|
+
export declare function assertExternalTemplateFileSize(filePath: string, options: {
|
|
15
|
+
label: string;
|
|
16
|
+
maxBytes: number;
|
|
17
|
+
}): void;
|
|
18
|
+
export declare function withExternalTemplateTimeout<T>(label: string, task: Promise<T> | (() => Promise<T>), timeoutMs?: number): Promise<T>;
|
|
19
|
+
export declare function fetchWithExternalTemplateTimeout(input: string, options: {
|
|
20
|
+
init?: RequestInit;
|
|
21
|
+
label: string;
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
}): Promise<Response>;
|
|
24
|
+
export declare function readJsonResponseWithLimit(response: Response, options: {
|
|
25
|
+
label: string;
|
|
26
|
+
maxBytes: number;
|
|
27
|
+
}): Promise<Record<string, unknown>>;
|
|
28
|
+
export declare function readBufferResponseWithLimit(response: Response, options: {
|
|
29
|
+
label: string;
|
|
30
|
+
maxBytes: number;
|
|
31
|
+
}): Promise<Buffer>;
|