@wp-typia/project-tools 0.16.7 → 0.16.9
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 +26 -1
- package/dist/runtime/block-generator-service.d.ts +9 -1
- package/dist/runtime/block-generator-service.js +137 -16
- package/dist/runtime/block-generator-tool-contract.d.ts +93 -0
- package/dist/runtime/block-generator-tool-contract.js +157 -0
- package/dist/runtime/built-in-block-artifacts.js +171 -423
- package/dist/runtime/built-in-block-code-artifacts.js +96 -46
- package/dist/runtime/built-in-block-code-templates.d.ts +36 -0
- package/dist/runtime/built-in-block-code-templates.js +2233 -0
- package/dist/runtime/cli-add-block.d.ts +2 -1
- package/dist/runtime/cli-add-block.js +163 -25
- package/dist/runtime/cli-add-shared.d.ts +7 -0
- package/dist/runtime/cli-add-shared.js +4 -6
- package/dist/runtime/cli-add-workspace.js +56 -17
- package/dist/runtime/cli-core.d.ts +4 -0
- package/dist/runtime/cli-core.js +3 -0
- package/dist/runtime/cli-diagnostics.d.ts +58 -0
- package/dist/runtime/cli-diagnostics.js +101 -0
- package/dist/runtime/cli-doctor.d.ts +2 -1
- package/dist/runtime/cli-doctor.js +16 -5
- package/dist/runtime/cli-help.js +4 -4
- package/dist/runtime/cli-scaffold.d.ts +5 -1
- package/dist/runtime/cli-scaffold.js +138 -111
- package/dist/runtime/external-layer-selection.d.ts +14 -0
- package/dist/runtime/external-layer-selection.js +35 -0
- package/dist/runtime/index.d.ts +4 -2
- package/dist/runtime/index.js +2 -1
- package/dist/runtime/migration-render.d.ts +23 -1
- package/dist/runtime/migration-render.js +58 -10
- package/dist/runtime/migration-ui-capability.js +17 -8
- package/dist/runtime/migration-utils.d.ts +7 -6
- package/dist/runtime/migration-utils.js +76 -73
- package/dist/runtime/migrations.js +2 -2
- package/dist/runtime/object-utils.d.ts +8 -1
- package/dist/runtime/object-utils.js +21 -1
- package/dist/runtime/scaffold-apply-utils.d.ts +14 -2
- package/dist/runtime/scaffold-apply-utils.js +19 -6
- package/dist/runtime/scaffold-repository-reference.d.ts +22 -0
- package/dist/runtime/scaffold-repository-reference.js +119 -0
- package/dist/runtime/scaffold.d.ts +5 -1
- package/dist/runtime/scaffold.js +15 -37
- package/dist/runtime/template-builtins.d.ts +11 -1
- package/dist/runtime/template-builtins.js +118 -25
- package/dist/runtime/template-layers.d.ts +37 -0
- package/dist/runtime/template-layers.js +184 -0
- package/dist/runtime/template-render.d.ts +28 -2
- package/dist/runtime/template-render.js +122 -55
- package/dist/runtime/template-source.d.ts +23 -5
- package/dist/runtime/template-source.js +296 -217
- package/package.json +8 -3
- package/templates/_shared/base/src/validator-toolkit.ts.mustache +2 -2
- package/templates/_shared/compound/core/scripts/add-compound-child.ts.mustache +58 -12
- package/templates/_shared/migration-ui/common/src/migrations/helpers.ts +19 -47
- package/templates/_shared/migration-ui/common/src/migrations/index.ts +40 -11
package/README.md
CHANGED
|
@@ -6,7 +6,8 @@ Package roles:
|
|
|
6
6
|
|
|
7
7
|
- `wp-typia` owns the CLI, help, TUI, completions, skills, MCP, and bin entry.
|
|
8
8
|
- `@wp-typia/project-tools` owns scaffold, add-block, migrate, template, doctor, and schema project helpers.
|
|
9
|
-
It also owns the typed generator boundary via `BlockSpec
|
|
9
|
+
It also owns the typed generator boundary via `BlockSpec`, `BlockGeneratorService`,
|
|
10
|
+
and `inspectBlockGeneration(...)`,
|
|
10
11
|
plus the emitter-owned built-in structural/code path where built-in
|
|
11
12
|
templates no longer ship structural, TS/TSX, style, or block-local `render.php`
|
|
12
13
|
Mustache files.
|
|
@@ -50,4 +51,28 @@ files and starter `typia.manifest.json` now come from the emitter path, while
|
|
|
50
51
|
project bootstrap/package-manager files, sync scripts, shared REST helpers, and
|
|
51
52
|
the remaining non-block assets still come from Mustache-backed template copy.
|
|
52
53
|
|
|
54
|
+
Generated projects now consume JSON artifacts through typed wrapper modules
|
|
55
|
+
instead of local casts:
|
|
56
|
+
|
|
57
|
+
- `block-metadata.ts` for `block.json`
|
|
58
|
+
- `manifest-document.ts` for editor and migration consumers of
|
|
59
|
+
`typia.manifest.json`
|
|
60
|
+
- `manifest-defaults-document.ts` for validator/defaults consumers of
|
|
61
|
+
`typia.manifest.json`
|
|
62
|
+
|
|
63
|
+
The higher-level generator architecture record, including the current phase map
|
|
64
|
+
and the non-mutating `plan -> validate -> render -> apply` tool-facing usage
|
|
65
|
+
model, lives in
|
|
66
|
+
[`docs/block-generator-architecture.md`](../../docs/block-generator-architecture.md).
|
|
67
|
+
The public non-mutating controller/tool contract now lives in
|
|
68
|
+
[`docs/block-generator-tool-contract.md`](../../docs/block-generator-tool-contract.md).
|
|
69
|
+
|
|
70
|
+
Reusable external layer packages on top of the built-in shared scaffold model
|
|
71
|
+
are now available through the canonical built-in CLI flags
|
|
72
|
+
`wp-typia create --external-layer-source ... [--external-layer-id ...]`,
|
|
73
|
+
`wp-typia add block --external-layer-source ... [--external-layer-id ...]`,
|
|
74
|
+
and programmatically through `scaffoldProject(...)`, `BlockGeneratorService`,
|
|
75
|
+
and `inspectBlockGeneration(...)`. The layer contract record lives in
|
|
76
|
+
[`docs/external-template-layer-composition.md`](../../docs/external-template-layer-composition.md).
|
|
77
|
+
|
|
53
78
|
If you need metadata sync, editor helpers, validation helpers, or other generated-project runtime utilities, import them directly from `@wp-typia/block-runtime/*`.
|
|
@@ -41,9 +41,13 @@ export interface BlockSpec {
|
|
|
41
41
|
export interface BlockGenerationTarget {
|
|
42
42
|
allowExistingDir: boolean;
|
|
43
43
|
cwd: string;
|
|
44
|
+
externalLayerId?: string;
|
|
45
|
+
externalLayerSource?: string;
|
|
46
|
+
externalLayerSourceLabel?: string;
|
|
44
47
|
noInstall: boolean;
|
|
45
48
|
packageManager: PackageManagerId;
|
|
46
49
|
projectDir: string;
|
|
50
|
+
repositoryReference?: string;
|
|
47
51
|
variant?: string;
|
|
48
52
|
}
|
|
49
53
|
export interface PlanBlockInput {
|
|
@@ -51,10 +55,14 @@ export interface PlanBlockInput {
|
|
|
51
55
|
answers: ScaffoldAnswers;
|
|
52
56
|
cwd?: string;
|
|
53
57
|
dataStorageMode?: DataStorageMode;
|
|
58
|
+
externalLayerId?: string;
|
|
59
|
+
externalLayerSource?: string;
|
|
60
|
+
externalLayerSourceLabel?: string;
|
|
54
61
|
noInstall?: boolean;
|
|
55
62
|
packageManager: PackageManagerId;
|
|
56
63
|
persistencePolicy?: PersistencePolicy;
|
|
57
64
|
projectDir: string;
|
|
65
|
+
repositoryReference?: string;
|
|
58
66
|
templateId: BuiltInTemplateId;
|
|
59
67
|
variant?: string;
|
|
60
68
|
withMigrationUi?: boolean;
|
|
@@ -95,7 +103,7 @@ export interface ApplyBlockInput {
|
|
|
95
103
|
export declare function createBuiltInBlockSpec({ answers, dataStorageMode, persistencePolicy, templateId, withMigrationUi, withTestPreset, withWpEnv, }: Omit<PlanBlockInput, "allowExistingDir" | "cwd" | "noInstall" | "packageManager" | "projectDir" | "variant">): BlockSpec;
|
|
96
104
|
export declare function buildTemplateVariablesFromBlockSpec(spec: BlockSpec): ScaffoldTemplateVariables;
|
|
97
105
|
export declare class BlockGeneratorService {
|
|
98
|
-
plan({ allowExistingDir, answers, cwd, dataStorageMode, noInstall, packageManager, persistencePolicy, projectDir, templateId, variant, withMigrationUi, withTestPreset, withWpEnv, }: PlanBlockInput): Promise<PlanBlockResult>;
|
|
106
|
+
plan({ allowExistingDir, answers, cwd, dataStorageMode, externalLayerId, externalLayerSource, externalLayerSourceLabel, noInstall, packageManager, persistencePolicy, projectDir, repositoryReference, templateId, variant, withMigrationUi, withTestPreset, withWpEnv, }: PlanBlockInput): Promise<PlanBlockResult>;
|
|
99
107
|
validate({ plan }: ValidateBlockInput): Promise<ValidateBlockResult>;
|
|
100
108
|
render({ validated }: RenderBlockInput): Promise<RenderBlockResult>;
|
|
101
109
|
apply({ rendered, installDependencies, }: ApplyBlockInput): Promise<ScaffoldProjectResult>;
|
|
@@ -7,9 +7,62 @@ import { applyBuiltInScaffoldProjectFiles, buildGitignore, buildReadme, } from "
|
|
|
7
7
|
import { buildBlockCssClassName, buildFrontendCssClassName, resolveScaffoldIdentifiers, } from "./scaffold-identifiers.js";
|
|
8
8
|
import { buildBuiltInBlockArtifacts, } from "./built-in-block-artifacts.js";
|
|
9
9
|
import { buildBuiltInCodeArtifacts, } from "./built-in-block-code-artifacts.js";
|
|
10
|
+
import { stableJsonStringify } from "./object-utils.js";
|
|
11
|
+
import { getStarterManifestFiles } from "./starter-manifests.js";
|
|
12
|
+
import { resolveTemplateSeed, parseTemplateLocator } from "./template-source.js";
|
|
13
|
+
import { assertExternalTemplateLayersDoNotWriteProtectedOutputs, resolveExternalTemplateLayers, } from "./template-layers.js";
|
|
14
|
+
import { getBuiltInTemplateOverlayDir, getBuiltInTemplateSharedLayerDirs, resolveBuiltInTemplateSourceFromLayerDirs, } from "./template-builtins.js";
|
|
10
15
|
const renderedArtifactCache = new WeakMap();
|
|
11
16
|
function createVariablesFingerprint(variables) {
|
|
12
|
-
return
|
|
17
|
+
return stableJsonStringify(variables);
|
|
18
|
+
}
|
|
19
|
+
function buildProtectedTemplateOutputPaths({ codeArtifacts, spec, variables, artifacts, }) {
|
|
20
|
+
const protectedOutputs = new Set([
|
|
21
|
+
".gitignore",
|
|
22
|
+
"package.json",
|
|
23
|
+
"scripts/add-compound-child.ts",
|
|
24
|
+
"scripts/block-config.ts",
|
|
25
|
+
"scripts/sync-project.ts",
|
|
26
|
+
"scripts/sync-rest-contracts.ts",
|
|
27
|
+
"scripts/sync-types-to-block-json.ts",
|
|
28
|
+
"tsconfig.json",
|
|
29
|
+
"webpack.config.js",
|
|
30
|
+
`${variables.slugKebabCase}.php`,
|
|
31
|
+
]);
|
|
32
|
+
for (const artifact of codeArtifacts) {
|
|
33
|
+
protectedOutputs.add(artifact.relativePath);
|
|
34
|
+
}
|
|
35
|
+
for (const artifact of artifacts) {
|
|
36
|
+
protectedOutputs.add(`${artifact.relativeDir}/block.json`);
|
|
37
|
+
protectedOutputs.add(`${artifact.relativeDir}/types.ts`);
|
|
38
|
+
}
|
|
39
|
+
for (const manifest of getStarterManifestFiles(spec.template.family, variables)) {
|
|
40
|
+
protectedOutputs.add(manifest.relativePath);
|
|
41
|
+
}
|
|
42
|
+
return protectedOutputs;
|
|
43
|
+
}
|
|
44
|
+
function buildCombinedTemplateLayerDirs({ baseLayerDirs, externalEntries, templateId, }) {
|
|
45
|
+
const orderedLayerDirs = [];
|
|
46
|
+
const seenLayerDirs = new Set();
|
|
47
|
+
for (const layerDir of baseLayerDirs) {
|
|
48
|
+
if (seenLayerDirs.has(layerDir)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
orderedLayerDirs.push(layerDir);
|
|
52
|
+
seenLayerDirs.add(layerDir);
|
|
53
|
+
}
|
|
54
|
+
for (const entry of externalEntries) {
|
|
55
|
+
if (seenLayerDirs.has(entry.dir)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
orderedLayerDirs.push(entry.dir);
|
|
59
|
+
seenLayerDirs.add(entry.dir);
|
|
60
|
+
}
|
|
61
|
+
const overlayDir = getBuiltInTemplateOverlayDir(templateId);
|
|
62
|
+
if (!seenLayerDirs.has(overlayDir)) {
|
|
63
|
+
orderedLayerDirs.push(overlayDir);
|
|
64
|
+
}
|
|
65
|
+
return orderedLayerDirs;
|
|
13
66
|
}
|
|
14
67
|
function getBuiltInPersistenceSpec({ templateId, dataStorageMode, persistencePolicy, }) {
|
|
15
68
|
if (templateId === "persistence") {
|
|
@@ -146,7 +199,7 @@ export function buildTemplateVariablesFromBlockSpec(spec) {
|
|
|
146
199
|
};
|
|
147
200
|
}
|
|
148
201
|
export class BlockGeneratorService {
|
|
149
|
-
async plan({ allowExistingDir = false, answers, cwd = process.cwd(), dataStorageMode, noInstall = false, packageManager, persistencePolicy, projectDir, templateId, variant, withMigrationUi = false, withTestPreset = false, withWpEnv = false, }) {
|
|
202
|
+
async plan({ allowExistingDir = false, answers, cwd = process.cwd(), dataStorageMode, externalLayerId, externalLayerSource, externalLayerSourceLabel, noInstall = false, packageManager, persistencePolicy, projectDir, repositoryReference, templateId, variant, withMigrationUi = false, withTestPreset = false, withWpEnv = false, }) {
|
|
150
203
|
return {
|
|
151
204
|
spec: createBuiltInBlockSpec({
|
|
152
205
|
answers,
|
|
@@ -160,29 +213,102 @@ export class BlockGeneratorService {
|
|
|
160
213
|
target: {
|
|
161
214
|
allowExistingDir,
|
|
162
215
|
cwd,
|
|
216
|
+
externalLayerId,
|
|
217
|
+
externalLayerSource,
|
|
218
|
+
externalLayerSourceLabel,
|
|
163
219
|
noInstall,
|
|
164
220
|
packageManager,
|
|
165
221
|
projectDir,
|
|
222
|
+
repositoryReference,
|
|
166
223
|
variant,
|
|
167
224
|
},
|
|
168
225
|
};
|
|
169
226
|
}
|
|
170
227
|
async validate({ plan }) {
|
|
228
|
+
if (plan.target.externalLayerId && !plan.target.externalLayerSource) {
|
|
229
|
+
throw new Error("externalLayerId requires externalLayerSource when composing built-in template layers.");
|
|
230
|
+
}
|
|
171
231
|
if (plan.target.variant) {
|
|
172
232
|
throw new Error(`--variant is only supported for official external template configs. Received variant "${plan.target.variant}" for built-in template "${plan.spec.template.family}".`);
|
|
173
233
|
}
|
|
174
234
|
return plan;
|
|
175
235
|
}
|
|
176
236
|
async render({ validated }) {
|
|
177
|
-
const
|
|
178
|
-
|
|
237
|
+
const variables = buildTemplateVariablesFromBlockSpec(validated.spec);
|
|
238
|
+
const persistenceEnabled = validated.spec.persistence.enabled;
|
|
239
|
+
const artifacts = buildBuiltInBlockArtifacts({
|
|
240
|
+
templateId: validated.spec.template.family,
|
|
241
|
+
variables,
|
|
242
|
+
});
|
|
243
|
+
const codeArtifacts = buildBuiltInCodeArtifacts({
|
|
244
|
+
templateId: validated.spec.template.family,
|
|
245
|
+
variables,
|
|
246
|
+
});
|
|
247
|
+
const templateVariantOptions = {
|
|
248
|
+
persistenceEnabled,
|
|
179
249
|
persistencePolicy: validated.spec.persistence.enabled &&
|
|
180
250
|
validated.spec.persistence.persistencePolicy === "public"
|
|
181
251
|
? "public"
|
|
182
252
|
: "authenticated",
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const
|
|
253
|
+
};
|
|
254
|
+
let templateSource = await resolveBuiltInTemplateSource(validated.spec.template.family, templateVariantOptions);
|
|
255
|
+
const warnings = [...(templateSource.warnings ?? [])];
|
|
256
|
+
if (validated.target.externalLayerSource) {
|
|
257
|
+
let layerSeed;
|
|
258
|
+
try {
|
|
259
|
+
layerSeed = await resolveTemplateSeed(parseTemplateLocator(validated.target.externalLayerSource), validated.target.cwd);
|
|
260
|
+
const resolvedLayers = await resolveExternalTemplateLayers({
|
|
261
|
+
externalLayerId: validated.target.externalLayerId,
|
|
262
|
+
sourceRoot: layerSeed.rootDir,
|
|
263
|
+
});
|
|
264
|
+
const baseLayerDirs = getBuiltInTemplateSharedLayerDirs(validated.spec.template.family, templateVariantOptions);
|
|
265
|
+
await assertExternalTemplateLayersDoNotWriteProtectedOutputs({
|
|
266
|
+
externalEntries: resolvedLayers.entries,
|
|
267
|
+
protectedOutputPaths: buildProtectedTemplateOutputPaths({
|
|
268
|
+
artifacts,
|
|
269
|
+
codeArtifacts,
|
|
270
|
+
spec: validated.spec,
|
|
271
|
+
variables,
|
|
272
|
+
}),
|
|
273
|
+
view: variables,
|
|
274
|
+
});
|
|
275
|
+
await templateSource.cleanup?.();
|
|
276
|
+
templateSource = await resolveBuiltInTemplateSourceFromLayerDirs(validated.spec.template.family, buildCombinedTemplateLayerDirs({
|
|
277
|
+
baseLayerDirs,
|
|
278
|
+
externalEntries: resolvedLayers.entries,
|
|
279
|
+
templateId: validated.spec.template.family,
|
|
280
|
+
}));
|
|
281
|
+
const layerSourceCleanup = layerSeed.cleanup;
|
|
282
|
+
const templateCleanup = templateSource.cleanup;
|
|
283
|
+
templateSource.cleanup = async () => {
|
|
284
|
+
const cleanupErrors = [];
|
|
285
|
+
try {
|
|
286
|
+
await templateCleanup?.();
|
|
287
|
+
}
|
|
288
|
+
catch (error) {
|
|
289
|
+
cleanupErrors.push(error instanceof Error ? error : new Error(String(error)));
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
await layerSourceCleanup?.();
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
cleanupErrors.push(error instanceof Error ? error : new Error(String(error)));
|
|
296
|
+
}
|
|
297
|
+
if (cleanupErrors.length > 0) {
|
|
298
|
+
throw new Error([
|
|
299
|
+
"Failed to cleanup composed template sources.",
|
|
300
|
+
...cleanupErrors.map((error) => `- ${error.message}`),
|
|
301
|
+
].join("\n"));
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
warnings.push(`Applied external layer "${resolvedLayers.selectedLayerId}" from "${validated.target.externalLayerSourceLabel ?? validated.target.externalLayerSource}".`);
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
await templateSource.cleanup?.();
|
|
308
|
+
await layerSeed?.cleanup?.();
|
|
309
|
+
throw error;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
186
312
|
const rendered = {
|
|
187
313
|
...validated,
|
|
188
314
|
cleanup: templateSource.cleanup,
|
|
@@ -202,17 +328,11 @@ export class BlockGeneratorService {
|
|
|
202
328
|
selectedVariant: null,
|
|
203
329
|
templateDir: templateSource.templateDir,
|
|
204
330
|
variables,
|
|
205
|
-
warnings
|
|
331
|
+
warnings,
|
|
206
332
|
};
|
|
207
333
|
renderedArtifactCache.set(rendered, {
|
|
208
|
-
artifacts
|
|
209
|
-
|
|
210
|
-
variables,
|
|
211
|
-
}),
|
|
212
|
-
codeArtifacts: buildBuiltInCodeArtifacts({
|
|
213
|
-
templateId: validated.spec.template.family,
|
|
214
|
-
variables,
|
|
215
|
-
}),
|
|
334
|
+
artifacts,
|
|
335
|
+
codeArtifacts,
|
|
216
336
|
variablesFingerprint: createVariablesFingerprint(variables),
|
|
217
337
|
});
|
|
218
338
|
return rendered;
|
|
@@ -243,6 +363,7 @@ export class BlockGeneratorService {
|
|
|
243
363
|
noInstall: rendered.target.noInstall,
|
|
244
364
|
packageManager: rendered.target.packageManager,
|
|
245
365
|
projectDir: rendered.target.projectDir,
|
|
366
|
+
repositoryReference: rendered.target.repositoryReference,
|
|
246
367
|
gitignoreContent: rendered.gitignoreContent,
|
|
247
368
|
readmeContent: rendered.readmeContent,
|
|
248
369
|
templateDir: rendered.templateDir,
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { BlockGeneratorService, type PlanBlockInput, type PlanBlockResult, type RenderBlockResult, type ValidateBlockResult } from "./block-generator-service.js";
|
|
2
|
+
import type { ManifestDocument } from "./migration-types.js";
|
|
3
|
+
import type { BuiltInTemplateId } from "./template-registry.js";
|
|
4
|
+
/**
|
|
5
|
+
* Semantic version marker for the public block generation tool contract.
|
|
6
|
+
*
|
|
7
|
+
* Increment this number whenever the serialized inspection payload changes in a
|
|
8
|
+
* breaking way.
|
|
9
|
+
*/
|
|
10
|
+
export declare const BLOCK_GENERATION_TOOL_CONTRACT_VERSION: 1;
|
|
11
|
+
/**
|
|
12
|
+
* Staged execution points for the non-mutating inspection workflow.
|
|
13
|
+
*
|
|
14
|
+
* - `"plan"` returns the normalized `BlockSpec` and target metadata.
|
|
15
|
+
* - `"validate"` returns the validated generation stage without rendering.
|
|
16
|
+
* - `"render"` returns the full non-mutating preview, including copied and
|
|
17
|
+
* emitted file snapshots.
|
|
18
|
+
*/
|
|
19
|
+
export type BlockGenerationToolStage = "plan" | "validate" | "render";
|
|
20
|
+
/**
|
|
21
|
+
* Input for the staged, non-mutating block generation inspection entrypoint.
|
|
22
|
+
*
|
|
23
|
+
* Extends `PlanBlockInput` with an optional `stopAfter` selector so callers can
|
|
24
|
+
* stop at `plan`, `validate`, or continue through the full `render` preview.
|
|
25
|
+
*/
|
|
26
|
+
export interface InspectBlockGenerationInput extends PlanBlockInput {
|
|
27
|
+
stopAfter?: BlockGenerationToolStage;
|
|
28
|
+
}
|
|
29
|
+
export interface BlockGenerationTemplateCopyPreview {
|
|
30
|
+
owner: "template-copy";
|
|
31
|
+
relativePath: string;
|
|
32
|
+
}
|
|
33
|
+
export interface BlockGenerationEmittedFilePreview {
|
|
34
|
+
kind: "generated-source" | "starter-manifest" | "structural";
|
|
35
|
+
owner: "emitter";
|
|
36
|
+
relativePath: string;
|
|
37
|
+
source: string;
|
|
38
|
+
}
|
|
39
|
+
export interface BlockGenerationStarterManifestPreview {
|
|
40
|
+
document: ManifestDocument;
|
|
41
|
+
owner: "emitter";
|
|
42
|
+
relativePath: string;
|
|
43
|
+
source: string;
|
|
44
|
+
}
|
|
45
|
+
export interface BlockGenerationRenderPreview {
|
|
46
|
+
copiedTemplateFiles: BlockGenerationTemplateCopyPreview[];
|
|
47
|
+
emittedFiles: BlockGenerationEmittedFilePreview[];
|
|
48
|
+
postRender: RenderBlockResult["postRender"] & {
|
|
49
|
+
installsDependencies: boolean;
|
|
50
|
+
};
|
|
51
|
+
selectedVariant: null;
|
|
52
|
+
starterManifestFiles: BlockGenerationStarterManifestPreview[];
|
|
53
|
+
template: {
|
|
54
|
+
description: string;
|
|
55
|
+
family: BuiltInTemplateId;
|
|
56
|
+
features: string[];
|
|
57
|
+
format: "wp-typia";
|
|
58
|
+
};
|
|
59
|
+
warnings: string[];
|
|
60
|
+
readmeContent: string;
|
|
61
|
+
gitignoreContent: string;
|
|
62
|
+
}
|
|
63
|
+
interface BlockGenerationInspectionBase {
|
|
64
|
+
contractVersion: typeof BLOCK_GENERATION_TOOL_CONTRACT_VERSION;
|
|
65
|
+
mutatesWorkspace: false;
|
|
66
|
+
stage: BlockGenerationToolStage;
|
|
67
|
+
}
|
|
68
|
+
export interface InspectBlockGenerationPlanResult extends BlockGenerationInspectionBase {
|
|
69
|
+
plan: PlanBlockResult;
|
|
70
|
+
stage: "plan";
|
|
71
|
+
}
|
|
72
|
+
export interface InspectBlockGenerationValidateResult extends BlockGenerationInspectionBase {
|
|
73
|
+
plan: PlanBlockResult;
|
|
74
|
+
stage: "validate";
|
|
75
|
+
validated: ValidateBlockResult;
|
|
76
|
+
}
|
|
77
|
+
export interface InspectBlockGenerationRenderResult extends BlockGenerationInspectionBase {
|
|
78
|
+
plan: PlanBlockResult;
|
|
79
|
+
rendered: BlockGenerationRenderPreview;
|
|
80
|
+
stage: "render";
|
|
81
|
+
validated: ValidateBlockResult;
|
|
82
|
+
}
|
|
83
|
+
export type InspectBlockGenerationResult = InspectBlockGenerationPlanResult | InspectBlockGenerationValidateResult | InspectBlockGenerationRenderResult;
|
|
84
|
+
export declare function inspectBlockGeneration(input: InspectBlockGenerationInput & {
|
|
85
|
+
stopAfter: "plan";
|
|
86
|
+
}, service?: BlockGeneratorService): Promise<InspectBlockGenerationPlanResult>;
|
|
87
|
+
export declare function inspectBlockGeneration(input: InspectBlockGenerationInput & {
|
|
88
|
+
stopAfter: "validate";
|
|
89
|
+
}, service?: BlockGeneratorService): Promise<InspectBlockGenerationValidateResult>;
|
|
90
|
+
export declare function inspectBlockGeneration(input: InspectBlockGenerationInput & {
|
|
91
|
+
stopAfter?: "render" | undefined;
|
|
92
|
+
}, service?: BlockGeneratorService): Promise<InspectBlockGenerationRenderResult>;
|
|
93
|
+
export {};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { buildBuiltInBlockArtifacts, stringifyBuiltInBlockJsonDocument, } from "./built-in-block-artifacts.js";
|
|
2
|
+
import { buildBuiltInCodeArtifacts, } from "./built-in-block-code-artifacts.js";
|
|
3
|
+
import { BlockGeneratorService, } from "./block-generator-service.js";
|
|
4
|
+
import { getStarterManifestFiles, stringifyStarterManifest, } from "./starter-manifests.js";
|
|
5
|
+
import { listInterpolatedDirectoryOutputs } from "./template-render.js";
|
|
6
|
+
/**
|
|
7
|
+
* Semantic version marker for the public block generation tool contract.
|
|
8
|
+
*
|
|
9
|
+
* Increment this number whenever the serialized inspection payload changes in a
|
|
10
|
+
* breaking way.
|
|
11
|
+
*/
|
|
12
|
+
export const BLOCK_GENERATION_TOOL_CONTRACT_VERSION = 1;
|
|
13
|
+
function buildStarterManifestPreviews(templateId, variables, artifacts) {
|
|
14
|
+
const starterManifests = getStarterManifestFiles(templateId, variables);
|
|
15
|
+
const artifactManifests = new Map(artifacts.map((artifact) => [
|
|
16
|
+
`${artifact.relativeDir}/typia.manifest.json`,
|
|
17
|
+
artifact.manifestDocument,
|
|
18
|
+
]));
|
|
19
|
+
return starterManifests
|
|
20
|
+
.map((entry) => {
|
|
21
|
+
const document = artifactManifests.get(entry.relativePath) ?? entry.document;
|
|
22
|
+
return {
|
|
23
|
+
document,
|
|
24
|
+
owner: "emitter",
|
|
25
|
+
relativePath: entry.relativePath,
|
|
26
|
+
source: stringifyStarterManifest(document),
|
|
27
|
+
};
|
|
28
|
+
})
|
|
29
|
+
.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
30
|
+
}
|
|
31
|
+
function buildStructuralArtifactPreviews(artifacts) {
|
|
32
|
+
return artifacts
|
|
33
|
+
.flatMap((artifact) => [
|
|
34
|
+
{
|
|
35
|
+
kind: "structural",
|
|
36
|
+
owner: "emitter",
|
|
37
|
+
relativePath: `${artifact.relativeDir}/block.json`,
|
|
38
|
+
source: stringifyBuiltInBlockJsonDocument(artifact.blockJsonDocument),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
kind: "structural",
|
|
42
|
+
owner: "emitter",
|
|
43
|
+
relativePath: `${artifact.relativeDir}/types.ts`,
|
|
44
|
+
source: artifact.typesSource,
|
|
45
|
+
},
|
|
46
|
+
])
|
|
47
|
+
.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
48
|
+
}
|
|
49
|
+
function buildCodeArtifactPreviews(codeArtifacts) {
|
|
50
|
+
return codeArtifacts
|
|
51
|
+
.map((artifact) => ({
|
|
52
|
+
kind: "generated-source",
|
|
53
|
+
owner: "emitter",
|
|
54
|
+
relativePath: artifact.relativePath,
|
|
55
|
+
source: artifact.source,
|
|
56
|
+
}))
|
|
57
|
+
.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
|
|
58
|
+
}
|
|
59
|
+
async function buildRenderPreview(rendered) {
|
|
60
|
+
const artifacts = buildBuiltInBlockArtifacts({
|
|
61
|
+
templateId: rendered.spec.template.family,
|
|
62
|
+
variables: rendered.variables,
|
|
63
|
+
});
|
|
64
|
+
const codeArtifacts = buildBuiltInCodeArtifacts({
|
|
65
|
+
templateId: rendered.spec.template.family,
|
|
66
|
+
variables: rendered.variables,
|
|
67
|
+
});
|
|
68
|
+
const copiedTemplateFiles = await listInterpolatedDirectoryOutputs(rendered.templateDir, rendered.variables);
|
|
69
|
+
return {
|
|
70
|
+
copiedTemplateFiles: copiedTemplateFiles.map((relativePath) => ({
|
|
71
|
+
owner: "template-copy",
|
|
72
|
+
relativePath,
|
|
73
|
+
})),
|
|
74
|
+
emittedFiles: [
|
|
75
|
+
...buildStructuralArtifactPreviews(artifacts),
|
|
76
|
+
...buildCodeArtifactPreviews(codeArtifacts),
|
|
77
|
+
].sort((left, right) => left.relativePath.localeCompare(right.relativePath)),
|
|
78
|
+
gitignoreContent: rendered.gitignoreContent,
|
|
79
|
+
postRender: {
|
|
80
|
+
...rendered.postRender,
|
|
81
|
+
installsDependencies: !rendered.target.noInstall,
|
|
82
|
+
},
|
|
83
|
+
readmeContent: rendered.readmeContent,
|
|
84
|
+
selectedVariant: rendered.selectedVariant,
|
|
85
|
+
starterManifestFiles: buildStarterManifestPreviews(rendered.spec.template.family, rendered.variables, artifacts),
|
|
86
|
+
template: {
|
|
87
|
+
description: rendered.spec.template.description,
|
|
88
|
+
family: rendered.spec.template.family,
|
|
89
|
+
features: [...rendered.spec.template.features],
|
|
90
|
+
format: "wp-typia",
|
|
91
|
+
},
|
|
92
|
+
warnings: [...rendered.warnings],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Inspects built-in block generation through the staged generator boundary
|
|
97
|
+
* without mutating the destination workspace.
|
|
98
|
+
*
|
|
99
|
+
* Use `stopAfter` to halt after the `plan`, `validate`, or `render` stage.
|
|
100
|
+
* The render stage includes copied template file paths, emitter-owned source
|
|
101
|
+
* previews, starter manifest previews, and post-render intent metadata. Any
|
|
102
|
+
* temporary render state is cleaned up before the promise resolves.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* const inspection = await inspectBlockGeneration({
|
|
107
|
+
* answers,
|
|
108
|
+
* noInstall: true,
|
|
109
|
+
* packageManager: "bun",
|
|
110
|
+
* projectDir: "demo-block",
|
|
111
|
+
* templateId: "basic",
|
|
112
|
+
* stopAfter: "render",
|
|
113
|
+
* });
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* @param input - Planning input plus an optional `stopAfter` stage selector.
|
|
117
|
+
* @param service - Optional generator service instance. Defaults to a new
|
|
118
|
+
* `BlockGeneratorService`.
|
|
119
|
+
* @returns The serialized inspection result for the reached stage.
|
|
120
|
+
* @throws Propagates planning, validation, or render failures from
|
|
121
|
+
* `BlockGeneratorService`.
|
|
122
|
+
*/
|
|
123
|
+
export async function inspectBlockGeneration({ stopAfter = "render", ...planInput }, service = new BlockGeneratorService()) {
|
|
124
|
+
const plan = await service.plan(planInput);
|
|
125
|
+
if (stopAfter === "plan") {
|
|
126
|
+
return {
|
|
127
|
+
contractVersion: BLOCK_GENERATION_TOOL_CONTRACT_VERSION,
|
|
128
|
+
mutatesWorkspace: false,
|
|
129
|
+
plan,
|
|
130
|
+
stage: "plan",
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const validated = await service.validate({ plan });
|
|
134
|
+
if (stopAfter === "validate") {
|
|
135
|
+
return {
|
|
136
|
+
contractVersion: BLOCK_GENERATION_TOOL_CONTRACT_VERSION,
|
|
137
|
+
mutatesWorkspace: false,
|
|
138
|
+
plan,
|
|
139
|
+
stage: "validate",
|
|
140
|
+
validated,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const rendered = await service.render({ validated });
|
|
144
|
+
try {
|
|
145
|
+
return {
|
|
146
|
+
contractVersion: BLOCK_GENERATION_TOOL_CONTRACT_VERSION,
|
|
147
|
+
mutatesWorkspace: false,
|
|
148
|
+
plan,
|
|
149
|
+
rendered: await buildRenderPreview(rendered),
|
|
150
|
+
stage: "render",
|
|
151
|
+
validated,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
await rendered.cleanup?.();
|
|
156
|
+
}
|
|
157
|
+
}
|