@wp-typia/project-tools 0.16.6 → 0.16.8
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 +25 -8
- package/dist/runtime/block-generator-service.d.ts +5 -1
- package/dist/runtime/block-generator-service.js +132 -15
- 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-code-artifacts.js +5 -5
- package/dist/runtime/cli-add-block.d.ts +36 -0
- package/dist/runtime/cli-add-block.js +518 -0
- package/dist/runtime/cli-add-shared.d.ts +93 -0
- package/dist/runtime/cli-add-shared.js +201 -0
- package/dist/runtime/cli-add-workspace.d.ts +81 -0
- package/dist/runtime/cli-add-workspace.js +582 -0
- package/dist/runtime/cli-add.d.ts +11 -131
- package/dist/runtime/cli-add.js +10 -1250
- package/dist/runtime/cli-prompt.d.ts +25 -0
- package/dist/runtime/cli-prompt.js +32 -20
- package/dist/runtime/cli-scaffold.js +1 -2
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/migration-types.d.ts +9 -53
- package/dist/runtime/scaffold.js +1 -2
- package/dist/runtime/template-builtins.d.ts +11 -1
- package/dist/runtime/template-builtins.js +118 -25
- package/dist/runtime/template-layers.d.ts +31 -0
- package/dist/runtime/template-layers.js +171 -0
- package/dist/runtime/template-render.d.ts +15 -0
- package/dist/runtime/template-render.js +36 -0
- package/dist/runtime/template-source.d.ts +17 -0
- package/dist/runtime/template-source.js +14 -3
- package/package.json +6 -8
- package/templates/_shared/base/package.json.mustache +1 -0
- package/templates/_shared/compound/core/package.json.mustache +1 -1
- package/templates/_shared/compound/core/scripts/add-compound-child.ts.mustache +5 -5
- package/templates/_shared/compound/persistence/package.json.mustache +1 -1
- package/templates/_shared/persistence/core/package.json.mustache +1 -0
- package/templates/interactivity/package.json.mustache +2 -1
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.
|
|
@@ -23,21 +24,23 @@ Implementation note:
|
|
|
23
24
|
- `@wp-typia/project-tools/schema-core` remains the preferred project-tooling
|
|
24
25
|
import path.
|
|
25
26
|
- The shared implementation now lives in `@wp-typia/block-runtime/schema-core`.
|
|
27
|
+
- Shared manifest/migration contract types now live in
|
|
28
|
+
`@wp-typia/block-runtime/migration-types`.
|
|
26
29
|
|
|
27
30
|
Example:
|
|
28
31
|
|
|
29
32
|
```ts
|
|
30
33
|
import {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
} from
|
|
34
|
+
BlockGeneratorService,
|
|
35
|
+
getTemplateById,
|
|
36
|
+
parseMigrationArgs,
|
|
37
|
+
projectJsonSchemaDocument,
|
|
38
|
+
resolvePackageManagerId,
|
|
39
|
+
} from '@wp-typia/project-tools';
|
|
37
40
|
```
|
|
38
41
|
|
|
39
42
|
```ts
|
|
40
|
-
import { normalizeEndpointAuthDefinition } from
|
|
43
|
+
import { normalizeEndpointAuthDefinition } from '@wp-typia/project-tools/schema-core';
|
|
41
44
|
```
|
|
42
45
|
|
|
43
46
|
`BlockGeneratorService` is the additive typed orchestration boundary for built-in
|
|
@@ -48,4 +51,18 @@ files and starter `typia.manifest.json` now come from the emitter path, while
|
|
|
48
51
|
project bootstrap/package-manager files, sync scripts, shared REST helpers, and
|
|
49
52
|
the remaining non-block assets still come from Mustache-backed template copy.
|
|
50
53
|
|
|
54
|
+
The higher-level generator architecture record, including the current phase map
|
|
55
|
+
and the non-mutating `plan -> validate -> render -> apply` tool-facing usage
|
|
56
|
+
model, lives in
|
|
57
|
+
[`docs/block-generator-architecture.md`](../../docs/block-generator-architecture.md).
|
|
58
|
+
The public non-mutating controller/tool contract now lives in
|
|
59
|
+
[`docs/block-generator-tool-contract.md`](../../docs/block-generator-tool-contract.md).
|
|
60
|
+
|
|
61
|
+
Reusable external layer packages on top of the built-in shared scaffold model
|
|
62
|
+
are now available programmatically through `scaffoldProject(...)`,
|
|
63
|
+
`BlockGeneratorService`, and `inspectBlockGeneration(...)` via
|
|
64
|
+
`externalLayerSource` and optional `externalLayerId`. The RFC/CLI UX record
|
|
65
|
+
still lives in
|
|
66
|
+
[`docs/external-template-layer-composition.md`](../../docs/external-template-layer-composition.md).
|
|
67
|
+
|
|
51
68
|
If you need metadata sync, editor helpers, validation helpers, or other generated-project runtime utilities, import them directly from `@wp-typia/block-runtime/*`.
|
|
@@ -41,6 +41,8 @@ export interface BlockSpec {
|
|
|
41
41
|
export interface BlockGenerationTarget {
|
|
42
42
|
allowExistingDir: boolean;
|
|
43
43
|
cwd: string;
|
|
44
|
+
externalLayerId?: string;
|
|
45
|
+
externalLayerSource?: string;
|
|
44
46
|
noInstall: boolean;
|
|
45
47
|
packageManager: PackageManagerId;
|
|
46
48
|
projectDir: string;
|
|
@@ -51,6 +53,8 @@ export interface PlanBlockInput {
|
|
|
51
53
|
answers: ScaffoldAnswers;
|
|
52
54
|
cwd?: string;
|
|
53
55
|
dataStorageMode?: DataStorageMode;
|
|
56
|
+
externalLayerId?: string;
|
|
57
|
+
externalLayerSource?: string;
|
|
54
58
|
noInstall?: boolean;
|
|
55
59
|
packageManager: PackageManagerId;
|
|
56
60
|
persistencePolicy?: PersistencePolicy;
|
|
@@ -95,7 +99,7 @@ export interface ApplyBlockInput {
|
|
|
95
99
|
export declare function createBuiltInBlockSpec({ answers, dataStorageMode, persistencePolicy, templateId, withMigrationUi, withTestPreset, withWpEnv, }: Omit<PlanBlockInput, "allowExistingDir" | "cwd" | "noInstall" | "packageManager" | "projectDir" | "variant">): BlockSpec;
|
|
96
100
|
export declare function buildTemplateVariablesFromBlockSpec(spec: BlockSpec): ScaffoldTemplateVariables;
|
|
97
101
|
export declare class BlockGeneratorService {
|
|
98
|
-
plan({ allowExistingDir, answers, cwd, dataStorageMode, noInstall, packageManager, persistencePolicy, projectDir, templateId, variant, withMigrationUi, withTestPreset, withWpEnv, }: PlanBlockInput): Promise<PlanBlockResult>;
|
|
102
|
+
plan({ allowExistingDir, answers, cwd, dataStorageMode, externalLayerId, externalLayerSource, noInstall, packageManager, persistencePolicy, projectDir, templateId, variant, withMigrationUi, withTestPreset, withWpEnv, }: PlanBlockInput): Promise<PlanBlockResult>;
|
|
99
103
|
validate({ plan }: ValidateBlockInput): Promise<ValidateBlockResult>;
|
|
100
104
|
render({ validated }: RenderBlockInput): Promise<RenderBlockResult>;
|
|
101
105
|
apply({ rendered, installDependencies, }: ApplyBlockInput): Promise<ScaffoldProjectResult>;
|
|
@@ -7,10 +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 { getStarterManifestFiles } from "./starter-manifests.js";
|
|
11
|
+
import { resolveTemplateSeed, parseTemplateLocator } from "./template-source.js";
|
|
12
|
+
import { assertExternalTemplateLayersDoNotWriteProtectedOutputs, resolveExternalTemplateLayers, } from "./template-layers.js";
|
|
13
|
+
import { getBuiltInTemplateOverlayDir, getBuiltInTemplateSharedLayerDirs, resolveBuiltInTemplateSourceFromLayerDirs, } from "./template-builtins.js";
|
|
10
14
|
const renderedArtifactCache = new WeakMap();
|
|
11
15
|
function createVariablesFingerprint(variables) {
|
|
12
16
|
return JSON.stringify(variables);
|
|
13
17
|
}
|
|
18
|
+
function buildProtectedTemplateOutputPaths({ codeArtifacts, spec, variables, artifacts, }) {
|
|
19
|
+
const protectedOutputs = new Set([
|
|
20
|
+
".gitignore",
|
|
21
|
+
"package.json",
|
|
22
|
+
"scripts/add-compound-child.ts",
|
|
23
|
+
"scripts/block-config.ts",
|
|
24
|
+
"scripts/sync-project.ts",
|
|
25
|
+
"scripts/sync-rest-contracts.ts",
|
|
26
|
+
"scripts/sync-types-to-block-json.ts",
|
|
27
|
+
"tsconfig.json",
|
|
28
|
+
"webpack.config.js",
|
|
29
|
+
`${variables.slugKebabCase}.php`,
|
|
30
|
+
]);
|
|
31
|
+
for (const artifact of codeArtifacts) {
|
|
32
|
+
protectedOutputs.add(artifact.relativePath);
|
|
33
|
+
}
|
|
34
|
+
for (const artifact of artifacts) {
|
|
35
|
+
protectedOutputs.add(`${artifact.relativeDir}/block.json`);
|
|
36
|
+
protectedOutputs.add(`${artifact.relativeDir}/types.ts`);
|
|
37
|
+
}
|
|
38
|
+
for (const manifest of getStarterManifestFiles(spec.template.family, variables)) {
|
|
39
|
+
protectedOutputs.add(manifest.relativePath);
|
|
40
|
+
}
|
|
41
|
+
return protectedOutputs;
|
|
42
|
+
}
|
|
43
|
+
function buildCombinedTemplateLayerDirs({ baseLayerDirs, externalEntries, templateId, }) {
|
|
44
|
+
const orderedLayerDirs = [];
|
|
45
|
+
const seenLayerDirs = new Set();
|
|
46
|
+
for (const layerDir of baseLayerDirs) {
|
|
47
|
+
if (seenLayerDirs.has(layerDir)) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
orderedLayerDirs.push(layerDir);
|
|
51
|
+
seenLayerDirs.add(layerDir);
|
|
52
|
+
}
|
|
53
|
+
for (const entry of externalEntries) {
|
|
54
|
+
if (seenLayerDirs.has(entry.dir)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
orderedLayerDirs.push(entry.dir);
|
|
58
|
+
seenLayerDirs.add(entry.dir);
|
|
59
|
+
}
|
|
60
|
+
const overlayDir = getBuiltInTemplateOverlayDir(templateId);
|
|
61
|
+
if (!seenLayerDirs.has(overlayDir)) {
|
|
62
|
+
orderedLayerDirs.push(overlayDir);
|
|
63
|
+
}
|
|
64
|
+
return orderedLayerDirs;
|
|
65
|
+
}
|
|
14
66
|
function getBuiltInPersistenceSpec({ templateId, dataStorageMode, persistencePolicy, }) {
|
|
15
67
|
if (templateId === "persistence") {
|
|
16
68
|
return {
|
|
@@ -146,7 +198,7 @@ export function buildTemplateVariablesFromBlockSpec(spec) {
|
|
|
146
198
|
};
|
|
147
199
|
}
|
|
148
200
|
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, }) {
|
|
201
|
+
async plan({ allowExistingDir = false, answers, cwd = process.cwd(), dataStorageMode, externalLayerId, externalLayerSource, noInstall = false, packageManager, persistencePolicy, projectDir, templateId, variant, withMigrationUi = false, withTestPreset = false, withWpEnv = false, }) {
|
|
150
202
|
return {
|
|
151
203
|
spec: createBuiltInBlockSpec({
|
|
152
204
|
answers,
|
|
@@ -160,6 +212,8 @@ export class BlockGeneratorService {
|
|
|
160
212
|
target: {
|
|
161
213
|
allowExistingDir,
|
|
162
214
|
cwd,
|
|
215
|
+
externalLayerId,
|
|
216
|
+
externalLayerSource,
|
|
163
217
|
noInstall,
|
|
164
218
|
packageManager,
|
|
165
219
|
projectDir,
|
|
@@ -168,21 +222,90 @@ export class BlockGeneratorService {
|
|
|
168
222
|
};
|
|
169
223
|
}
|
|
170
224
|
async validate({ plan }) {
|
|
225
|
+
if (plan.target.externalLayerId && !plan.target.externalLayerSource) {
|
|
226
|
+
throw new Error("externalLayerId requires externalLayerSource when composing built-in template layers.");
|
|
227
|
+
}
|
|
171
228
|
if (plan.target.variant) {
|
|
172
229
|
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
230
|
}
|
|
174
231
|
return plan;
|
|
175
232
|
}
|
|
176
233
|
async render({ validated }) {
|
|
177
|
-
const
|
|
178
|
-
|
|
234
|
+
const variables = buildTemplateVariablesFromBlockSpec(validated.spec);
|
|
235
|
+
const persistenceEnabled = validated.spec.persistence.enabled;
|
|
236
|
+
const artifacts = buildBuiltInBlockArtifacts({
|
|
237
|
+
templateId: validated.spec.template.family,
|
|
238
|
+
variables,
|
|
239
|
+
});
|
|
240
|
+
const codeArtifacts = buildBuiltInCodeArtifacts({
|
|
241
|
+
templateId: validated.spec.template.family,
|
|
242
|
+
variables,
|
|
243
|
+
});
|
|
244
|
+
const templateVariantOptions = {
|
|
245
|
+
persistenceEnabled,
|
|
179
246
|
persistencePolicy: validated.spec.persistence.enabled &&
|
|
180
247
|
validated.spec.persistence.persistencePolicy === "public"
|
|
181
248
|
? "public"
|
|
182
249
|
: "authenticated",
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const
|
|
250
|
+
};
|
|
251
|
+
let templateSource = await resolveBuiltInTemplateSource(validated.spec.template.family, templateVariantOptions);
|
|
252
|
+
const warnings = [...(templateSource.warnings ?? [])];
|
|
253
|
+
if (validated.target.externalLayerSource) {
|
|
254
|
+
let layerSeed;
|
|
255
|
+
try {
|
|
256
|
+
layerSeed = await resolveTemplateSeed(parseTemplateLocator(validated.target.externalLayerSource), validated.target.cwd);
|
|
257
|
+
const resolvedLayers = await resolveExternalTemplateLayers({
|
|
258
|
+
externalLayerId: validated.target.externalLayerId,
|
|
259
|
+
sourceRoot: layerSeed.rootDir,
|
|
260
|
+
});
|
|
261
|
+
const baseLayerDirs = getBuiltInTemplateSharedLayerDirs(validated.spec.template.family, templateVariantOptions);
|
|
262
|
+
await assertExternalTemplateLayersDoNotWriteProtectedOutputs({
|
|
263
|
+
externalEntries: resolvedLayers.entries,
|
|
264
|
+
protectedOutputPaths: buildProtectedTemplateOutputPaths({
|
|
265
|
+
artifacts,
|
|
266
|
+
codeArtifacts,
|
|
267
|
+
spec: validated.spec,
|
|
268
|
+
variables,
|
|
269
|
+
}),
|
|
270
|
+
view: variables,
|
|
271
|
+
});
|
|
272
|
+
await templateSource.cleanup?.();
|
|
273
|
+
templateSource = await resolveBuiltInTemplateSourceFromLayerDirs(validated.spec.template.family, buildCombinedTemplateLayerDirs({
|
|
274
|
+
baseLayerDirs,
|
|
275
|
+
externalEntries: resolvedLayers.entries,
|
|
276
|
+
templateId: validated.spec.template.family,
|
|
277
|
+
}));
|
|
278
|
+
const layerSourceCleanup = layerSeed.cleanup;
|
|
279
|
+
const templateCleanup = templateSource.cleanup;
|
|
280
|
+
templateSource.cleanup = async () => {
|
|
281
|
+
const cleanupErrors = [];
|
|
282
|
+
try {
|
|
283
|
+
await templateCleanup?.();
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
cleanupErrors.push(error instanceof Error ? error : new Error(String(error)));
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
await layerSourceCleanup?.();
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
cleanupErrors.push(error instanceof Error ? error : new Error(String(error)));
|
|
293
|
+
}
|
|
294
|
+
if (cleanupErrors.length > 0) {
|
|
295
|
+
throw new Error([
|
|
296
|
+
"Failed to cleanup composed template sources.",
|
|
297
|
+
...cleanupErrors.map((error) => `- ${error.message}`),
|
|
298
|
+
].join("\n"));
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
warnings.push(`Applied external layer "${resolvedLayers.selectedLayerId}" from "${validated.target.externalLayerSource}".`);
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
await templateSource.cleanup?.();
|
|
305
|
+
await layerSeed?.cleanup?.();
|
|
306
|
+
throw error;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
186
309
|
const rendered = {
|
|
187
310
|
...validated,
|
|
188
311
|
cleanup: templateSource.cleanup,
|
|
@@ -202,17 +325,11 @@ export class BlockGeneratorService {
|
|
|
202
325
|
selectedVariant: null,
|
|
203
326
|
templateDir: templateSource.templateDir,
|
|
204
327
|
variables,
|
|
205
|
-
warnings
|
|
328
|
+
warnings,
|
|
206
329
|
};
|
|
207
330
|
renderedArtifactCache.set(rendered, {
|
|
208
|
-
artifacts
|
|
209
|
-
|
|
210
|
-
variables,
|
|
211
|
-
}),
|
|
212
|
-
codeArtifacts: buildBuiltInCodeArtifacts({
|
|
213
|
-
templateId: validated.spec.template.family,
|
|
214
|
-
variables,
|
|
215
|
-
}),
|
|
331
|
+
artifacts,
|
|
332
|
+
codeArtifacts,
|
|
216
333
|
variablesFingerprint: createVariablesFingerprint(variables),
|
|
217
334
|
});
|
|
218
335
|
return rendered;
|
|
@@ -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
|
+
}
|