@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
|
@@ -3,18 +3,19 @@ import { promises as fsp } from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { execSync } from "node:child_process";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { applyGeneratedProjectDxPackageJson, applyLocalDevPresetFiles,
|
|
6
|
+
import { applyGeneratedProjectDxPackageJson, applyLocalDevPresetFiles, } from "./local-dev-presets.js";
|
|
7
7
|
import { applyMigrationUiCapability } from "./migration-ui-capability.js";
|
|
8
8
|
import { getPackageVersions } from "./package-versions.js";
|
|
9
9
|
import { ensureMigrationDirectories, writeInitialMigrationScaffold, writeMigrationConfig, } from "./migration-project.js";
|
|
10
10
|
import { syncPersistenceRestArtifacts, } from "./persistence-rest-artifacts.js";
|
|
11
|
-
import {
|
|
11
|
+
import { buildGitignore, buildReadme, mergeTextLines, } from "./scaffold-documents.js";
|
|
12
12
|
import { getStarterManifestFiles, stringifyStarterManifest, } from "./starter-manifests.js";
|
|
13
13
|
import { stringifyBuiltInBlockJsonDocument, } from "./built-in-block-artifacts.js";
|
|
14
14
|
import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, } from "./template-registry.js";
|
|
15
15
|
import { copyInterpolatedDirectory } from "./template-render.js";
|
|
16
|
-
import { formatInstallCommand, formatPackageExecCommand,
|
|
16
|
+
import { formatInstallCommand, formatPackageExecCommand, getPackageManager, transformPackageManagerText, } from "./package-managers.js";
|
|
17
17
|
import { replaceRepositoryReferencePlaceholders, resolveScaffoldRepositoryReference, } from "./scaffold-repository-reference.js";
|
|
18
|
+
export { buildGitignore, buildReadme, mergeTextLines, } from "./scaffold-documents.js";
|
|
18
19
|
const EPHEMERAL_NODE_MODULES_LINK_TYPE = process.platform === "win32" ? "junction" : "dir";
|
|
19
20
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
20
21
|
const LOCKFILES = {
|
|
@@ -36,108 +37,6 @@ export async function ensureDirectory(targetDir, allowExisting = false) {
|
|
|
36
37
|
throw new Error(`Target directory is not empty: ${targetDir}`);
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
|
-
export function buildReadme(templateId, variables, packageManager, { withMigrationUi = false, withTestPreset = false, withWpEnv = false, } = {}) {
|
|
40
|
-
const optionalOnboardingSteps = getOptionalOnboardingSteps(packageManager, templateId, {
|
|
41
|
-
compoundPersistenceEnabled: variables.compoundPersistenceEnabled === "true",
|
|
42
|
-
});
|
|
43
|
-
const sourceOfTruthNote = getTemplateSourceOfTruthNote(templateId, {
|
|
44
|
-
compoundPersistenceEnabled: variables.compoundPersistenceEnabled === "true",
|
|
45
|
-
});
|
|
46
|
-
const compoundPersistenceEnabled = variables.compoundPersistenceEnabled === "true";
|
|
47
|
-
const publicPersistencePolicyNote = variables.isPublicPersistencePolicy === "true"
|
|
48
|
-
? "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."
|
|
49
|
-
: null;
|
|
50
|
-
const compoundExtensionWorkflowSection = getCompoundExtensionWorkflowSection(packageManager, templateId);
|
|
51
|
-
const phpRestExtensionPointsSection = getPhpRestExtensionPointsSection(templateId, {
|
|
52
|
-
compoundPersistenceEnabled,
|
|
53
|
-
slug: variables.slug,
|
|
54
|
-
});
|
|
55
|
-
const developmentScript = getPrimaryDevelopmentScript(templateId);
|
|
56
|
-
const wpEnvSection = withWpEnv
|
|
57
|
-
? `## Local WordPress\n\n\`\`\`bash\n${formatRunScript(packageManager, "wp-env:start")}\n${formatRunScript(packageManager, "wp-env:stop")}\n${formatRunScript(packageManager, "wp-env:reset")}\n\`\`\``
|
|
58
|
-
: "";
|
|
59
|
-
const testPresetSection = withTestPreset
|
|
60
|
-
? `## 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.`
|
|
61
|
-
: "";
|
|
62
|
-
const migrationSection = withMigrationUi
|
|
63
|
-
? `## 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\`.`
|
|
64
|
-
: "";
|
|
65
|
-
return `# ${variables.title}
|
|
66
|
-
|
|
67
|
-
${variables.description}
|
|
68
|
-
|
|
69
|
-
## Template
|
|
70
|
-
|
|
71
|
-
${templateId}
|
|
72
|
-
|
|
73
|
-
## Development
|
|
74
|
-
|
|
75
|
-
\`\`\`bash
|
|
76
|
-
${formatInstallCommand(packageManager)}
|
|
77
|
-
${formatRunScript(packageManager, developmentScript)}
|
|
78
|
-
\`\`\`
|
|
79
|
-
|
|
80
|
-
## Build
|
|
81
|
-
|
|
82
|
-
\`\`\`bash
|
|
83
|
-
${formatRunScript(packageManager, "build")}
|
|
84
|
-
\`\`\`
|
|
85
|
-
|
|
86
|
-
## Optional First Sync
|
|
87
|
-
|
|
88
|
-
\`\`\`bash
|
|
89
|
-
${optionalOnboardingSteps.join("\n")}
|
|
90
|
-
\`\`\`
|
|
91
|
-
|
|
92
|
-
${getOptionalOnboardingNote(packageManager, templateId, {
|
|
93
|
-
compoundPersistenceEnabled,
|
|
94
|
-
})}
|
|
95
|
-
|
|
96
|
-
${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}` : ""}
|
|
97
|
-
`;
|
|
98
|
-
}
|
|
99
|
-
export function buildGitignore() {
|
|
100
|
-
return `# Dependencies
|
|
101
|
-
node_modules/
|
|
102
|
-
.yarn/
|
|
103
|
-
.pnp.*
|
|
104
|
-
|
|
105
|
-
# Build
|
|
106
|
-
build/
|
|
107
|
-
dist/
|
|
108
|
-
|
|
109
|
-
# Editor
|
|
110
|
-
.vscode/
|
|
111
|
-
.idea/
|
|
112
|
-
|
|
113
|
-
# OS
|
|
114
|
-
.DS_Store
|
|
115
|
-
Thumbs.db
|
|
116
|
-
|
|
117
|
-
# WordPress
|
|
118
|
-
*.log
|
|
119
|
-
.wp-env/
|
|
120
|
-
`;
|
|
121
|
-
}
|
|
122
|
-
export function mergeTextLines(primaryContent, existingContent) {
|
|
123
|
-
const normalizedPrimary = primaryContent.replace(/\r\n/g, "\n").trimEnd();
|
|
124
|
-
const normalizedExisting = existingContent.replace(/\r\n/g, "\n").trimEnd();
|
|
125
|
-
const mergedLines = [];
|
|
126
|
-
const seen = new Set();
|
|
127
|
-
for (const line of [...normalizedPrimary.split("\n"), ...normalizedExisting.split("\n")]) {
|
|
128
|
-
if (line.length === 0 && mergedLines[mergedLines.length - 1] === "") {
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
if (line.length > 0 && seen.has(line)) {
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
if (line.length > 0) {
|
|
135
|
-
seen.add(line);
|
|
136
|
-
}
|
|
137
|
-
mergedLines.push(line);
|
|
138
|
-
}
|
|
139
|
-
return `${mergedLines.join("\n").replace(/\n{3,}/g, "\n\n")}\n`;
|
|
140
|
-
}
|
|
141
40
|
export async function writeStarterManifestFiles(targetDir, templateId, variables, artifacts) {
|
|
142
41
|
const manifests = artifacts
|
|
143
42
|
? artifacts.map((artifact) => ({
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { PackageManagerId } from './package-managers.js';
|
|
2
|
+
import type { ScaffoldTemplateVariables } from './scaffold.js';
|
|
3
|
+
/**
|
|
4
|
+
* Builds the generated README markdown for one scaffolded project.
|
|
5
|
+
*
|
|
6
|
+
* @param templateId Scaffold template family or template identifier.
|
|
7
|
+
* @param variables Interpolated scaffold variables used in generated content.
|
|
8
|
+
* @param packageManager Package manager used to format install and script commands.
|
|
9
|
+
* @param options Optional README sections enabled by scaffold presets.
|
|
10
|
+
* @returns Markdown README content for the generated project root.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildReadme(templateId: string, variables: ScaffoldTemplateVariables, packageManager: PackageManagerId, { withMigrationUi, withTestPreset, withWpEnv, }?: {
|
|
13
|
+
withMigrationUi?: boolean;
|
|
14
|
+
withTestPreset?: boolean;
|
|
15
|
+
withWpEnv?: boolean;
|
|
16
|
+
}): string;
|
|
17
|
+
/**
|
|
18
|
+
* Build the default `.gitignore` contents for a scaffolded project.
|
|
19
|
+
*
|
|
20
|
+
* @returns A newline-terminated `.gitignore` string covering dependency, build, editor, OS, and WordPress artifacts.
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildGitignore(): string;
|
|
23
|
+
/**
|
|
24
|
+
* Merge generated and existing text files while keeping line order stable.
|
|
25
|
+
*
|
|
26
|
+
* Existing unique lines are appended after the primary content, duplicate
|
|
27
|
+
* non-empty lines are removed, and runs of more than two blank lines collapse
|
|
28
|
+
* to a single empty separator.
|
|
29
|
+
*
|
|
30
|
+
* @param primaryContent Newly generated text that should appear first.
|
|
31
|
+
* @param existingContent Existing file contents preserved after unique generated lines.
|
|
32
|
+
* @returns The merged text block with a trailing newline.
|
|
33
|
+
*/
|
|
34
|
+
export declare function mergeTextLines(primaryContent: string, existingContent: string): string;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { getPrimaryDevelopmentScript } from './local-dev-presets.js';
|
|
2
|
+
import { getCompoundExtensionWorkflowSection, getInitialCommitCommands, getInitialCommitNote, getOptionalOnboardingNote, getOptionalOnboardingSteps, getQuickStartWorkflowNote, getPhpRestExtensionPointsSection, getTemplateSourceOfTruthNote, } from './scaffold-onboarding.js';
|
|
3
|
+
import { formatInstallCommand, formatRunScript, } from './package-managers.js';
|
|
4
|
+
/**
|
|
5
|
+
* Builds the generated README markdown for one scaffolded project.
|
|
6
|
+
*
|
|
7
|
+
* @param templateId Scaffold template family or template identifier.
|
|
8
|
+
* @param variables Interpolated scaffold variables used in generated content.
|
|
9
|
+
* @param packageManager Package manager used to format install and script commands.
|
|
10
|
+
* @param options Optional README sections enabled by scaffold presets.
|
|
11
|
+
* @returns Markdown README content for the generated project root.
|
|
12
|
+
*/
|
|
13
|
+
export function buildReadme(templateId, variables, packageManager, { withMigrationUi = false, withTestPreset = false, withWpEnv = false, } = {}) {
|
|
14
|
+
const optionalOnboardingSteps = getOptionalOnboardingSteps(packageManager, templateId, {
|
|
15
|
+
compoundPersistenceEnabled: variables.compoundPersistenceEnabled === 'true',
|
|
16
|
+
});
|
|
17
|
+
const initialCommitCommands = getInitialCommitCommands();
|
|
18
|
+
const sourceOfTruthNote = getTemplateSourceOfTruthNote(templateId, {
|
|
19
|
+
compoundPersistenceEnabled: variables.compoundPersistenceEnabled === 'true',
|
|
20
|
+
});
|
|
21
|
+
const compoundPersistenceEnabled = variables.compoundPersistenceEnabled === 'true';
|
|
22
|
+
const publicPersistencePolicyNote = variables.isPublicPersistencePolicy === 'true'
|
|
23
|
+
? '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.'
|
|
24
|
+
: null;
|
|
25
|
+
const compoundExtensionWorkflowSection = getCompoundExtensionWorkflowSection(packageManager, templateId);
|
|
26
|
+
const phpRestExtensionPointsSection = getPhpRestExtensionPointsSection(templateId, {
|
|
27
|
+
compoundPersistenceEnabled,
|
|
28
|
+
slug: variables.slug,
|
|
29
|
+
});
|
|
30
|
+
const developmentScript = getPrimaryDevelopmentScript(templateId);
|
|
31
|
+
const wpEnvSection = withWpEnv
|
|
32
|
+
? `## Local WordPress\n\n\`\`\`bash\n${formatRunScript(packageManager, 'wp-env:start')}\n${formatRunScript(packageManager, 'wp-env:stop')}\n${formatRunScript(packageManager, 'wp-env:reset')}\n\`\`\``
|
|
33
|
+
: '';
|
|
34
|
+
const testPresetSection = withTestPreset
|
|
35
|
+
? `## 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.`
|
|
36
|
+
: '';
|
|
37
|
+
const migrationSection = withMigrationUi
|
|
38
|
+
? `## 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\`.`
|
|
39
|
+
: '';
|
|
40
|
+
return `# ${variables.title}
|
|
41
|
+
|
|
42
|
+
${variables.description}
|
|
43
|
+
|
|
44
|
+
## Template
|
|
45
|
+
|
|
46
|
+
${templateId}
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
\`\`\`bash
|
|
51
|
+
${formatInstallCommand(packageManager)}
|
|
52
|
+
${formatRunScript(packageManager, developmentScript)}
|
|
53
|
+
\`\`\`
|
|
54
|
+
|
|
55
|
+
${getQuickStartWorkflowNote(packageManager, templateId, {
|
|
56
|
+
compoundPersistenceEnabled,
|
|
57
|
+
})}
|
|
58
|
+
|
|
59
|
+
## Build and Verify
|
|
60
|
+
|
|
61
|
+
\`\`\`bash
|
|
62
|
+
${formatRunScript(packageManager, 'build')}
|
|
63
|
+
${formatRunScript(packageManager, 'typecheck')}
|
|
64
|
+
\`\`\`
|
|
65
|
+
|
|
66
|
+
## Advanced Sync
|
|
67
|
+
|
|
68
|
+
\`\`\`bash
|
|
69
|
+
${optionalOnboardingSteps.join('\n')}
|
|
70
|
+
\`\`\`
|
|
71
|
+
|
|
72
|
+
${getOptionalOnboardingNote(packageManager, templateId, {
|
|
73
|
+
compoundPersistenceEnabled,
|
|
74
|
+
})}
|
|
75
|
+
|
|
76
|
+
## Before First Commit
|
|
77
|
+
|
|
78
|
+
\`\`\`bash
|
|
79
|
+
${initialCommitCommands.join('\n')}
|
|
80
|
+
\`\`\`
|
|
81
|
+
|
|
82
|
+
${getInitialCommitNote()}
|
|
83
|
+
|
|
84
|
+
${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}` : ''}
|
|
85
|
+
`;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Build the default `.gitignore` contents for a scaffolded project.
|
|
89
|
+
*
|
|
90
|
+
* @returns A newline-terminated `.gitignore` string covering dependency, build, editor, OS, and WordPress artifacts.
|
|
91
|
+
*/
|
|
92
|
+
export function buildGitignore() {
|
|
93
|
+
return `# Dependencies
|
|
94
|
+
node_modules/
|
|
95
|
+
.yarn/
|
|
96
|
+
.pnp.*
|
|
97
|
+
|
|
98
|
+
# Build
|
|
99
|
+
build/
|
|
100
|
+
dist/
|
|
101
|
+
|
|
102
|
+
# Editor
|
|
103
|
+
.vscode/
|
|
104
|
+
.idea/
|
|
105
|
+
|
|
106
|
+
# OS
|
|
107
|
+
.DS_Store
|
|
108
|
+
Thumbs.db
|
|
109
|
+
|
|
110
|
+
# WordPress
|
|
111
|
+
*.log
|
|
112
|
+
.wp-env/
|
|
113
|
+
`;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Merge generated and existing text files while keeping line order stable.
|
|
117
|
+
*
|
|
118
|
+
* Existing unique lines are appended after the primary content, duplicate
|
|
119
|
+
* non-empty lines are removed, and runs of more than two blank lines collapse
|
|
120
|
+
* to a single empty separator.
|
|
121
|
+
*
|
|
122
|
+
* @param primaryContent Newly generated text that should appear first.
|
|
123
|
+
* @param existingContent Existing file contents preserved after unique generated lines.
|
|
124
|
+
* @returns The merged text block with a trailing newline.
|
|
125
|
+
*/
|
|
126
|
+
export function mergeTextLines(primaryContent, existingContent) {
|
|
127
|
+
const normalizedPrimary = primaryContent.replace(/\r\n/g, '\n').trimEnd();
|
|
128
|
+
const normalizedExisting = existingContent.replace(/\r\n/g, '\n').trimEnd();
|
|
129
|
+
const mergedLines = [];
|
|
130
|
+
const seen = new Set();
|
|
131
|
+
for (const line of [...normalizedPrimary.split('\n'), ...normalizedExisting.split('\n')]) {
|
|
132
|
+
if (line.length === 0 && mergedLines[mergedLines.length - 1] === '') {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (line.length > 0 && seen.has(line)) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (line.length > 0) {
|
|
139
|
+
seen.add(line);
|
|
140
|
+
}
|
|
141
|
+
mergedLines.push(line);
|
|
142
|
+
}
|
|
143
|
+
return `${mergedLines.join('\n').replace(/\n{3,}/g, '\n\n')}\n`;
|
|
144
|
+
}
|
|
@@ -14,10 +14,22 @@ export declare function getOptionalSyncScriptNames(templateId: string, options?:
|
|
|
14
14
|
* Formats optional onboarding sync commands for the selected package manager.
|
|
15
15
|
*/
|
|
16
16
|
export declare function getOptionalOnboardingSteps(packageManager: PackageManagerId, templateId: string, options?: SyncOnboardingOptions): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Returns the quick-start note explaining the scaffold's primary local loop.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getQuickStartWorkflowNote(packageManager: PackageManagerId, templateId?: string, options?: SyncOnboardingOptions): string;
|
|
17
21
|
/**
|
|
18
22
|
* Returns the onboarding note explaining when manual sync is optional.
|
|
19
23
|
*/
|
|
20
24
|
export declare function getOptionalOnboardingNote(packageManager: PackageManagerId, templateId?: string, options?: SyncOnboardingOptions): string;
|
|
25
|
+
/**
|
|
26
|
+
* Returns the recommended version-control commands for a fresh scaffold.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getInitialCommitCommands(): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Returns the version-control note shown after the initial scaffold.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getInitialCommitNote(): string;
|
|
21
33
|
/**
|
|
22
34
|
* Returns source-of-truth guidance for generated artifacts by template mode.
|
|
23
35
|
*/
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { formatRunScript } from "./package-managers.js";
|
|
2
2
|
import { getPrimaryDevelopmentScript } from "./local-dev-presets.js";
|
|
3
3
|
import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, isBuiltInTemplateId, } from "./template-registry.js";
|
|
4
|
+
const INITIAL_COMMIT_COMMANDS = [
|
|
5
|
+
"git init",
|
|
6
|
+
"git add .",
|
|
7
|
+
'git commit -m "Initial scaffold"',
|
|
8
|
+
];
|
|
4
9
|
function templateHasPersistenceSync(templateId, { compoundPersistenceEnabled = false } = {}) {
|
|
5
10
|
return templateId === "persistence" || (templateId === "compound" && compoundPersistenceEnabled);
|
|
6
11
|
}
|
|
@@ -28,6 +33,24 @@ export function getOptionalSyncScriptNames(templateId, options = {}) {
|
|
|
28
33
|
export function getOptionalOnboardingSteps(packageManager, templateId, options = {}) {
|
|
29
34
|
return getOptionalSyncScriptNames(templateId, options).map((scriptName) => formatRunScript(packageManager, scriptName));
|
|
30
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Returns the quick-start note explaining the scaffold's primary local loop.
|
|
38
|
+
*/
|
|
39
|
+
export function getQuickStartWorkflowNote(packageManager, templateId = "basic", options = {}) {
|
|
40
|
+
const developmentScript = getPrimaryDevelopmentScript(templateId);
|
|
41
|
+
const devCommand = formatRunScript(packageManager, developmentScript);
|
|
42
|
+
const startCommand = formatRunScript(packageManager, "start");
|
|
43
|
+
if (developmentScript === "start") {
|
|
44
|
+
return `${startCommand} is the primary local entry point for this template. If the template also exposes a dedicated watch mode or alternate editor workflow, follow that template-specific documentation alongside the generated project scripts.`;
|
|
45
|
+
}
|
|
46
|
+
if (developmentScript !== "dev") {
|
|
47
|
+
return `${devCommand} is the primary local entry point for this template. Use ${startCommand} when you want the scaffold's one-shot startup flow instead of the watch-oriented workflow.`;
|
|
48
|
+
}
|
|
49
|
+
if (templateHasPersistenceSync(templateId, options)) {
|
|
50
|
+
return `${devCommand} keeps the editor, type-derived artifacts, and REST-derived artifacts moving together during local development. Use ${startCommand} when you want a one-shot sync plus editor startup without the long-running watch loop.`;
|
|
51
|
+
}
|
|
52
|
+
return `${devCommand} keeps the editor and type-derived artifacts moving together during local development. Use ${startCommand} when you want a one-shot sync plus editor startup without the long-running watch loop.`;
|
|
53
|
+
}
|
|
31
54
|
/**
|
|
32
55
|
* Returns the onboarding note explaining when manual sync is optional.
|
|
33
56
|
*/
|
|
@@ -46,18 +69,32 @@ export function getOptionalOnboardingNote(packageManager, templateId = "basic",
|
|
|
46
69
|
const advancedPersistenceNote = templateHasPersistenceSync(templateId, options)
|
|
47
70
|
? ` ${syncRestCommand} remains available for advanced REST-only refreshes, but it now fails fast when type-derived artifacts are stale; run \`${syncCommand}\` or \`${syncTypesCommand}\` first.`
|
|
48
71
|
: "";
|
|
72
|
+
const isCustomTemplate = !isBuiltInTemplateId(templateId) &&
|
|
73
|
+
templateId !== OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE;
|
|
49
74
|
const fallbackCustomTemplateNote = !hasUnifiedSync &&
|
|
50
75
|
syncSteps.length > 0 &&
|
|
51
|
-
|
|
52
|
-
templateId !== OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE
|
|
76
|
+
isCustomTemplate
|
|
53
77
|
? `Run ${syncSteps.join(" then ")} manually before build, typecheck, or commit. ${syncCheckCommand} verifies the current type-derived artifacts without rewriting them.${optionalSyncScripts.includes("sync-rest") ? ` ${syncRestCommand} remains available for REST-only refreshes after ${syncTypesCommand}.` : ""}`
|
|
54
78
|
: null;
|
|
55
79
|
if (fallbackCustomTemplateNote) {
|
|
56
80
|
return fallbackCustomTemplateNote;
|
|
57
81
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
82
|
+
if (isCustomTemplate && syncSteps.length === 0) {
|
|
83
|
+
return "No optional sync command was detected for this custom template. Follow the template's own artifact-refresh guidance before build, typecheck, or your first commit.";
|
|
84
|
+
}
|
|
85
|
+
return `You usually do not need to run ${syncCommand} during a normal ${formatRunScript(packageManager, developmentScript)} session. Run ${syncCommand} manually when you want a reviewable artifact refresh before ${formatRunScript(packageManager, "build")}, ${typecheckCommand}, or your first commit. ${syncTypesCommand} stays warn-only by default; use \`${failOnLossySyncCommand}\` to fail only on lossy WordPress projections, or \`${strictSyncCommand}\` for the stricter CI-oriented report.${advancedPersistenceNote} They do not create migration history. If this directory is new, create your first Git commit after that refresh.`;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Returns the recommended version-control commands for a fresh scaffold.
|
|
89
|
+
*/
|
|
90
|
+
export function getInitialCommitCommands() {
|
|
91
|
+
return [...INITIAL_COMMIT_COMMANDS];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Returns the version-control note shown after the initial scaffold.
|
|
95
|
+
*/
|
|
96
|
+
export function getInitialCommitNote() {
|
|
97
|
+
return "Skip `git init` if this directory already lives inside an existing repository. If you want generated artifacts refreshed before the first checkpoint, run your manual sync step first and then create the commit.";
|
|
61
98
|
}
|
|
62
99
|
/**
|
|
63
100
|
* Returns source-of-truth guidance for generated artifacts by template mode.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ScaffoldAnswers, ScaffoldTemplateVariables } from './scaffold.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build the normalized template variables used by scaffold rendering.
|
|
4
|
+
*
|
|
5
|
+
* @param templateId Selected scaffold template identifier.
|
|
6
|
+
* @param answers Normalized scaffold answers collected from defaults, flags, and prompts.
|
|
7
|
+
* @returns Template variables ready for file interpolation and generated artifacts.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getTemplateVariables(templateId: string, answers: ScaffoldAnswers): ScaffoldTemplateVariables;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { buildTemplateVariablesFromBlockSpec, createBuiltInBlockSpec, } from './block-generator-service.js';
|
|
2
|
+
import { getPackageVersions } from './package-versions.js';
|
|
3
|
+
import { buildBlockCssClassName, buildFrontendCssClassName, resolveScaffoldIdentifiers, } from './scaffold-identifiers.js';
|
|
4
|
+
import { BUILTIN_BLOCK_METADATA_VERSION, COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS, getBuiltInTemplateMetadataDefaults, } from './template-defaults.js';
|
|
5
|
+
import { getTemplateById, isBuiltInTemplateId, } from './template-registry.js';
|
|
6
|
+
import { toPascalCase, toSnakeCase, } from './string-case.js';
|
|
7
|
+
/**
|
|
8
|
+
* Build the normalized template variables used by scaffold rendering.
|
|
9
|
+
*
|
|
10
|
+
* @param templateId Selected scaffold template identifier.
|
|
11
|
+
* @param answers Normalized scaffold answers collected from defaults, flags, and prompts.
|
|
12
|
+
* @returns Template variables ready for file interpolation and generated artifacts.
|
|
13
|
+
*/
|
|
14
|
+
export function getTemplateVariables(templateId, answers) {
|
|
15
|
+
if (isBuiltInTemplateId(templateId)) {
|
|
16
|
+
return buildTemplateVariablesFromBlockSpec(createBuiltInBlockSpec({
|
|
17
|
+
answers,
|
|
18
|
+
dataStorageMode: answers.dataStorageMode,
|
|
19
|
+
persistencePolicy: answers.persistencePolicy,
|
|
20
|
+
templateId,
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
const { apiClientPackageVersion, blockRuntimePackageVersion, blockTypesPackageVersion, projectToolsPackageVersion, restPackageVersion, } = getPackageVersions();
|
|
24
|
+
const template = isBuiltInTemplateId(templateId) ? getTemplateById(templateId) : null;
|
|
25
|
+
const metadataDefaults = isBuiltInTemplateId(templateId)
|
|
26
|
+
? getBuiltInTemplateMetadataDefaults(templateId)
|
|
27
|
+
: null;
|
|
28
|
+
const identifiers = resolveScaffoldIdentifiers({
|
|
29
|
+
namespace: answers.namespace,
|
|
30
|
+
phpPrefix: answers.phpPrefix,
|
|
31
|
+
slug: answers.slug,
|
|
32
|
+
textDomain: answers.textDomain,
|
|
33
|
+
});
|
|
34
|
+
const slug = identifiers.slug;
|
|
35
|
+
const slugSnakeCase = toSnakeCase(slug);
|
|
36
|
+
const pascalCase = toPascalCase(slug);
|
|
37
|
+
const title = answers.title.trim();
|
|
38
|
+
const namespace = identifiers.namespace;
|
|
39
|
+
const description = answers.description.trim();
|
|
40
|
+
const textDomain = identifiers.textDomain;
|
|
41
|
+
const phpPrefix = identifiers.phpPrefix;
|
|
42
|
+
const phpPrefixUpper = phpPrefix.toUpperCase();
|
|
43
|
+
const compoundChildTitle = `${title} Item`;
|
|
44
|
+
const cssClassName = buildBlockCssClassName(namespace, slug);
|
|
45
|
+
const compoundChildCssClassName = buildBlockCssClassName(namespace, `${slug}-item`);
|
|
46
|
+
const compoundPersistenceEnabled = templateId === 'persistence'
|
|
47
|
+
? true
|
|
48
|
+
: templateId === 'compound'
|
|
49
|
+
? Boolean(answers.dataStorageMode || answers.persistencePolicy)
|
|
50
|
+
: false;
|
|
51
|
+
const dataStorageMode = templateId === 'persistence' || compoundPersistenceEnabled
|
|
52
|
+
? answers.dataStorageMode ?? 'custom-table'
|
|
53
|
+
: 'custom-table';
|
|
54
|
+
const persistencePolicy = templateId === 'persistence' || compoundPersistenceEnabled
|
|
55
|
+
? answers.persistencePolicy ?? 'authenticated'
|
|
56
|
+
: 'authenticated';
|
|
57
|
+
return {
|
|
58
|
+
apiClientPackageVersion,
|
|
59
|
+
author: answers.author.trim(),
|
|
60
|
+
blockRuntimePackageVersion,
|
|
61
|
+
blockMetadataVersion: BUILTIN_BLOCK_METADATA_VERSION,
|
|
62
|
+
blockTypesPackageVersion,
|
|
63
|
+
category: metadataDefaults?.category ?? template?.defaultCategory ?? 'widgets',
|
|
64
|
+
icon: metadataDefaults?.icon ?? 'smiley',
|
|
65
|
+
compoundChildTitle,
|
|
66
|
+
compoundChildCategory: COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS.category,
|
|
67
|
+
compoundChildCssClassName,
|
|
68
|
+
compoundChildIcon: COMPOUND_CHILD_BLOCK_METADATA_DEFAULTS.icon,
|
|
69
|
+
compoundChildTitleJson: JSON.stringify(compoundChildTitle),
|
|
70
|
+
compoundPersistenceEnabled: compoundPersistenceEnabled ? 'true' : 'false',
|
|
71
|
+
projectToolsPackageVersion,
|
|
72
|
+
cssClassName,
|
|
73
|
+
dataStorageMode,
|
|
74
|
+
dashCase: slug,
|
|
75
|
+
description,
|
|
76
|
+
frontendCssClassName: buildFrontendCssClassName(cssClassName),
|
|
77
|
+
isAuthenticatedPersistencePolicy: persistencePolicy === 'authenticated' ? 'true' : 'false',
|
|
78
|
+
isPublicPersistencePolicy: persistencePolicy === 'public' ? 'true' : 'false',
|
|
79
|
+
bootstrapCredentialDeclarations: persistencePolicy === 'public'
|
|
80
|
+
? "publicWriteExpiresAt?: number & tags.Type< 'uint32' >;\n\tpublicWriteToken?: string & tags.MinLength< 1 > & tags.MaxLength< 512 >;"
|
|
81
|
+
: "restNonce?: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;",
|
|
82
|
+
persistencePolicyDescriptionJson: JSON.stringify(persistencePolicy === 'authenticated'
|
|
83
|
+
? 'Writes require a logged-in user and a valid REST nonce.'
|
|
84
|
+
: 'Anonymous writes use signed short-lived public tokens, per-request ids, and coarse rate limiting.'),
|
|
85
|
+
keyword: slug.replace(/-/g, ' '),
|
|
86
|
+
namespace,
|
|
87
|
+
needsMigration: '{{needsMigration}}',
|
|
88
|
+
pascalCase,
|
|
89
|
+
phpPrefix,
|
|
90
|
+
phpPrefixUpper,
|
|
91
|
+
restPackageVersion,
|
|
92
|
+
publicWriteRequestIdDeclaration: persistencePolicy === 'public'
|
|
93
|
+
? "publicWriteRequestId: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;"
|
|
94
|
+
: "publicWriteRequestId?: string & tags.MinLength< 1 > & tags.MaxLength< 128 >;",
|
|
95
|
+
restWriteAuthIntent: persistencePolicy === 'public'
|
|
96
|
+
? 'public-write-protected'
|
|
97
|
+
: 'authenticated',
|
|
98
|
+
restWriteAuthMechanism: persistencePolicy === 'public' ? 'public-signed-token' : 'rest-nonce',
|
|
99
|
+
restWriteAuthMode: persistencePolicy === 'public' ? 'public-signed-token' : 'authenticated-rest-nonce',
|
|
100
|
+
slug,
|
|
101
|
+
slugCamelCase: pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1),
|
|
102
|
+
slugKebabCase: slug,
|
|
103
|
+
slugSnakeCase,
|
|
104
|
+
textDomain,
|
|
105
|
+
textdomain: textDomain,
|
|
106
|
+
title,
|
|
107
|
+
titleJson: JSON.stringify(title),
|
|
108
|
+
titleCase: pascalCase,
|
|
109
|
+
persistencePolicy,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -81,19 +81,25 @@ export interface ScaffoldTemplateVariables extends Record<string, string> {
|
|
|
81
81
|
* can surface custom ids. Downstream code uses `isBuiltInTemplateId()` to
|
|
82
82
|
* distinguish built-in templates from custom sources.
|
|
83
83
|
*/
|
|
84
|
-
interface ResolveTemplateOptions {
|
|
84
|
+
export interface ResolveTemplateOptions {
|
|
85
85
|
isInteractive?: boolean;
|
|
86
86
|
selectTemplate?: () => Promise<string>;
|
|
87
87
|
templateId?: string;
|
|
88
88
|
yes?: boolean;
|
|
89
89
|
}
|
|
90
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Options for resolving the package manager in CLI and interactive scaffold flows.
|
|
92
|
+
*/
|
|
93
|
+
export interface ResolvePackageManagerOptions {
|
|
91
94
|
isInteractive?: boolean;
|
|
92
95
|
packageManager?: string;
|
|
93
96
|
selectPackageManager?: () => Promise<PackageManagerId>;
|
|
94
97
|
yes?: boolean;
|
|
95
98
|
}
|
|
96
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Options for collecting scaffold answers from defaults, overrides, and prompts.
|
|
101
|
+
*/
|
|
102
|
+
export interface CollectScaffoldAnswersOptions {
|
|
97
103
|
dataStorageMode?: DataStorageMode;
|
|
98
104
|
namespace?: string;
|
|
99
105
|
phpPrefix?: string;
|
|
@@ -139,12 +145,8 @@ export interface ScaffoldProjectResult {
|
|
|
139
145
|
warnings: string[];
|
|
140
146
|
}
|
|
141
147
|
export { buildBlockCssClassName } from "./scaffold-identifiers.js";
|
|
148
|
+
export { collectScaffoldAnswers, detectAuthor, getDefaultAnswers, resolvePackageManagerId, resolveTemplateId, } from "./scaffold-answer-resolution.js";
|
|
149
|
+
export { getTemplateVariables } from "./scaffold-template-variables.js";
|
|
142
150
|
export declare function isDataStorageMode(value: string): value is DataStorageMode;
|
|
143
151
|
export declare function isPersistencePolicy(value: string): value is PersistencePolicy;
|
|
144
|
-
export declare function detectAuthor(): string;
|
|
145
|
-
export declare function getDefaultAnswers(projectName: string, templateId: string): ScaffoldAnswers;
|
|
146
|
-
export declare function resolveTemplateId({ templateId, yes, isInteractive, selectTemplate, }: ResolveTemplateOptions): Promise<string>;
|
|
147
|
-
export declare function resolvePackageManagerId({ packageManager, yes, isInteractive, selectPackageManager, }: ResolvePackageManagerOptions): Promise<PackageManagerId>;
|
|
148
|
-
export declare function collectScaffoldAnswers({ projectName, templateId, yes, dataStorageMode, namespace, persistencePolicy, phpPrefix, promptText, textDomain, }: CollectScaffoldAnswersOptions): Promise<ScaffoldAnswers>;
|
|
149
|
-
export declare function getTemplateVariables(templateId: string, answers: ScaffoldAnswers): ScaffoldTemplateVariables;
|
|
150
152
|
export declare function scaffoldProject({ projectDir, templateId, answers, dataStorageMode, persistencePolicy, packageManager, externalLayerId, externalLayerSource, externalLayerSourceLabel, repositoryReference, cwd, allowExistingDir, noInstall, installDependencies, variant, withMigrationUi, withTestPreset, withWpEnv, }: ScaffoldProjectOptions): Promise<ScaffoldProjectResult>;
|