@wp-typia/project-tools 0.16.10 → 0.16.12
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 +9 -3
- package/dist/runtime/built-in-block-artifact-documents.d.ts +3 -0
- package/dist/runtime/built-in-block-artifact-documents.js +2 -0
- package/dist/runtime/built-in-block-artifact-types.d.ts +51 -0
- package/dist/runtime/built-in-block-artifact-types.js +304 -0
- package/dist/runtime/built-in-block-artifacts.js +4 -803
- package/dist/runtime/built-in-block-attribute-emitters.d.ts +71 -0
- package/dist/runtime/built-in-block-attribute-emitters.js +176 -0
- package/dist/runtime/built-in-block-attribute-specs.d.ts +38 -0
- package/dist/runtime/built-in-block-attribute-specs.js +358 -0
- package/dist/runtime/built-in-block-code-templates/basic.d.ts +4 -0
- package/dist/runtime/built-in-block-code-templates/basic.js +249 -0
- package/dist/runtime/built-in-block-code-templates/compound-child.d.ts +4 -0
- package/dist/runtime/built-in-block-code-templates/compound-child.js +138 -0
- package/dist/runtime/built-in-block-code-templates/compound-parent.d.ts +6 -0
- package/dist/runtime/built-in-block-code-templates/compound-parent.js +227 -0
- package/dist/runtime/built-in-block-code-templates/compound-persistence.d.ts +4 -0
- package/dist/runtime/built-in-block-code-templates/compound-persistence.js +478 -0
- package/dist/runtime/built-in-block-code-templates/compound.d.ts +3 -0
- package/dist/runtime/built-in-block-code-templates/compound.js +3 -0
- package/dist/runtime/built-in-block-code-templates/interactivity.d.ts +5 -0
- package/dist/runtime/built-in-block-code-templates/interactivity.js +547 -0
- package/dist/runtime/built-in-block-code-templates/persistence.d.ts +5 -0
- package/dist/runtime/built-in-block-code-templates/persistence.js +550 -0
- package/dist/runtime/built-in-block-code-templates/shared.d.ts +16 -0
- package/dist/runtime/built-in-block-code-templates/shared.js +53 -0
- package/dist/runtime/built-in-block-code-templates.d.ts +5 -32
- package/dist/runtime/built-in-block-code-templates.js +5 -2230
- package/dist/runtime/cli-add-block-config.d.ts +6 -0
- package/dist/runtime/cli-add-block-config.js +143 -0
- package/dist/runtime/cli-add-block-legacy-validator.d.ts +4 -0
- package/dist/runtime/cli-add-block-legacy-validator.js +168 -0
- package/dist/runtime/cli-add-block.js +3 -301
- package/dist/runtime/cli-add-workspace-assets.d.ts +38 -0
- package/dist/runtime/cli-add-workspace-assets.js +399 -0
- package/dist/runtime/cli-add-workspace.d.ts +2 -38
- package/dist/runtime/cli-add-workspace.js +5 -396
- package/dist/runtime/cli-doctor-environment.d.ts +12 -0
- package/dist/runtime/cli-doctor-environment.js +123 -0
- package/dist/runtime/cli-doctor-workspace.d.ts +14 -0
- package/dist/runtime/cli-doctor-workspace.js +296 -0
- package/dist/runtime/cli-doctor.d.ts +4 -2
- package/dist/runtime/cli-doctor.js +10 -405
- package/dist/runtime/cli-help.js +1 -1
- package/dist/runtime/cli-scaffold.js +1 -1
- package/dist/runtime/migration-command-surface.d.ts +67 -0
- package/dist/runtime/migration-command-surface.js +189 -0
- package/dist/runtime/migration-diff-rename.d.ts +13 -0
- package/dist/runtime/migration-diff-rename.js +192 -0
- package/dist/runtime/migration-diff-transform.d.ts +14 -0
- package/dist/runtime/migration-diff-transform.js +105 -0
- package/dist/runtime/migration-diff.js +12 -297
- package/dist/runtime/migration-generated-artifacts.d.ts +3 -0
- package/dist/runtime/migration-generated-artifacts.js +41 -0
- package/dist/runtime/migration-maintenance.d.ts +51 -0
- package/dist/runtime/migration-maintenance.js +380 -0
- package/dist/runtime/migration-planning.d.ts +23 -0
- package/dist/runtime/migration-planning.js +131 -0
- package/dist/runtime/migration-project-config-source.d.ts +6 -0
- package/dist/runtime/migration-project-config-source.js +424 -0
- package/dist/runtime/migration-project-layout-discovery.d.ts +61 -0
- package/dist/runtime/migration-project-layout-discovery.js +337 -0
- package/dist/runtime/migration-project-layout-paths.d.ts +135 -0
- package/dist/runtime/migration-project-layout-paths.js +288 -0
- package/dist/runtime/migration-project-layout.d.ts +3 -0
- package/dist/runtime/migration-project-layout.js +2 -0
- package/dist/runtime/migration-project-workspace.d.ts +47 -0
- package/dist/runtime/migration-project-workspace.js +212 -0
- package/dist/runtime/migration-project.d.ts +4 -94
- package/dist/runtime/migration-project.js +3 -1101
- package/dist/runtime/migration-render-diff-rule.d.ts +5 -0
- package/dist/runtime/migration-render-diff-rule.js +120 -0
- package/dist/runtime/migration-render-execution.d.ts +3 -0
- package/dist/runtime/migration-render-execution.js +428 -0
- package/dist/runtime/migration-render-generated.d.ts +27 -0
- package/dist/runtime/migration-render-generated.js +230 -0
- package/dist/runtime/migration-render-support.d.ts +3 -0
- package/dist/runtime/migration-render-support.js +16 -0
- package/dist/runtime/migration-render.d.ts +3 -33
- package/dist/runtime/migration-render.js +3 -789
- package/dist/runtime/migration-ui-capability.js +1 -1
- package/dist/runtime/migrations.d.ts +24 -118
- package/dist/runtime/migrations.js +12 -700
- package/dist/runtime/scaffold-bootstrap.d.ts +45 -0
- package/dist/runtime/scaffold-bootstrap.js +185 -0
- package/dist/runtime/scaffold-package-manager-files.d.ts +35 -0
- package/dist/runtime/scaffold-package-manager-files.js +79 -0
- package/dist/runtime/scaffold.d.ts +1 -12
- package/dist/runtime/scaffold.js +10 -393
- package/dist/runtime/template-source-contracts.d.ts +81 -0
- package/dist/runtime/template-source-contracts.js +1 -0
- package/dist/runtime/template-source-external.d.ts +21 -0
- package/dist/runtime/template-source-external.js +184 -0
- package/dist/runtime/template-source-locators.d.ts +4 -0
- package/dist/runtime/template-source-locators.js +72 -0
- package/dist/runtime/template-source-normalization.d.ts +7 -0
- package/dist/runtime/template-source-normalization.js +53 -0
- package/dist/runtime/template-source-remote.d.ts +23 -0
- package/dist/runtime/template-source-remote.js +336 -0
- package/dist/runtime/template-source-seeds.d.ts +12 -0
- package/dist/runtime/template-source-seeds.js +243 -0
- package/dist/runtime/template-source.d.ts +4 -86
- package/dist/runtime/template-source.js +9 -828
- package/package.json +5 -5
package/dist/runtime/scaffold.js
CHANGED
|
@@ -2,112 +2,24 @@ import fs from "node:fs";
|
|
|
2
2
|
import { promises as fsp } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { execSync } from "node:child_process";
|
|
5
|
-
import { PACKAGE_MANAGER_IDS,
|
|
6
|
-
import { replaceTextRecursively } from "./scaffold-apply-utils.js";
|
|
7
|
-
import {
|
|
5
|
+
import { PACKAGE_MANAGER_IDS, getPackageManager, } from "./package-managers.js";
|
|
6
|
+
import { buildGitignore, buildReadme, mergeTextLines, replaceTextRecursively, } from "./scaffold-apply-utils.js";
|
|
7
|
+
import { buildBlockCssClassName, buildFrontendCssClassName, normalizeBlockSlug, resolveScaffoldIdentifiers, validateBlockSlug, validateNamespace, } from "./scaffold-identifiers.js";
|
|
8
|
+
import { applyGeneratedProjectDxPackageJson, applyLocalDevPresetFiles, } from "./local-dev-presets.js";
|
|
8
9
|
import { applyMigrationUiCapability } from "./migration-ui-capability.js";
|
|
9
10
|
import { getPackageVersions } from "./package-versions.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { getStarterManifestFiles, stringifyStarterManifest } from "./starter-manifests.js";
|
|
14
|
-
import { toKebabCase, toPascalCase, toSnakeCase, toTitleCase, } from "./string-case.js";
|
|
11
|
+
import { applyWorkspaceMigrationCapability, ensureScaffoldDirectory, isOfficialWorkspaceProject, seedBuiltInPersistenceArtifacts, writeStarterManifestFiles, } from "./scaffold-bootstrap.js";
|
|
12
|
+
import { defaultInstallDependencies, normalizePackageJson, normalizePackageManagerFiles, removeUnexpectedLockfiles, } from "./scaffold-package-manager-files.js";
|
|
13
|
+
import { toPascalCase, toSnakeCase, toTitleCase, } from "./string-case.js";
|
|
15
14
|
import { BUILTIN_BLOCK_METADATA_VERSION, COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS, getBuiltInTemplateMetadataDefaults, getRemovedBuiltInTemplateMessage, isRemovedBuiltInTemplateId, } from "./template-defaults.js";
|
|
16
15
|
import { copyInterpolatedDirectory } from "./template-render.js";
|
|
17
|
-
import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE,
|
|
16
|
+
import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, TEMPLATE_IDS, getTemplateById, isBuiltInTemplateId, } from "./template-registry.js";
|
|
18
17
|
import { resolveTemplateSource } from "./template-source.js";
|
|
19
18
|
import { BlockGeneratorService, buildTemplateVariablesFromBlockSpec, createBuiltInBlockSpec, } from "./block-generator-service.js";
|
|
20
|
-
const BLOCK_SLUG_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
21
|
-
const PHP_PREFIX_PATTERN = /^[a-z_][a-z0-9_]*$/;
|
|
22
|
-
const PHP_PREFIX_MAX_LENGTH = 50;
|
|
23
19
|
const WORKSPACE_TEMPLATE_ALIAS = "workspace";
|
|
24
|
-
const EPHEMERAL_NODE_MODULES_LINK_TYPE = process.platform === "win32" ? "junction" : "dir";
|
|
25
|
-
const LOCKFILES = {
|
|
26
|
-
bun: ["bun.lock", "bun.lockb"],
|
|
27
|
-
npm: ["package-lock.json"],
|
|
28
|
-
pnpm: ["pnpm-lock.yaml"],
|
|
29
|
-
yarn: ["yarn.lock"],
|
|
30
|
-
};
|
|
31
20
|
export const DATA_STORAGE_MODES = ["post-meta", "custom-table"];
|
|
32
21
|
export const PERSISTENCE_POLICIES = ["authenticated", "public"];
|
|
33
|
-
|
|
34
|
-
return BLOCK_SLUG_PATTERN.test(input) || "Use lowercase letters, numbers, and hyphens only";
|
|
35
|
-
}
|
|
36
|
-
function validateNamespace(input) {
|
|
37
|
-
return BLOCK_SLUG_PATTERN.test(toKebabCase(input))
|
|
38
|
-
? true
|
|
39
|
-
: "Use lowercase letters, numbers, and hyphens only";
|
|
40
|
-
}
|
|
41
|
-
function validateTextDomain(input) {
|
|
42
|
-
return BLOCK_SLUG_PATTERN.test(toKebabCase(input))
|
|
43
|
-
? true
|
|
44
|
-
: "Use lowercase letters, numbers, and hyphens only";
|
|
45
|
-
}
|
|
46
|
-
function validatePhpPrefix(input) {
|
|
47
|
-
const normalizedPrefix = toSnakeCase(input);
|
|
48
|
-
if (normalizedPrefix.length > PHP_PREFIX_MAX_LENGTH) {
|
|
49
|
-
return `Use ${PHP_PREFIX_MAX_LENGTH} characters or fewer to keep generated database identifiers within MySQL limits`;
|
|
50
|
-
}
|
|
51
|
-
return PHP_PREFIX_PATTERN.test(normalizedPrefix)
|
|
52
|
-
? true
|
|
53
|
-
: "Use letters, numbers, and underscores only, starting with a letter";
|
|
54
|
-
}
|
|
55
|
-
function assertValidIdentifier(label, value, validate) {
|
|
56
|
-
const result = validate(value);
|
|
57
|
-
if (result !== true) {
|
|
58
|
-
throw new Error(typeof result === "string" ? `${label}: ${result}` : `${label} is invalid`);
|
|
59
|
-
}
|
|
60
|
-
return value;
|
|
61
|
-
}
|
|
62
|
-
function normalizeBlockSlug(input) {
|
|
63
|
-
return toKebabCase(input);
|
|
64
|
-
}
|
|
65
|
-
function resolveValidatedBlockSlug(value) {
|
|
66
|
-
return assertValidIdentifier("Block slug", normalizeBlockSlug(value), validateBlockSlug);
|
|
67
|
-
}
|
|
68
|
-
function resolveValidatedNamespace(value) {
|
|
69
|
-
return assertValidIdentifier("Namespace", toKebabCase(value), validateNamespace);
|
|
70
|
-
}
|
|
71
|
-
function resolveValidatedTextDomain(value) {
|
|
72
|
-
return assertValidIdentifier("Text domain", toKebabCase(value), validateTextDomain);
|
|
73
|
-
}
|
|
74
|
-
function resolveValidatedPhpPrefix(value) {
|
|
75
|
-
return assertValidIdentifier("PHP prefix", toSnakeCase(value), validatePhpPrefix);
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Builds the generated WordPress wrapper CSS class for a scaffolded block.
|
|
79
|
-
*
|
|
80
|
-
* Returns `wp-block-{namespace}-{slug}` when a non-empty namespace is present,
|
|
81
|
-
* or `wp-block-{slug}` when the namespace is empty or undefined. When the
|
|
82
|
-
* normalized namespace equals the normalized slug, appends `-block` so the
|
|
83
|
-
* generated class avoids repeated namespace segments without colliding with the
|
|
84
|
-
* default core wrapper classes. Both inputs are normalized and validated with
|
|
85
|
-
* the same scaffold identifier rules used for block names.
|
|
86
|
-
*/
|
|
87
|
-
export function buildBlockCssClassName(namespace, slug) {
|
|
88
|
-
const normalizedSlug = resolveValidatedBlockSlug(slug);
|
|
89
|
-
const normalizedNamespace = typeof namespace === "string" && namespace.trim().length > 0
|
|
90
|
-
? resolveValidatedNamespace(namespace)
|
|
91
|
-
: "";
|
|
92
|
-
if (normalizedNamespace === normalizedSlug) {
|
|
93
|
-
return `wp-block-${normalizedSlug}-block`;
|
|
94
|
-
}
|
|
95
|
-
return normalizedNamespace.length > 0
|
|
96
|
-
? `wp-block-${normalizedNamespace}-${normalizedSlug}`
|
|
97
|
-
: `wp-block-${normalizedSlug}`;
|
|
98
|
-
}
|
|
99
|
-
function buildFrontendCssClassName(blockCssClassName) {
|
|
100
|
-
return `${blockCssClassName}-frontend`;
|
|
101
|
-
}
|
|
102
|
-
function resolveScaffoldIdentifiers({ namespace, phpPrefix, slug, textDomain, }) {
|
|
103
|
-
const normalizedSlug = resolveValidatedBlockSlug(slug);
|
|
104
|
-
return {
|
|
105
|
-
namespace: resolveValidatedNamespace(namespace),
|
|
106
|
-
phpPrefix: resolveValidatedPhpPrefix(phpPrefix ?? normalizedSlug),
|
|
107
|
-
slug: normalizedSlug,
|
|
108
|
-
textDomain: resolveValidatedTextDomain(textDomain ?? normalizedSlug),
|
|
109
|
-
};
|
|
110
|
-
}
|
|
22
|
+
export { buildBlockCssClassName } from "./scaffold-identifiers.js";
|
|
111
23
|
export function isDataStorageMode(value) {
|
|
112
24
|
return DATA_STORAGE_MODES.includes(value);
|
|
113
25
|
}
|
|
@@ -313,301 +225,6 @@ export function getTemplateVariables(templateId, answers) {
|
|
|
313
225
|
persistencePolicy,
|
|
314
226
|
};
|
|
315
227
|
}
|
|
316
|
-
async function ensureDirectory(targetDir, allowExisting = false) {
|
|
317
|
-
if (!fs.existsSync(targetDir)) {
|
|
318
|
-
await fsp.mkdir(targetDir, { recursive: true });
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
if (allowExisting) {
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
const entries = await fsp.readdir(targetDir);
|
|
325
|
-
if (entries.length > 0) {
|
|
326
|
-
throw new Error(`Target directory is not empty: ${targetDir}`);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
function buildReadme(templateId, variables, packageManager, { withMigrationUi = false, withTestPreset = false, withWpEnv = false, } = {}) {
|
|
330
|
-
const optionalOnboardingSteps = getOptionalOnboardingSteps(packageManager, templateId, {
|
|
331
|
-
compoundPersistenceEnabled: variables.compoundPersistenceEnabled === "true",
|
|
332
|
-
});
|
|
333
|
-
const sourceOfTruthNote = getTemplateSourceOfTruthNote(templateId, {
|
|
334
|
-
compoundPersistenceEnabled: variables.compoundPersistenceEnabled === "true",
|
|
335
|
-
});
|
|
336
|
-
const compoundPersistenceEnabled = variables.compoundPersistenceEnabled === "true";
|
|
337
|
-
const publicPersistencePolicyNote = variables.isPublicPersistencePolicy === "true"
|
|
338
|
-
? "Public persistence writes use signed short-lived tokens, per-request ids, and coarse rate limiting by default. Add application-specific abuse controls before using the same pattern for high-value metrics or experiments."
|
|
339
|
-
: null;
|
|
340
|
-
const compoundExtensionWorkflowSection = getCompoundExtensionWorkflowSection(packageManager, templateId);
|
|
341
|
-
const phpRestExtensionPointsSection = getPhpRestExtensionPointsSection(templateId, {
|
|
342
|
-
compoundPersistenceEnabled,
|
|
343
|
-
slug: variables.slug,
|
|
344
|
-
});
|
|
345
|
-
const developmentScript = getPrimaryDevelopmentScript(templateId);
|
|
346
|
-
const wpEnvSection = withWpEnv
|
|
347
|
-
? `## Local WordPress\n\n\`\`\`bash\n${formatRunScript(packageManager, "wp-env:start")}\n${formatRunScript(packageManager, "wp-env:stop")}\n${formatRunScript(packageManager, "wp-env:reset")}\n\`\`\``
|
|
348
|
-
: "";
|
|
349
|
-
const testPresetSection = withTestPreset
|
|
350
|
-
? `## Local Test Preset\n\n\`\`\`bash\n${formatRunScript(packageManager, "wp-env:start:test")}\n${formatRunScript(packageManager, "wp-env:wait:test")}\n${formatRunScript(packageManager, "test:e2e")}\n\`\`\`\n\nThe generated smoke test uses \`.wp-env.test.json\` and verifies that the scaffolded block registers in the WordPress editor.`
|
|
351
|
-
: "";
|
|
352
|
-
const migrationSection = withMigrationUi
|
|
353
|
-
? `## Migration UI\n\nThis scaffold already includes an initialized migration workspace at \`v1\`, generated deprecated/runtime artifacts, and an editor-embedded migration dashboard. Migration versions are schema lineage labels and are separate from your package or plugin release version. Use the existing CLI commands to snapshot, diff, scaffold, verify, and fuzz future schema changes.\n\n\`\`\`bash\n${formatRunScript(packageManager, "migration:doctor")}\n${formatRunScript(packageManager, "migration:verify")}\n${formatRunScript(packageManager, "migration:fuzz")}\n\`\`\`\n\nRun \`migration:init\` only when retrofitting migration support into an older project that was not scaffolded with \`--with-migration-ui\`.`
|
|
354
|
-
: "";
|
|
355
|
-
return `# ${variables.title}
|
|
356
|
-
|
|
357
|
-
${variables.description}
|
|
358
|
-
|
|
359
|
-
## Template
|
|
360
|
-
|
|
361
|
-
${templateId}
|
|
362
|
-
|
|
363
|
-
## Development
|
|
364
|
-
|
|
365
|
-
\`\`\`bash
|
|
366
|
-
${formatInstallCommand(packageManager)}
|
|
367
|
-
${formatRunScript(packageManager, developmentScript)}
|
|
368
|
-
\`\`\`
|
|
369
|
-
|
|
370
|
-
## Build
|
|
371
|
-
|
|
372
|
-
\`\`\`bash
|
|
373
|
-
${formatRunScript(packageManager, "build")}
|
|
374
|
-
\`\`\`
|
|
375
|
-
|
|
376
|
-
## Optional First Sync
|
|
377
|
-
|
|
378
|
-
\`\`\`bash
|
|
379
|
-
${optionalOnboardingSteps.join("\n")}
|
|
380
|
-
\`\`\`
|
|
381
|
-
|
|
382
|
-
${getOptionalOnboardingNote(packageManager, templateId, {
|
|
383
|
-
compoundPersistenceEnabled,
|
|
384
|
-
})}
|
|
385
|
-
|
|
386
|
-
${sourceOfTruthNote}${publicPersistencePolicyNote ? `\n\n${publicPersistencePolicyNote}` : ""}${migrationSection ? `\n\n${migrationSection}` : ""}${compoundExtensionWorkflowSection ? `\n\n${compoundExtensionWorkflowSection}` : ""}${wpEnvSection ? `\n\n${wpEnvSection}` : ""}${testPresetSection ? `\n\n${testPresetSection}` : ""}${phpRestExtensionPointsSection ? `\n\n${phpRestExtensionPointsSection}` : ""}
|
|
387
|
-
`;
|
|
388
|
-
}
|
|
389
|
-
function buildGitignore() {
|
|
390
|
-
return `# Dependencies
|
|
391
|
-
node_modules/
|
|
392
|
-
.yarn/
|
|
393
|
-
.pnp.*
|
|
394
|
-
|
|
395
|
-
# Build
|
|
396
|
-
build/
|
|
397
|
-
dist/
|
|
398
|
-
|
|
399
|
-
# Editor
|
|
400
|
-
.vscode/
|
|
401
|
-
.idea/
|
|
402
|
-
|
|
403
|
-
# OS
|
|
404
|
-
.DS_Store
|
|
405
|
-
Thumbs.db
|
|
406
|
-
|
|
407
|
-
# WordPress
|
|
408
|
-
*.log
|
|
409
|
-
.wp-env/
|
|
410
|
-
`;
|
|
411
|
-
}
|
|
412
|
-
function mergeTextLines(primaryContent, existingContent) {
|
|
413
|
-
const normalizedPrimary = primaryContent.replace(/\r\n/g, "\n").trimEnd();
|
|
414
|
-
const normalizedExisting = existingContent.replace(/\r\n/g, "\n").trimEnd();
|
|
415
|
-
const mergedLines = [];
|
|
416
|
-
const seen = new Set();
|
|
417
|
-
for (const line of [...normalizedPrimary.split("\n"), ...normalizedExisting.split("\n")]) {
|
|
418
|
-
if (line.length === 0 && mergedLines[mergedLines.length - 1] === "") {
|
|
419
|
-
continue;
|
|
420
|
-
}
|
|
421
|
-
if (line.length > 0 && seen.has(line)) {
|
|
422
|
-
continue;
|
|
423
|
-
}
|
|
424
|
-
if (line.length > 0) {
|
|
425
|
-
seen.add(line);
|
|
426
|
-
}
|
|
427
|
-
mergedLines.push(line);
|
|
428
|
-
}
|
|
429
|
-
return `${mergedLines.join("\n").replace(/\n{3,}/g, "\n\n")}\n`;
|
|
430
|
-
}
|
|
431
|
-
async function writeStarterManifestFiles(targetDir, templateId, variables) {
|
|
432
|
-
const manifests = getStarterManifestFiles(templateId, variables);
|
|
433
|
-
for (const { document, relativePath } of manifests) {
|
|
434
|
-
const destinationPath = path.join(targetDir, relativePath);
|
|
435
|
-
await fsp.mkdir(path.dirname(destinationPath), { recursive: true });
|
|
436
|
-
await fsp.writeFile(destinationPath, stringifyStarterManifest(document), "utf8");
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* Seed REST-derived persistence artifacts into a newly scaffolded built-in
|
|
441
|
-
* project before the first manual `sync-rest` run.
|
|
442
|
-
*
|
|
443
|
-
* @param targetDir Absolute scaffold target directory.
|
|
444
|
-
* @param templateId Built-in template id being scaffolded.
|
|
445
|
-
* @param variables Resolved scaffold template variables for the project.
|
|
446
|
-
* @returns A promise that resolves after any required persistence artifacts are generated.
|
|
447
|
-
*/
|
|
448
|
-
async function seedBuiltInPersistenceArtifacts(targetDir, templateId, variables) {
|
|
449
|
-
const needsPersistenceArtifacts = templateId === "persistence" ||
|
|
450
|
-
(templateId === "compound" && variables.compoundPersistenceEnabled === "true");
|
|
451
|
-
if (!needsPersistenceArtifacts) {
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
await withEphemeralScaffoldNodeModules(targetDir, async () => {
|
|
455
|
-
if (templateId === "persistence") {
|
|
456
|
-
await syncPersistenceRestArtifacts({
|
|
457
|
-
apiTypesFile: path.join("src", "api-types.ts"),
|
|
458
|
-
outputDir: "src",
|
|
459
|
-
projectDir: targetDir,
|
|
460
|
-
variables,
|
|
461
|
-
});
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
await syncPersistenceRestArtifacts({
|
|
465
|
-
apiTypesFile: path.join("src", "blocks", variables.slugKebabCase, "api-types.ts"),
|
|
466
|
-
outputDir: path.join("src", "blocks", variables.slugKebabCase),
|
|
467
|
-
projectDir: targetDir,
|
|
468
|
-
variables,
|
|
469
|
-
});
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Locate a node_modules directory containing `typia` relative to the project
|
|
474
|
-
* tools package root.
|
|
475
|
-
*
|
|
476
|
-
* Search order:
|
|
477
|
-
* 1. `PROJECT_TOOLS_PACKAGE_ROOT/node_modules`
|
|
478
|
-
* 2. The monorepo root resolved from `PROJECT_TOOLS_PACKAGE_ROOT`
|
|
479
|
-
* 3. The monorepo root `node_modules`
|
|
480
|
-
*
|
|
481
|
-
* @returns The first matching path, or `null` when no candidate contains `typia`.
|
|
482
|
-
*/
|
|
483
|
-
function resolveScaffoldGeneratorNodeModulesPath() {
|
|
484
|
-
const candidates = [
|
|
485
|
-
path.join(PROJECT_TOOLS_PACKAGE_ROOT, "node_modules"),
|
|
486
|
-
path.resolve(PROJECT_TOOLS_PACKAGE_ROOT, "..", ".."),
|
|
487
|
-
path.resolve(PROJECT_TOOLS_PACKAGE_ROOT, "..", "..", "node_modules"),
|
|
488
|
-
];
|
|
489
|
-
for (const candidate of candidates) {
|
|
490
|
-
if (fs.existsSync(path.join(candidate, "typia", "package.json"))) {
|
|
491
|
-
return candidate;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
return null;
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Temporarily symlink a scaffold generator node_modules directory into the
|
|
498
|
-
* target project while running an async callback.
|
|
499
|
-
*
|
|
500
|
-
* The helper resolves the source path via `resolveScaffoldGeneratorNodeModulesPath()`
|
|
501
|
-
* and uses `EPHEMERAL_NODE_MODULES_LINK_TYPE` for the symlink. The temporary
|
|
502
|
-
* link is removed in the `finally` block so cleanup still happens if the
|
|
503
|
-
* callback throws.
|
|
504
|
-
*
|
|
505
|
-
* @param targetDir Absolute scaffold target directory.
|
|
506
|
-
* @param callback Async work that requires a resolvable `node_modules`.
|
|
507
|
-
* @returns A promise that resolves after the callback and cleanup complete.
|
|
508
|
-
*/
|
|
509
|
-
async function withEphemeralScaffoldNodeModules(targetDir, callback) {
|
|
510
|
-
const targetNodeModulesPath = path.join(targetDir, "node_modules");
|
|
511
|
-
if (fs.existsSync(targetNodeModulesPath)) {
|
|
512
|
-
await callback();
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
const sourceNodeModulesPath = resolveScaffoldGeneratorNodeModulesPath();
|
|
516
|
-
if (!sourceNodeModulesPath) {
|
|
517
|
-
throw new Error("Unable to resolve a node_modules directory with typia for scaffold-time REST artifact generation.");
|
|
518
|
-
}
|
|
519
|
-
await fsp.symlink(sourceNodeModulesPath, targetNodeModulesPath, EPHEMERAL_NODE_MODULES_LINK_TYPE);
|
|
520
|
-
try {
|
|
521
|
-
await callback();
|
|
522
|
-
}
|
|
523
|
-
finally {
|
|
524
|
-
await fsp.rm(targetNodeModulesPath, { force: true, recursive: true });
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
async function normalizePackageManagerFiles(targetDir, packageManagerId) {
|
|
528
|
-
const yarnRcPath = path.join(targetDir, ".yarnrc.yml");
|
|
529
|
-
if (packageManagerId === "yarn") {
|
|
530
|
-
await fsp.writeFile(yarnRcPath, "nodeLinker: node-modules\n", "utf8");
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
if (fs.existsSync(yarnRcPath)) {
|
|
534
|
-
await fsp.rm(yarnRcPath, { force: true });
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
async function normalizePackageJson(targetDir, packageManagerId) {
|
|
538
|
-
const packageJsonPath = path.join(targetDir, "package.json");
|
|
539
|
-
if (!fs.existsSync(packageJsonPath)) {
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
const packageManager = getPackageManager(packageManagerId);
|
|
543
|
-
const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, "utf8"));
|
|
544
|
-
packageJson.packageManager = packageManager.packageManagerField;
|
|
545
|
-
if (packageJson.scripts) {
|
|
546
|
-
for (const [key, value] of Object.entries(packageJson.scripts)) {
|
|
547
|
-
if (typeof value === "string") {
|
|
548
|
-
packageJson.scripts[key] = transformPackageManagerText(value, packageManagerId);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
await fsp.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, "\t")}\n`, "utf8");
|
|
553
|
-
}
|
|
554
|
-
async function removeUnexpectedLockfiles(targetDir, packageManagerId) {
|
|
555
|
-
const keep = new Set(LOCKFILES[packageManagerId] ?? []);
|
|
556
|
-
const allLockfiles = Object.values(LOCKFILES).flat();
|
|
557
|
-
await Promise.all(allLockfiles.map(async (filename) => {
|
|
558
|
-
if (keep.has(filename)) {
|
|
559
|
-
return;
|
|
560
|
-
}
|
|
561
|
-
const filePath = path.join(targetDir, filename);
|
|
562
|
-
if (fs.existsSync(filePath)) {
|
|
563
|
-
await fsp.rm(filePath, { force: true });
|
|
564
|
-
}
|
|
565
|
-
}));
|
|
566
|
-
}
|
|
567
|
-
async function defaultInstallDependencies({ projectDir, packageManager, }) {
|
|
568
|
-
execSync(formatInstallCommand(packageManager), {
|
|
569
|
-
cwd: projectDir,
|
|
570
|
-
stdio: "inherit",
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
function isOfficialWorkspaceProject(projectDir) {
|
|
574
|
-
const packageJsonPath = path.join(projectDir, "package.json");
|
|
575
|
-
if (!fs.existsSync(packageJsonPath)) {
|
|
576
|
-
return false;
|
|
577
|
-
}
|
|
578
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
579
|
-
return (packageJson.wpTypia?.projectType === "workspace" &&
|
|
580
|
-
packageJson.wpTypia?.templatePackage === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE);
|
|
581
|
-
}
|
|
582
|
-
async function applyWorkspaceMigrationCapability(projectDir, packageManager) {
|
|
583
|
-
const packageJsonPath = path.join(projectDir, "package.json");
|
|
584
|
-
const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, "utf8"));
|
|
585
|
-
const wpTypiaPackageVersion = getPackageVersions().wpTypiaPackageVersion;
|
|
586
|
-
const canonicalCliSpecifier = wpTypiaPackageVersion === "^0.0.0"
|
|
587
|
-
? "wp-typia"
|
|
588
|
-
: `wp-typia@${wpTypiaPackageVersion.replace(/^[~^]/u, "")}`;
|
|
589
|
-
const migrationCli = (args) => formatPackageExecCommand(packageManager, canonicalCliSpecifier, `migrate ${args}`);
|
|
590
|
-
packageJson.scripts = {
|
|
591
|
-
...(packageJson.scripts ?? {}),
|
|
592
|
-
"migration:init": migrationCli("init --current-migration-version v1"),
|
|
593
|
-
"migration:snapshot": migrationCli("snapshot"),
|
|
594
|
-
"migration:diff": migrationCli("diff"),
|
|
595
|
-
"migration:scaffold": migrationCli("scaffold"),
|
|
596
|
-
"migration:doctor": migrationCli("doctor --all"),
|
|
597
|
-
"migration:fixtures": migrationCli("fixtures --all"),
|
|
598
|
-
"migration:verify": migrationCli("verify --all"),
|
|
599
|
-
"migration:fuzz": migrationCli("fuzz --all"),
|
|
600
|
-
};
|
|
601
|
-
await fsp.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, "\t")}\n`, "utf8");
|
|
602
|
-
writeMigrationConfig(projectDir, {
|
|
603
|
-
blocks: [],
|
|
604
|
-
currentMigrationVersion: "v1",
|
|
605
|
-
snapshotDir: "src/migrations/versions",
|
|
606
|
-
supportedMigrationVersions: ["v1"],
|
|
607
|
-
});
|
|
608
|
-
ensureMigrationDirectories(projectDir, []);
|
|
609
|
-
writeInitialMigrationScaffold(projectDir, "v1", []);
|
|
610
|
-
}
|
|
611
228
|
export async function scaffoldProject({ projectDir, templateId, answers, dataStorageMode, persistencePolicy, packageManager, externalLayerId, externalLayerSource, externalLayerSourceLabel, repositoryReference, cwd = process.cwd(), allowExistingDir = false, noInstall = false, installDependencies = undefined, variant, withMigrationUi = false, withTestPreset = false, withWpEnv = false, }) {
|
|
612
229
|
const resolvedTemplateId = normalizeTemplateSelection(templateId);
|
|
613
230
|
const resolvedPackageManager = getPackageManager(packageManager).id;
|
|
@@ -658,7 +275,7 @@ export async function scaffoldProject({ projectDir, templateId, answers, dataSto
|
|
|
658
275
|
throw new Error("`--with-migration-ui` is currently supported only for built-in templates and @wp-typia/create-workspace-template.");
|
|
659
276
|
}
|
|
660
277
|
try {
|
|
661
|
-
await
|
|
278
|
+
await ensureScaffoldDirectory(projectDir, allowExistingDir);
|
|
662
279
|
await copyInterpolatedDirectory(templateSource.templateDir, projectDir, variables);
|
|
663
280
|
}
|
|
664
281
|
finally {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { UnknownRecord } from './object-utils.js';
|
|
2
|
+
export type TemplateSourceFormat = 'wp-typia' | 'create-block-external' | 'create-block-subset';
|
|
3
|
+
/**
|
|
4
|
+
* Public template variables exposed to external template seeds before wp-typia
|
|
5
|
+
* normalizes them into a scaffold project.
|
|
6
|
+
*/
|
|
7
|
+
export interface TemplateVariableContext extends UnknownRecord {
|
|
8
|
+
/** Version string for `@wp-typia/api-client` used in generated dependencies. */
|
|
9
|
+
apiClientPackageVersion: string;
|
|
10
|
+
/** Version string for `@wp-typia/block-runtime` used in generated dependencies. */
|
|
11
|
+
blockRuntimePackageVersion: string;
|
|
12
|
+
/** Version string for `@wp-typia/block-types` used in generated dependencies. */
|
|
13
|
+
blockTypesPackageVersion: string;
|
|
14
|
+
/** PascalCase block type name derived from the scaffold slug. */
|
|
15
|
+
pascalCase: string;
|
|
16
|
+
/** Snake_case PHP symbol prefix used for generated functions, constants, and keys. */
|
|
17
|
+
phpPrefix: string;
|
|
18
|
+
/** Human-readable block title. */
|
|
19
|
+
title: string;
|
|
20
|
+
/** Human-readable project or block description. */
|
|
21
|
+
description: string;
|
|
22
|
+
/** Keyword string derived from the slug for generated block metadata. */
|
|
23
|
+
keyword: string;
|
|
24
|
+
/** Block namespace used in generated block names such as `namespace/slug`. */
|
|
25
|
+
namespace: string;
|
|
26
|
+
/** Kebab-case scaffold slug used for package names, paths, and block slugs. */
|
|
27
|
+
slug: string;
|
|
28
|
+
/** Kebab-case text domain used for generated i18n strings and plugin headers. */
|
|
29
|
+
textDomain: string;
|
|
30
|
+
}
|
|
31
|
+
export interface ResolvedTemplateSource {
|
|
32
|
+
id: string;
|
|
33
|
+
defaultCategory: string;
|
|
34
|
+
description: string;
|
|
35
|
+
features: string[];
|
|
36
|
+
format: TemplateSourceFormat;
|
|
37
|
+
isOfficialWorkspaceTemplate?: boolean;
|
|
38
|
+
templateDir: string;
|
|
39
|
+
cleanup?: () => Promise<void>;
|
|
40
|
+
selectedVariant?: string | null;
|
|
41
|
+
warnings?: string[];
|
|
42
|
+
}
|
|
43
|
+
export interface GitHubTemplateLocator {
|
|
44
|
+
owner: string;
|
|
45
|
+
repo: string;
|
|
46
|
+
ref: string | null;
|
|
47
|
+
sourcePath: string;
|
|
48
|
+
}
|
|
49
|
+
export interface NpmTemplateLocator {
|
|
50
|
+
fetchSpec: string;
|
|
51
|
+
name: string;
|
|
52
|
+
raw: string;
|
|
53
|
+
rawSpec: string;
|
|
54
|
+
type: string;
|
|
55
|
+
}
|
|
56
|
+
export interface ExternalTemplateConfig<TView extends UnknownRecord = TemplateVariableContext> {
|
|
57
|
+
assetsPath?: string;
|
|
58
|
+
blockTemplatesPath?: string;
|
|
59
|
+
defaultValues?: Partial<TView>;
|
|
60
|
+
folderName?: string;
|
|
61
|
+
transformer?: (view: TView) => UnknownRecord | Promise<UnknownRecord>;
|
|
62
|
+
variants?: Record<string, Partial<TView>>;
|
|
63
|
+
}
|
|
64
|
+
export interface SeedSource {
|
|
65
|
+
assetsDir?: string;
|
|
66
|
+
blockDir: string;
|
|
67
|
+
cleanup?: () => Promise<void>;
|
|
68
|
+
rootDir: string;
|
|
69
|
+
selectedVariant?: string | null;
|
|
70
|
+
warnings?: string[];
|
|
71
|
+
}
|
|
72
|
+
export type RemoteTemplateLocator = {
|
|
73
|
+
kind: 'github';
|
|
74
|
+
locator: GitHubTemplateLocator;
|
|
75
|
+
} | {
|
|
76
|
+
kind: 'npm';
|
|
77
|
+
locator: NpmTemplateLocator;
|
|
78
|
+
} | {
|
|
79
|
+
kind: 'path';
|
|
80
|
+
templatePath: string;
|
|
81
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SeedSource, TemplateVariableContext } from './template-source-contracts.js';
|
|
2
|
+
/**
|
|
3
|
+
* Candidate filenames for official external template config entrypoints.
|
|
4
|
+
*/
|
|
5
|
+
export declare const EXTERNAL_TEMPLATE_ENTRY_CANDIDATES: readonly ["index.js", "index.cjs", "index.mjs"];
|
|
6
|
+
/**
|
|
7
|
+
* Search a source directory for the first supported external template entry.
|
|
8
|
+
*
|
|
9
|
+
* @param sourceDir Directory that may contain an external template config entry.
|
|
10
|
+
* @returns The first matching entry path, or null when no supported entry exists.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getExternalTemplateEntry(sourceDir: string): string | null;
|
|
13
|
+
/**
|
|
14
|
+
* Load an official external create-block template config and render its seed.
|
|
15
|
+
*
|
|
16
|
+
* @param sourceDir Source directory that contains the external template config.
|
|
17
|
+
* @param context Template render context used for the selected variant.
|
|
18
|
+
* @param requestedVariant Optional explicit variant override.
|
|
19
|
+
* @returns A rendered temporary seed directory with optional assets and cleanup.
|
|
20
|
+
*/
|
|
21
|
+
export declare function renderCreateBlockExternalTemplate(sourceDir: string, context: TemplateVariableContext, requestedVariant?: string): Promise<SeedSource>;
|