@wp-typia/project-tools 0.16.12 → 0.16.14
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 +0 -1
- package/dist/runtime/block-generator-service-core.d.ts +8 -0
- package/dist/runtime/block-generator-service-core.js +274 -0
- package/dist/runtime/block-generator-service-spec.d.ts +104 -0
- package/dist/runtime/block-generator-service-spec.js +139 -0
- package/dist/runtime/block-generator-service.d.ts +2 -110
- package/dist/runtime/block-generator-service.js +2 -389
- package/dist/runtime/cli-diagnostics.js +76 -4
- package/dist/runtime/cli-doctor-workspace.d.ts +9 -5
- package/dist/runtime/cli-doctor-workspace.js +18 -6
- package/dist/runtime/cli-help.js +1 -1
- package/dist/runtime/cli-prompt.js +78 -19
- package/dist/runtime/cli-scaffold.d.ts +8 -1
- package/dist/runtime/cli-scaffold.js +47 -4
- package/dist/runtime/migration-maintenance-fixtures.d.ts +23 -0
- package/dist/runtime/migration-maintenance-fixtures.js +126 -0
- package/dist/runtime/migration-maintenance-verify.d.ts +26 -0
- package/dist/runtime/migration-maintenance-verify.js +262 -0
- package/dist/runtime/migration-maintenance.d.ts +2 -51
- package/dist/runtime/migration-maintenance.js +2 -380
- package/dist/runtime/migrations.d.ts +0 -3
- package/dist/runtime/scaffold-answer-resolution.d.ts +37 -0
- package/dist/runtime/scaffold-answer-resolution.js +138 -0
- package/dist/runtime/scaffold-apply-utils.d.ts +1 -7
- package/dist/runtime/scaffold-apply-utils.js +4 -105
- package/dist/runtime/scaffold-documents.d.ts +34 -0
- package/dist/runtime/scaffold-documents.js +144 -0
- package/dist/runtime/scaffold-onboarding.d.ts +12 -0
- package/dist/runtime/scaffold-onboarding.js +42 -5
- package/dist/runtime/scaffold-template-variables.d.ts +9 -0
- package/dist/runtime/scaffold-template-variables.js +111 -0
- package/dist/runtime/scaffold.d.ts +11 -9
- package/dist/runtime/scaffold.js +6 -202
- package/package.json +3 -3
|
@@ -1,389 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { getTemplateById, } from "./template-registry.js";
|
|
4
|
-
import { resolveBuiltInTemplateSource } from "./template-builtins.js";
|
|
5
|
-
import { toPascalCase, toSnakeCase, } from "./string-case.js";
|
|
6
|
-
import { applyBuiltInScaffoldProjectFiles, buildGitignore, buildReadme, } from "./scaffold-apply-utils.js";
|
|
7
|
-
import { buildBlockCssClassName, buildFrontendCssClassName, resolveScaffoldIdentifiers, } from "./scaffold-identifiers.js";
|
|
8
|
-
import { buildBuiltInBlockArtifacts, } from "./built-in-block-artifacts.js";
|
|
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";
|
|
15
|
-
const renderedArtifactCache = new WeakMap();
|
|
16
|
-
function createVariablesFingerprint(variables) {
|
|
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;
|
|
66
|
-
}
|
|
67
|
-
function getBuiltInPersistenceSpec({ templateId, dataStorageMode, persistencePolicy, }) {
|
|
68
|
-
if (templateId === "persistence") {
|
|
69
|
-
return {
|
|
70
|
-
dataStorageMode: dataStorageMode ?? "custom-table",
|
|
71
|
-
enabled: true,
|
|
72
|
-
persistencePolicy: persistencePolicy ?? "authenticated",
|
|
73
|
-
scope: "single",
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
if (templateId === "compound" && (dataStorageMode || persistencePolicy)) {
|
|
77
|
-
return {
|
|
78
|
-
dataStorageMode: dataStorageMode ?? "custom-table",
|
|
79
|
-
enabled: true,
|
|
80
|
-
persistencePolicy: persistencePolicy ?? "authenticated",
|
|
81
|
-
scope: "compound-parent",
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
enabled: false,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
export function createBuiltInBlockSpec({ answers, dataStorageMode, persistencePolicy, templateId, withMigrationUi = false, withTestPreset = false, withWpEnv = false, }) {
|
|
89
|
-
const template = getTemplateById(templateId);
|
|
90
|
-
const metadataDefaults = getBuiltInTemplateMetadataDefaults(templateId);
|
|
91
|
-
const identifiers = resolveScaffoldIdentifiers({
|
|
92
|
-
namespace: answers.namespace,
|
|
93
|
-
phpPrefix: answers.phpPrefix,
|
|
94
|
-
slug: answers.slug,
|
|
95
|
-
textDomain: answers.textDomain,
|
|
96
|
-
});
|
|
97
|
-
const resolvedDataStorageMode = dataStorageMode ?? answers.dataStorageMode;
|
|
98
|
-
const resolvedPersistencePolicy = persistencePolicy ?? answers.persistencePolicy;
|
|
99
|
-
return {
|
|
100
|
-
block: identifiers,
|
|
101
|
-
metadata: {
|
|
102
|
-
category: metadataDefaults.category,
|
|
103
|
-
description: answers.description.trim(),
|
|
104
|
-
icon: metadataDefaults.icon,
|
|
105
|
-
keyword: identifiers.slug.replace(/-/g, " "),
|
|
106
|
-
title: answers.title.trim(),
|
|
107
|
-
},
|
|
108
|
-
persistence: getBuiltInPersistenceSpec({
|
|
109
|
-
dataStorageMode: resolvedDataStorageMode,
|
|
110
|
-
persistencePolicy: resolvedPersistencePolicy,
|
|
111
|
-
templateId,
|
|
112
|
-
}),
|
|
113
|
-
project: {
|
|
114
|
-
author: answers.author.trim(),
|
|
115
|
-
},
|
|
116
|
-
runtime: {
|
|
117
|
-
withMigrationUi,
|
|
118
|
-
withTestPreset,
|
|
119
|
-
withWpEnv,
|
|
120
|
-
},
|
|
121
|
-
template: {
|
|
122
|
-
description: template.description,
|
|
123
|
-
family: templateId,
|
|
124
|
-
features: [...template.features],
|
|
125
|
-
},
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
export function buildTemplateVariablesFromBlockSpec(spec) {
|
|
129
|
-
const { apiClientPackageVersion, blockRuntimePackageVersion, blockTypesPackageVersion, projectToolsPackageVersion, restPackageVersion, } = getPackageVersions();
|
|
130
|
-
const slug = spec.block.slug;
|
|
131
|
-
const slugSnakeCase = toSnakeCase(slug);
|
|
132
|
-
const pascalCase = toPascalCase(slug);
|
|
133
|
-
const title = spec.metadata.title;
|
|
134
|
-
const namespace = spec.block.namespace;
|
|
135
|
-
const textDomain = spec.block.textDomain;
|
|
136
|
-
const phpPrefix = spec.block.phpPrefix;
|
|
137
|
-
const phpPrefixUpper = phpPrefix.toUpperCase();
|
|
138
|
-
const compoundChildTitle = `${title} Item`;
|
|
139
|
-
const cssClassName = buildBlockCssClassName(namespace, slug);
|
|
140
|
-
const compoundChildCssClassName = buildBlockCssClassName(namespace, `${slug}-item`);
|
|
141
|
-
const persistenceEnabled = spec.persistence.enabled;
|
|
142
|
-
const dataStorageMode = persistenceEnabled ? spec.persistence.dataStorageMode : "custom-table";
|
|
143
|
-
const persistencePolicy = persistenceEnabled
|
|
144
|
-
? spec.persistence.persistencePolicy
|
|
145
|
-
: "authenticated";
|
|
146
|
-
return {
|
|
147
|
-
apiClientPackageVersion,
|
|
148
|
-
author: spec.project.author,
|
|
149
|
-
blockRuntimePackageVersion,
|
|
150
|
-
blockMetadataVersion: BUILTIN_BLOCK_METADATA_VERSION,
|
|
151
|
-
blockTypesPackageVersion,
|
|
152
|
-
category: spec.metadata.category,
|
|
153
|
-
icon: spec.metadata.icon,
|
|
154
|
-
compoundChildTitle,
|
|
155
|
-
compoundChildCategory: COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS.category,
|
|
156
|
-
compoundChildCssClassName,
|
|
157
|
-
compoundChildIcon: COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS.icon,
|
|
158
|
-
compoundChildTitleJson: JSON.stringify(compoundChildTitle),
|
|
159
|
-
compoundPersistenceEnabled: spec.template.family === "compound" && persistenceEnabled ? "true" : "false",
|
|
160
|
-
projectToolsPackageVersion,
|
|
161
|
-
cssClassName,
|
|
162
|
-
dashCase: slug,
|
|
163
|
-
dataStorageMode,
|
|
164
|
-
description: spec.metadata.description,
|
|
165
|
-
frontendCssClassName: buildFrontendCssClassName(cssClassName),
|
|
166
|
-
isAuthenticatedPersistencePolicy: persistencePolicy === "authenticated" ? "true" : "false",
|
|
167
|
-
isPublicPersistencePolicy: persistencePolicy === "public" ? "true" : "false",
|
|
168
|
-
bootstrapCredentialDeclarations: persistencePolicy === "public"
|
|
169
|
-
? "publicWriteExpiresAt?: number & tags.Type< 'uint32' >;\n\tpublicWriteToken?: string & tags.MinLength< 1 > & tags.MaxLength< 512 >;"
|
|
170
|
-
: "restNonce?: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;",
|
|
171
|
-
persistencePolicyDescriptionJson: JSON.stringify(persistencePolicy === "authenticated"
|
|
172
|
-
? "Writes require a logged-in user and a valid REST nonce."
|
|
173
|
-
: "Anonymous writes use signed short-lived public tokens, per-request ids, and coarse rate limiting."),
|
|
174
|
-
keyword: spec.metadata.keyword,
|
|
175
|
-
namespace,
|
|
176
|
-
needsMigration: "{{needsMigration}}",
|
|
177
|
-
pascalCase,
|
|
178
|
-
phpPrefix,
|
|
179
|
-
phpPrefixUpper,
|
|
180
|
-
restPackageVersion,
|
|
181
|
-
publicWriteRequestIdDeclaration: persistencePolicy === "public"
|
|
182
|
-
? "publicWriteRequestId: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;"
|
|
183
|
-
: "publicWriteRequestId?: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;",
|
|
184
|
-
restWriteAuthIntent: persistencePolicy === "public"
|
|
185
|
-
? "public-write-protected"
|
|
186
|
-
: "authenticated",
|
|
187
|
-
restWriteAuthMechanism: persistencePolicy === "public" ? "public-signed-token" : "rest-nonce",
|
|
188
|
-
restWriteAuthMode: persistencePolicy === "public" ? "public-signed-token" : "authenticated-rest-nonce",
|
|
189
|
-
slug,
|
|
190
|
-
slugCamelCase: pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1),
|
|
191
|
-
slugKebabCase: slug,
|
|
192
|
-
slugSnakeCase,
|
|
193
|
-
textDomain,
|
|
194
|
-
textdomain: textDomain,
|
|
195
|
-
title,
|
|
196
|
-
titleJson: JSON.stringify(title),
|
|
197
|
-
titleCase: pascalCase,
|
|
198
|
-
persistencePolicy,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
export class BlockGeneratorService {
|
|
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, }) {
|
|
203
|
-
return {
|
|
204
|
-
spec: createBuiltInBlockSpec({
|
|
205
|
-
answers,
|
|
206
|
-
dataStorageMode,
|
|
207
|
-
persistencePolicy,
|
|
208
|
-
templateId,
|
|
209
|
-
withMigrationUi,
|
|
210
|
-
withTestPreset,
|
|
211
|
-
withWpEnv,
|
|
212
|
-
}),
|
|
213
|
-
target: {
|
|
214
|
-
allowExistingDir,
|
|
215
|
-
cwd,
|
|
216
|
-
externalLayerId,
|
|
217
|
-
externalLayerSource,
|
|
218
|
-
externalLayerSourceLabel,
|
|
219
|
-
noInstall,
|
|
220
|
-
packageManager,
|
|
221
|
-
projectDir,
|
|
222
|
-
repositoryReference,
|
|
223
|
-
variant,
|
|
224
|
-
},
|
|
225
|
-
};
|
|
226
|
-
}
|
|
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
|
-
}
|
|
231
|
-
if (plan.target.variant) {
|
|
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}".`);
|
|
233
|
-
}
|
|
234
|
-
return plan;
|
|
235
|
-
}
|
|
236
|
-
async render({ validated }) {
|
|
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,
|
|
249
|
-
persistencePolicy: validated.spec.persistence.enabled &&
|
|
250
|
-
validated.spec.persistence.persistencePolicy === "public"
|
|
251
|
-
? "public"
|
|
252
|
-
: "authenticated",
|
|
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
|
-
}
|
|
312
|
-
const rendered = {
|
|
313
|
-
...validated,
|
|
314
|
-
cleanup: templateSource.cleanup,
|
|
315
|
-
gitignoreContent: buildGitignore(),
|
|
316
|
-
postRender: {
|
|
317
|
-
applyLocalDevPresets: true,
|
|
318
|
-
applyMigrationUiCapability: validated.spec.runtime.withMigrationUi,
|
|
319
|
-
seedPersistenceArtifacts: validated.spec.template.family === "persistence" ||
|
|
320
|
-
(validated.spec.template.family === "compound" && persistenceEnabled),
|
|
321
|
-
seedStarterManifestFiles: true,
|
|
322
|
-
},
|
|
323
|
-
readmeContent: buildReadme(validated.spec.template.family, variables, validated.target.packageManager, {
|
|
324
|
-
withMigrationUi: validated.spec.runtime.withMigrationUi,
|
|
325
|
-
withTestPreset: validated.spec.runtime.withTestPreset,
|
|
326
|
-
withWpEnv: validated.spec.runtime.withWpEnv,
|
|
327
|
-
}),
|
|
328
|
-
selectedVariant: null,
|
|
329
|
-
templateDir: templateSource.templateDir,
|
|
330
|
-
variables,
|
|
331
|
-
warnings,
|
|
332
|
-
};
|
|
333
|
-
renderedArtifactCache.set(rendered, {
|
|
334
|
-
artifacts,
|
|
335
|
-
codeArtifacts,
|
|
336
|
-
variablesFingerprint: createVariablesFingerprint(variables),
|
|
337
|
-
});
|
|
338
|
-
return rendered;
|
|
339
|
-
}
|
|
340
|
-
async apply({ rendered, installDependencies, }) {
|
|
341
|
-
const cachedArtifacts = renderedArtifactCache.get(rendered);
|
|
342
|
-
const currentVariablesFingerprint = createVariablesFingerprint(rendered.variables);
|
|
343
|
-
const artifacts = cachedArtifacts &&
|
|
344
|
-
cachedArtifacts.variablesFingerprint === currentVariablesFingerprint
|
|
345
|
-
? cachedArtifacts.artifacts
|
|
346
|
-
: buildBuiltInBlockArtifacts({
|
|
347
|
-
templateId: rendered.spec.template.family,
|
|
348
|
-
variables: rendered.variables,
|
|
349
|
-
});
|
|
350
|
-
const codeArtifacts = cachedArtifacts &&
|
|
351
|
-
cachedArtifacts.variablesFingerprint === currentVariablesFingerprint
|
|
352
|
-
? cachedArtifacts.codeArtifacts
|
|
353
|
-
: buildBuiltInCodeArtifacts({
|
|
354
|
-
templateId: rendered.spec.template.family,
|
|
355
|
-
variables: rendered.variables,
|
|
356
|
-
});
|
|
357
|
-
try {
|
|
358
|
-
await applyBuiltInScaffoldProjectFiles({
|
|
359
|
-
allowExistingDir: rendered.target.allowExistingDir,
|
|
360
|
-
artifacts,
|
|
361
|
-
codeArtifacts,
|
|
362
|
-
installDependencies,
|
|
363
|
-
noInstall: rendered.target.noInstall,
|
|
364
|
-
packageManager: rendered.target.packageManager,
|
|
365
|
-
projectDir: rendered.target.projectDir,
|
|
366
|
-
repositoryReference: rendered.target.repositoryReference,
|
|
367
|
-
gitignoreContent: rendered.gitignoreContent,
|
|
368
|
-
readmeContent: rendered.readmeContent,
|
|
369
|
-
templateDir: rendered.templateDir,
|
|
370
|
-
templateId: rendered.spec.template.family,
|
|
371
|
-
variables: rendered.variables,
|
|
372
|
-
withMigrationUi: rendered.spec.runtime.withMigrationUi,
|
|
373
|
-
withTestPreset: rendered.spec.runtime.withTestPreset,
|
|
374
|
-
withWpEnv: rendered.spec.runtime.withWpEnv,
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
finally {
|
|
378
|
-
await rendered.cleanup?.();
|
|
379
|
-
}
|
|
380
|
-
return {
|
|
381
|
-
packageManager: rendered.target.packageManager,
|
|
382
|
-
projectDir: rendered.target.projectDir,
|
|
383
|
-
selectedVariant: rendered.selectedVariant,
|
|
384
|
-
templateId: rendered.spec.template.family,
|
|
385
|
-
variables: rendered.variables,
|
|
386
|
-
warnings: rendered.warnings,
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
}
|
|
1
|
+
export * from "./block-generator-service-spec.js";
|
|
2
|
+
export * from "./block-generator-service-core.js";
|
|
@@ -4,12 +4,84 @@ const DEFAULT_CLI_FAILURE_SUMMARIES = {
|
|
|
4
4
|
doctor: "One or more doctor checks failed.",
|
|
5
5
|
migrate: "Unable to complete the requested migration command.",
|
|
6
6
|
};
|
|
7
|
+
const MIN_CLI_WRAP_COLUMNS = 32;
|
|
8
|
+
function parseCliColumns(value) {
|
|
9
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const parsed = Number.parseInt(value, 10);
|
|
13
|
+
return Number.isFinite(parsed) && parsed >= MIN_CLI_WRAP_COLUMNS ? parsed : null;
|
|
14
|
+
}
|
|
15
|
+
function resolveCliWrapColumns(streamColumns) {
|
|
16
|
+
return parseCliColumns(process.env.COLUMNS) ??
|
|
17
|
+
(typeof streamColumns === "number" && streamColumns >= MIN_CLI_WRAP_COLUMNS
|
|
18
|
+
? streamColumns
|
|
19
|
+
: null);
|
|
20
|
+
}
|
|
21
|
+
function wrapCliText(text, maxWidth) {
|
|
22
|
+
const words = text.trim().split(/\s+/u).filter((word) => word.length > 0);
|
|
23
|
+
if (words.length === 0) {
|
|
24
|
+
return [""];
|
|
25
|
+
}
|
|
26
|
+
const lines = [];
|
|
27
|
+
let currentLine = words[0] ?? "";
|
|
28
|
+
for (const word of words.slice(1)) {
|
|
29
|
+
const nextLine = `${currentLine} ${word}`;
|
|
30
|
+
if (nextLine.length <= maxWidth) {
|
|
31
|
+
currentLine = nextLine;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
lines.push(currentLine);
|
|
35
|
+
currentLine = word;
|
|
36
|
+
}
|
|
37
|
+
lines.push(currentLine);
|
|
38
|
+
return lines;
|
|
39
|
+
}
|
|
40
|
+
function formatWrappedPrefixedLine(prefix, text, columns, continuationIndent = " ") {
|
|
41
|
+
const singleLine = `${prefix}${text}`;
|
|
42
|
+
if (columns === null || singleLine.length <= columns) {
|
|
43
|
+
return [singleLine];
|
|
44
|
+
}
|
|
45
|
+
const words = text.trim().split(/\s+/u).filter((word) => word.length > 0);
|
|
46
|
+
if (words.length === 0) {
|
|
47
|
+
return [prefix.trimEnd()];
|
|
48
|
+
}
|
|
49
|
+
const continuationWidth = Math.max(1, columns - continuationIndent.length);
|
|
50
|
+
const firstLineWidth = columns - prefix.length;
|
|
51
|
+
if (firstLineWidth <= 0 || (words[0]?.length ?? 0) > firstLineWidth) {
|
|
52
|
+
return [
|
|
53
|
+
prefix.trimEnd(),
|
|
54
|
+
...wrapCliText(text, continuationWidth).map((line) => `${continuationIndent}${line}`),
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
const lines = [];
|
|
58
|
+
let currentPrefix = prefix;
|
|
59
|
+
let currentWidth = Math.max(1, columns - currentPrefix.length);
|
|
60
|
+
let currentLine = words[0] ?? "";
|
|
61
|
+
for (const word of words.slice(1)) {
|
|
62
|
+
const nextLine = `${currentLine} ${word}`;
|
|
63
|
+
if (nextLine.length <= currentWidth) {
|
|
64
|
+
currentLine = nextLine;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
lines.push(`${currentPrefix}${currentLine}`);
|
|
68
|
+
currentPrefix = continuationIndent;
|
|
69
|
+
currentWidth = continuationWidth;
|
|
70
|
+
currentLine = word;
|
|
71
|
+
}
|
|
72
|
+
lines.push(`${currentPrefix}${currentLine}`);
|
|
73
|
+
return lines;
|
|
74
|
+
}
|
|
7
75
|
function formatCliDiagnosticBlock(message) {
|
|
8
|
-
const
|
|
76
|
+
const columns = resolveCliWrapColumns(process.stderr.columns);
|
|
77
|
+
const lines = [
|
|
78
|
+
`wp-typia ${message.command} failed`,
|
|
79
|
+
...formatWrappedPrefixedLine("Summary: ", message.summary, columns),
|
|
80
|
+
];
|
|
9
81
|
if (message.detailLines.length > 0) {
|
|
10
82
|
lines.push("Details:");
|
|
11
83
|
for (const detailLine of message.detailLines) {
|
|
12
|
-
lines.push(
|
|
84
|
+
lines.push(...formatWrappedPrefixedLine("- ", detailLine, columns));
|
|
13
85
|
}
|
|
14
86
|
}
|
|
15
87
|
return lines.join("\n");
|
|
@@ -77,7 +149,7 @@ export function formatCliDiagnosticError(error) {
|
|
|
77
149
|
* Format one human-readable doctor check row.
|
|
78
150
|
*/
|
|
79
151
|
export function formatDoctorCheckLine(check) {
|
|
80
|
-
return `${check.status === "pass" ? "PASS" : "FAIL"} ${check.label}:
|
|
152
|
+
return formatWrappedPrefixedLine(`${check.status === "pass" ? "PASS" : "FAIL"} ${check.label}: `, check.detail, resolveCliWrapColumns(process.stdout.columns)).join("\n");
|
|
81
153
|
}
|
|
82
154
|
/**
|
|
83
155
|
* Return the failing doctor checks from one doctor run.
|
|
@@ -90,7 +162,7 @@ export function getFailingDoctorChecks(checks) {
|
|
|
90
162
|
*/
|
|
91
163
|
export function formatDoctorSummaryLine(checks) {
|
|
92
164
|
const failedChecks = getFailingDoctorChecks(checks);
|
|
93
|
-
return `${failedChecks.length === 0 ? "PASS" : "FAIL"} wp-typia doctor summary:
|
|
165
|
+
return formatWrappedPrefixedLine(`${failedChecks.length === 0 ? "PASS" : "FAIL"} wp-typia doctor summary: `, `${checks.length - failedChecks.length}/${checks.length} checks passed`, resolveCliWrapColumns(process.stdout.columns)).join("\n");
|
|
94
166
|
}
|
|
95
167
|
/**
|
|
96
168
|
* Build detail lines for doctor failures so the non-interactive formatter can
|
|
@@ -2,11 +2,15 @@ import type { DoctorCheck } from "./cli-doctor.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* Collect workspace-scoped doctor checks for the given working directory.
|
|
4
4
|
*
|
|
5
|
-
* When the directory is not an official workspace, the function returns
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* skipped.
|
|
5
|
+
* When the directory is not an official workspace, the function returns a
|
|
6
|
+
* "Doctor scope" row explaining that only environment checks ran, plus a
|
|
7
|
+
* failing workspace metadata row when a nearby candidate workspace is invalid.
|
|
8
|
+
* When workspace resolution or metadata parsing throws, the corresponding
|
|
9
|
+
* failing rows are returned early and the remaining checks are skipped.
|
|
10
|
+
* When an official workspace is detected, a passing "Doctor scope" row is
|
|
11
|
+
* emitted first so the remaining package metadata, inventory, source-tree
|
|
12
|
+
* drift, and optional migration hint rows are clearly framed as workspace
|
|
13
|
+
* diagnostics for that run.
|
|
10
14
|
*
|
|
11
15
|
* @param cwd Working directory expected to host an official workspace.
|
|
12
16
|
* @returns Ordered workspace check rows ready for CLI rendering.
|
|
@@ -19,6 +19,9 @@ const WORKSPACE_GENERATED_BLOCK_ARTIFACTS = [
|
|
|
19
19
|
function createDoctorCheck(label, status, detail) {
|
|
20
20
|
return { detail, label, status };
|
|
21
21
|
}
|
|
22
|
+
function createDoctorScopeCheck(status, detail) {
|
|
23
|
+
return createDoctorCheck("Doctor scope", status, detail);
|
|
24
|
+
}
|
|
22
25
|
function getWorkspaceBootstrapRelativePath(packageName) {
|
|
23
26
|
const packageBaseName = packageName.split("/").pop() ?? packageName;
|
|
24
27
|
return `${packageBaseName}.php`;
|
|
@@ -207,11 +210,15 @@ function checkMigrationWorkspaceHint(workspace, packageJson) {
|
|
|
207
210
|
/**
|
|
208
211
|
* Collect workspace-scoped doctor checks for the given working directory.
|
|
209
212
|
*
|
|
210
|
-
* When the directory is not an official workspace, the function returns
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* skipped.
|
|
213
|
+
* When the directory is not an official workspace, the function returns a
|
|
214
|
+
* "Doctor scope" row explaining that only environment checks ran, plus a
|
|
215
|
+
* failing workspace metadata row when a nearby candidate workspace is invalid.
|
|
216
|
+
* When workspace resolution or metadata parsing throws, the corresponding
|
|
217
|
+
* failing rows are returned early and the remaining checks are skipped.
|
|
218
|
+
* When an official workspace is detected, a passing "Doctor scope" row is
|
|
219
|
+
* emitted first so the remaining package metadata, inventory, source-tree
|
|
220
|
+
* drift, and optional migration hint rows are clearly framed as workspace
|
|
221
|
+
* diagnostics for that run.
|
|
215
222
|
*
|
|
216
223
|
* @param cwd Working directory expected to host an official workspace.
|
|
217
224
|
* @returns Ordered workspace check rows ready for CLI rendering.
|
|
@@ -225,16 +232,21 @@ export function getWorkspaceDoctorChecks(cwd) {
|
|
|
225
232
|
workspace = tryResolveWorkspaceProject(cwd);
|
|
226
233
|
}
|
|
227
234
|
catch (error) {
|
|
235
|
+
checks.push(createDoctorScopeCheck("fail", "Environment checks ran, but workspace discovery could not continue. Fix the nearby workspace package metadata and rerun `wp-typia doctor`."));
|
|
228
236
|
checks.push(createDoctorCheck("Workspace package metadata", "fail", error instanceof Error ? error.message : String(error)));
|
|
229
237
|
return checks;
|
|
230
238
|
}
|
|
231
239
|
if (!workspace) {
|
|
232
240
|
if (invalidWorkspaceReason) {
|
|
241
|
+
checks.push(createDoctorScopeCheck("fail", "Environment checks ran, but workspace diagnostics could not continue because a nearby wp-typia workspace candidate is invalid. Fix the workspace package metadata and rerun `wp-typia doctor`."));
|
|
233
242
|
checks.push(createDoctorCheck("Workspace package metadata", "fail", invalidWorkspaceReason));
|
|
234
243
|
}
|
|
244
|
+
else {
|
|
245
|
+
checks.push(createDoctorScopeCheck("pass", "No official wp-typia workspace root was detected, so this run only covered environment readiness. Re-run `wp-typia doctor` from a workspace root if you expected package metadata, inventory, or generated artifact checks."));
|
|
246
|
+
}
|
|
235
247
|
return checks;
|
|
236
248
|
}
|
|
237
|
-
checks.push(
|
|
249
|
+
checks.push(createDoctorScopeCheck("pass", `Official workspace detected for ${workspace.workspace.namespace}; environment readiness checks ran and workspace-scoped diagnostics are enabled for the package metadata, inventory, source-tree drift, and any configured migration hint rows below.`));
|
|
238
250
|
let workspacePackageJson;
|
|
239
251
|
try {
|
|
240
252
|
workspacePackageJson = parseWorkspacePackageJson(workspace.projectDir);
|
package/dist/runtime/cli-help.js
CHANGED
|
@@ -39,7 +39,7 @@ Notes:
|
|
|
39
39
|
\`add pattern\` scaffolds a namespaced PHP pattern shell under \`src/patterns/\`.
|
|
40
40
|
\`add binding-source\` scaffolds shared PHP and editor registration under \`src/bindings/\`.
|
|
41
41
|
\`add hooked-block\` patches an existing workspace block's \`block.json\` \`blockHooks\` metadata.
|
|
42
|
-
\`wp-typia doctor\` checks environment readiness
|
|
42
|
+
\`wp-typia doctor\` always checks environment readiness and reports when it only ran environment-level diagnostics; official workspace roots also get inventory and source-tree drift checks.
|
|
43
43
|
\`wp-typia migrate doctor --all\` checks migration target alignment, snapshots, fixtures, and generated migration artifacts.
|
|
44
44
|
\`migrate\` is the canonical migration command; \`migrations\` is no longer supported.`;
|
|
45
45
|
}
|