@wp-typia/project-tools 0.16.11 → 0.16.13

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.
Files changed (119) hide show
  1. package/README.md +9 -3
  2. package/dist/runtime/block-generator-service-core.d.ts +8 -0
  3. package/dist/runtime/block-generator-service-core.js +274 -0
  4. package/dist/runtime/block-generator-service-spec.d.ts +104 -0
  5. package/dist/runtime/block-generator-service-spec.js +139 -0
  6. package/dist/runtime/block-generator-service.d.ts +2 -110
  7. package/dist/runtime/block-generator-service.js +2 -389
  8. package/dist/runtime/built-in-block-artifact-documents.d.ts +3 -0
  9. package/dist/runtime/built-in-block-artifact-documents.js +2 -0
  10. package/dist/runtime/built-in-block-artifact-types.d.ts +51 -0
  11. package/dist/runtime/built-in-block-artifact-types.js +304 -0
  12. package/dist/runtime/built-in-block-artifacts.js +4 -803
  13. package/dist/runtime/built-in-block-attribute-emitters.d.ts +71 -0
  14. package/dist/runtime/built-in-block-attribute-emitters.js +176 -0
  15. package/dist/runtime/built-in-block-attribute-specs.d.ts +38 -0
  16. package/dist/runtime/built-in-block-attribute-specs.js +358 -0
  17. package/dist/runtime/built-in-block-code-templates/basic.d.ts +4 -0
  18. package/dist/runtime/built-in-block-code-templates/basic.js +249 -0
  19. package/dist/runtime/built-in-block-code-templates/compound-child.d.ts +4 -0
  20. package/dist/runtime/built-in-block-code-templates/compound-child.js +138 -0
  21. package/dist/runtime/built-in-block-code-templates/compound-parent.d.ts +6 -0
  22. package/dist/runtime/built-in-block-code-templates/compound-parent.js +227 -0
  23. package/dist/runtime/built-in-block-code-templates/compound-persistence.d.ts +4 -0
  24. package/dist/runtime/built-in-block-code-templates/compound-persistence.js +478 -0
  25. package/dist/runtime/built-in-block-code-templates/compound.d.ts +3 -0
  26. package/dist/runtime/built-in-block-code-templates/compound.js +3 -0
  27. package/dist/runtime/built-in-block-code-templates/interactivity.d.ts +5 -0
  28. package/dist/runtime/built-in-block-code-templates/interactivity.js +547 -0
  29. package/dist/runtime/built-in-block-code-templates/persistence.d.ts +5 -0
  30. package/dist/runtime/built-in-block-code-templates/persistence.js +550 -0
  31. package/dist/runtime/built-in-block-code-templates/shared.d.ts +16 -0
  32. package/dist/runtime/built-in-block-code-templates/shared.js +53 -0
  33. package/dist/runtime/built-in-block-code-templates.d.ts +5 -32
  34. package/dist/runtime/built-in-block-code-templates.js +5 -2230
  35. package/dist/runtime/cli-add-block-config.d.ts +6 -0
  36. package/dist/runtime/cli-add-block-config.js +143 -0
  37. package/dist/runtime/cli-add-block-legacy-validator.d.ts +4 -0
  38. package/dist/runtime/cli-add-block-legacy-validator.js +168 -0
  39. package/dist/runtime/cli-add-block.js +3 -301
  40. package/dist/runtime/cli-add-workspace-assets.d.ts +38 -0
  41. package/dist/runtime/cli-add-workspace-assets.js +399 -0
  42. package/dist/runtime/cli-add-workspace.d.ts +2 -38
  43. package/dist/runtime/cli-add-workspace.js +5 -396
  44. package/dist/runtime/cli-diagnostics.js +76 -4
  45. package/dist/runtime/cli-doctor-environment.d.ts +12 -0
  46. package/dist/runtime/cli-doctor-environment.js +123 -0
  47. package/dist/runtime/cli-doctor-workspace.d.ts +18 -0
  48. package/dist/runtime/cli-doctor-workspace.js +308 -0
  49. package/dist/runtime/cli-doctor.d.ts +4 -2
  50. package/dist/runtime/cli-doctor.js +10 -405
  51. package/dist/runtime/cli-help.js +1 -1
  52. package/dist/runtime/cli-scaffold.d.ts +8 -1
  53. package/dist/runtime/cli-scaffold.js +47 -4
  54. package/dist/runtime/migration-command-surface.d.ts +67 -0
  55. package/dist/runtime/migration-command-surface.js +189 -0
  56. package/dist/runtime/migration-diff-rename.d.ts +13 -0
  57. package/dist/runtime/migration-diff-rename.js +192 -0
  58. package/dist/runtime/migration-diff-transform.d.ts +14 -0
  59. package/dist/runtime/migration-diff-transform.js +105 -0
  60. package/dist/runtime/migration-diff.js +12 -297
  61. package/dist/runtime/migration-generated-artifacts.d.ts +3 -0
  62. package/dist/runtime/migration-generated-artifacts.js +41 -0
  63. package/dist/runtime/migration-maintenance-fixtures.d.ts +23 -0
  64. package/dist/runtime/migration-maintenance-fixtures.js +126 -0
  65. package/dist/runtime/migration-maintenance-verify.d.ts +26 -0
  66. package/dist/runtime/migration-maintenance-verify.js +262 -0
  67. package/dist/runtime/migration-maintenance.d.ts +2 -0
  68. package/dist/runtime/migration-maintenance.js +2 -0
  69. package/dist/runtime/migration-planning.d.ts +23 -0
  70. package/dist/runtime/migration-planning.js +131 -0
  71. package/dist/runtime/migration-project-config-source.d.ts +6 -0
  72. package/dist/runtime/migration-project-config-source.js +424 -0
  73. package/dist/runtime/migration-project-layout-discovery.d.ts +61 -0
  74. package/dist/runtime/migration-project-layout-discovery.js +337 -0
  75. package/dist/runtime/migration-project-layout-paths.d.ts +135 -0
  76. package/dist/runtime/migration-project-layout-paths.js +288 -0
  77. package/dist/runtime/migration-project-layout.d.ts +3 -0
  78. package/dist/runtime/migration-project-layout.js +2 -0
  79. package/dist/runtime/migration-project-workspace.d.ts +47 -0
  80. package/dist/runtime/migration-project-workspace.js +212 -0
  81. package/dist/runtime/migration-project.d.ts +4 -94
  82. package/dist/runtime/migration-project.js +3 -1101
  83. package/dist/runtime/migration-render-diff-rule.d.ts +5 -0
  84. package/dist/runtime/migration-render-diff-rule.js +120 -0
  85. package/dist/runtime/migration-render-execution.d.ts +3 -0
  86. package/dist/runtime/migration-render-execution.js +428 -0
  87. package/dist/runtime/migration-render-generated.d.ts +27 -0
  88. package/dist/runtime/migration-render-generated.js +230 -0
  89. package/dist/runtime/migration-render-support.d.ts +3 -0
  90. package/dist/runtime/migration-render-support.js +16 -0
  91. package/dist/runtime/migration-render.d.ts +3 -33
  92. package/dist/runtime/migration-render.js +3 -789
  93. package/dist/runtime/migrations.d.ts +24 -121
  94. package/dist/runtime/migrations.js +12 -700
  95. package/dist/runtime/scaffold-apply-utils.d.ts +9 -0
  96. package/dist/runtime/scaffold-apply-utils.js +27 -4
  97. package/dist/runtime/scaffold-bootstrap.d.ts +45 -0
  98. package/dist/runtime/scaffold-bootstrap.js +185 -0
  99. package/dist/runtime/scaffold-onboarding.d.ts +12 -0
  100. package/dist/runtime/scaffold-onboarding.js +42 -5
  101. package/dist/runtime/scaffold-package-manager-files.d.ts +35 -0
  102. package/dist/runtime/scaffold-package-manager-files.js +79 -0
  103. package/dist/runtime/scaffold.d.ts +1 -12
  104. package/dist/runtime/scaffold.js +11 -394
  105. package/dist/runtime/template-source-contracts.d.ts +81 -0
  106. package/dist/runtime/template-source-contracts.js +1 -0
  107. package/dist/runtime/template-source-external.d.ts +21 -0
  108. package/dist/runtime/template-source-external.js +184 -0
  109. package/dist/runtime/template-source-locators.d.ts +4 -0
  110. package/dist/runtime/template-source-locators.js +72 -0
  111. package/dist/runtime/template-source-normalization.d.ts +7 -0
  112. package/dist/runtime/template-source-normalization.js +53 -0
  113. package/dist/runtime/template-source-remote.d.ts +23 -0
  114. package/dist/runtime/template-source-remote.js +336 -0
  115. package/dist/runtime/template-source-seeds.d.ts +12 -0
  116. package/dist/runtime/template-source-seeds.js +243 -0
  117. package/dist/runtime/template-source.d.ts +4 -86
  118. package/dist/runtime/template-source.js +9 -828
  119. package/package.json +4 -4
@@ -8,6 +8,15 @@ export interface InstallDependenciesOptions {
8
8
  projectDir: string;
9
9
  }
10
10
  export declare function ensureDirectory(targetDir: string, allowExisting?: boolean): Promise<void>;
11
+ /**
12
+ * Builds the generated README markdown for one scaffolded project.
13
+ *
14
+ * @param templateId Scaffold template family or template identifier.
15
+ * @param variables Interpolated scaffold variables used in generated content.
16
+ * @param packageManager Package manager used to format install and script commands.
17
+ * @param options Optional README sections enabled by scaffold presets.
18
+ * @returns Markdown README content for the generated project root.
19
+ */
11
20
  export declare function buildReadme(templateId: string, variables: ScaffoldTemplateVariables, packageManager: PackageManagerId, { withMigrationUi, withTestPreset, withWpEnv, }?: {
12
21
  withMigrationUi?: boolean;
13
22
  withTestPreset?: boolean;
@@ -8,7 +8,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 { getCompoundExtensionWorkflowSection, getOptionalOnboardingNote, getOptionalOnboardingSteps, getPhpRestExtensionPointsSection, getTemplateSourceOfTruthNote, } from "./scaffold-onboarding.js";
11
+ import { getCompoundExtensionWorkflowSection, getInitialCommitCommands, getInitialCommitNote, getOptionalOnboardingNote, getOptionalOnboardingSteps, getQuickStartWorkflowNote, getPhpRestExtensionPointsSection, getTemplateSourceOfTruthNote, } from "./scaffold-onboarding.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";
@@ -36,10 +36,20 @@ export async function ensureDirectory(targetDir, allowExisting = false) {
36
36
  throw new Error(`Target directory is not empty: ${targetDir}`);
37
37
  }
38
38
  }
39
+ /**
40
+ * Builds the generated README markdown for one scaffolded project.
41
+ *
42
+ * @param templateId Scaffold template family or template identifier.
43
+ * @param variables Interpolated scaffold variables used in generated content.
44
+ * @param packageManager Package manager used to format install and script commands.
45
+ * @param options Optional README sections enabled by scaffold presets.
46
+ * @returns Markdown README content for the generated project root.
47
+ */
39
48
  export function buildReadme(templateId, variables, packageManager, { withMigrationUi = false, withTestPreset = false, withWpEnv = false, } = {}) {
40
49
  const optionalOnboardingSteps = getOptionalOnboardingSteps(packageManager, templateId, {
41
50
  compoundPersistenceEnabled: variables.compoundPersistenceEnabled === "true",
42
51
  });
52
+ const initialCommitCommands = getInitialCommitCommands();
43
53
  const sourceOfTruthNote = getTemplateSourceOfTruthNote(templateId, {
44
54
  compoundPersistenceEnabled: variables.compoundPersistenceEnabled === "true",
45
55
  });
@@ -70,20 +80,25 @@ ${variables.description}
70
80
 
71
81
  ${templateId}
72
82
 
73
- ## Development
83
+ ## Quick Start
74
84
 
75
85
  \`\`\`bash
76
86
  ${formatInstallCommand(packageManager)}
77
87
  ${formatRunScript(packageManager, developmentScript)}
78
88
  \`\`\`
79
89
 
80
- ## Build
90
+ ${getQuickStartWorkflowNote(packageManager, templateId, {
91
+ compoundPersistenceEnabled,
92
+ })}
93
+
94
+ ## Build and Verify
81
95
 
82
96
  \`\`\`bash
83
97
  ${formatRunScript(packageManager, "build")}
98
+ ${formatRunScript(packageManager, "typecheck")}
84
99
  \`\`\`
85
100
 
86
- ## Optional First Sync
101
+ ## Advanced Sync
87
102
 
88
103
  \`\`\`bash
89
104
  ${optionalOnboardingSteps.join("\n")}
@@ -93,6 +108,14 @@ ${getOptionalOnboardingNote(packageManager, templateId, {
93
108
  compoundPersistenceEnabled,
94
109
  })}
95
110
 
111
+ ## Before First Commit
112
+
113
+ \`\`\`bash
114
+ ${initialCommitCommands.join("\n")}
115
+ \`\`\`
116
+
117
+ ${getInitialCommitNote()}
118
+
96
119
  ${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
120
  `;
98
121
  }
@@ -0,0 +1,45 @@
1
+ import type { PackageManagerId } from "./package-managers.js";
2
+ import type { ScaffoldTemplateVariables } from "./scaffold.js";
3
+ import type { BuiltInTemplateId } from "./template-registry.js";
4
+ /**
5
+ * Ensures the scaffold target directory exists and is empty unless explicitly allowed.
6
+ *
7
+ * @param targetDir Absolute project directory that will receive scaffold output.
8
+ * @param allowExisting Whether an existing non-empty directory should be accepted.
9
+ * @returns A promise that resolves once the directory precondition is satisfied.
10
+ */
11
+ export declare function ensureScaffoldDirectory(targetDir: string, allowExisting?: boolean): Promise<void>;
12
+ /**
13
+ * Writes built-in starter manifest files into a scaffolded project.
14
+ *
15
+ * @param targetDir Absolute scaffold target directory.
16
+ * @param templateId Built-in template id being scaffolded.
17
+ * @param variables Resolved scaffold template variables.
18
+ * @returns A promise that resolves after all starter manifests are written.
19
+ */
20
+ export declare function writeStarterManifestFiles(targetDir: string, templateId: string, variables: ScaffoldTemplateVariables): Promise<void>;
21
+ /**
22
+ * Seed REST-derived persistence artifacts into a newly scaffolded built-in
23
+ * project before the first manual `sync-rest` run.
24
+ *
25
+ * @param targetDir Absolute scaffold target directory.
26
+ * @param templateId Built-in template id being scaffolded.
27
+ * @param variables Resolved scaffold template variables for the project.
28
+ * @returns A promise that resolves after any required persistence artifacts are generated.
29
+ */
30
+ export declare function seedBuiltInPersistenceArtifacts(targetDir: string, templateId: BuiltInTemplateId, variables: ScaffoldTemplateVariables): Promise<void>;
31
+ /**
32
+ * Detects whether a scaffolded project is the official workspace template.
33
+ *
34
+ * @param projectDir Absolute scaffold target directory.
35
+ * @returns `true` when the project metadata identifies the official workspace template.
36
+ */
37
+ export declare function isOfficialWorkspaceProject(projectDir: string): boolean;
38
+ /**
39
+ * Adds migration scripts and starter workspace files to the official workspace template.
40
+ *
41
+ * @param projectDir Absolute scaffold target directory.
42
+ * @param packageManager Package manager used for generated script commands.
43
+ * @returns A promise that resolves after scripts and migration starter files are written.
44
+ */
45
+ export declare function applyWorkspaceMigrationCapability(projectDir: string, packageManager: PackageManagerId): Promise<void>;
@@ -0,0 +1,185 @@
1
+ import fs from "node:fs";
2
+ import { promises as fsp } from "node:fs";
3
+ import path from "node:path";
4
+ import { formatPackageExecCommand } from "./package-managers.js";
5
+ import { ensureMigrationDirectories, writeInitialMigrationScaffold, writeMigrationConfig, } from "./migration-project.js";
6
+ import { syncPersistenceRestArtifacts } from "./persistence-rest-artifacts.js";
7
+ import { getPackageVersions } from "./package-versions.js";
8
+ import { getStarterManifestFiles, stringifyStarterManifest } from "./starter-manifests.js";
9
+ import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, PROJECT_TOOLS_PACKAGE_ROOT, } from "./template-registry.js";
10
+ const EPHEMERAL_NODE_MODULES_LINK_TYPE = process.platform === "win32" ? "junction" : "dir";
11
+ /**
12
+ * Ensures the scaffold target directory exists and is empty unless explicitly allowed.
13
+ *
14
+ * @param targetDir Absolute project directory that will receive scaffold output.
15
+ * @param allowExisting Whether an existing non-empty directory should be accepted.
16
+ * @returns A promise that resolves once the directory precondition is satisfied.
17
+ */
18
+ export async function ensureScaffoldDirectory(targetDir, allowExisting = false) {
19
+ if (!fs.existsSync(targetDir)) {
20
+ await fsp.mkdir(targetDir, { recursive: true });
21
+ return;
22
+ }
23
+ if (allowExisting) {
24
+ return;
25
+ }
26
+ const entries = await fsp.readdir(targetDir);
27
+ if (entries.length > 0) {
28
+ throw new Error(`Target directory is not empty: ${targetDir}`);
29
+ }
30
+ }
31
+ /**
32
+ * Writes built-in starter manifest files into a scaffolded project.
33
+ *
34
+ * @param targetDir Absolute scaffold target directory.
35
+ * @param templateId Built-in template id being scaffolded.
36
+ * @param variables Resolved scaffold template variables.
37
+ * @returns A promise that resolves after all starter manifests are written.
38
+ */
39
+ export async function writeStarterManifestFiles(targetDir, templateId, variables) {
40
+ const manifests = getStarterManifestFiles(templateId, variables);
41
+ for (const { document, relativePath } of manifests) {
42
+ const destinationPath = path.join(targetDir, relativePath);
43
+ await fsp.mkdir(path.dirname(destinationPath), { recursive: true });
44
+ await fsp.writeFile(destinationPath, stringifyStarterManifest(document), "utf8");
45
+ }
46
+ }
47
+ /**
48
+ * Seed REST-derived persistence artifacts into a newly scaffolded built-in
49
+ * project before the first manual `sync-rest` run.
50
+ *
51
+ * @param targetDir Absolute scaffold target directory.
52
+ * @param templateId Built-in template id being scaffolded.
53
+ * @param variables Resolved scaffold template variables for the project.
54
+ * @returns A promise that resolves after any required persistence artifacts are generated.
55
+ */
56
+ export async function seedBuiltInPersistenceArtifacts(targetDir, templateId, variables) {
57
+ const needsPersistenceArtifacts = templateId === "persistence" ||
58
+ (templateId === "compound" && variables.compoundPersistenceEnabled === "true");
59
+ if (!needsPersistenceArtifacts) {
60
+ return;
61
+ }
62
+ await withEphemeralScaffoldNodeModules(targetDir, async () => {
63
+ if (templateId === "persistence") {
64
+ await syncPersistenceRestArtifacts({
65
+ apiTypesFile: path.join("src", "api-types.ts"),
66
+ outputDir: "src",
67
+ projectDir: targetDir,
68
+ variables,
69
+ });
70
+ return;
71
+ }
72
+ await syncPersistenceRestArtifacts({
73
+ apiTypesFile: path.join("src", "blocks", variables.slugKebabCase, "api-types.ts"),
74
+ outputDir: path.join("src", "blocks", variables.slugKebabCase),
75
+ projectDir: targetDir,
76
+ variables,
77
+ });
78
+ });
79
+ }
80
+ /**
81
+ * Detects whether a scaffolded project is the official workspace template.
82
+ *
83
+ * @param projectDir Absolute scaffold target directory.
84
+ * @returns `true` when the project metadata identifies the official workspace template.
85
+ */
86
+ export function isOfficialWorkspaceProject(projectDir) {
87
+ const packageJsonPath = path.join(projectDir, "package.json");
88
+ if (!fs.existsSync(packageJsonPath)) {
89
+ return false;
90
+ }
91
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
92
+ return (packageJson.wpTypia?.projectType === "workspace" &&
93
+ packageJson.wpTypia?.templatePackage === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE);
94
+ }
95
+ /**
96
+ * Adds migration scripts and starter workspace files to the official workspace template.
97
+ *
98
+ * @param projectDir Absolute scaffold target directory.
99
+ * @param packageManager Package manager used for generated script commands.
100
+ * @returns A promise that resolves after scripts and migration starter files are written.
101
+ */
102
+ export async function applyWorkspaceMigrationCapability(projectDir, packageManager) {
103
+ const packageJsonPath = path.join(projectDir, "package.json");
104
+ const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, "utf8"));
105
+ const wpTypiaPackageVersion = getPackageVersions().wpTypiaPackageVersion;
106
+ const canonicalCliSpecifier = wpTypiaPackageVersion === "^0.0.0"
107
+ ? "wp-typia"
108
+ : `wp-typia@${wpTypiaPackageVersion.replace(/^[~^]/u, "")}`;
109
+ const migrationCli = (args) => formatPackageExecCommand(packageManager, canonicalCliSpecifier, `migrate ${args}`);
110
+ packageJson.scripts = {
111
+ ...(packageJson.scripts ?? {}),
112
+ "migration:init": migrationCli("init --current-migration-version v1"),
113
+ "migration:snapshot": migrationCli("snapshot"),
114
+ "migration:diff": migrationCli("diff"),
115
+ "migration:scaffold": migrationCli("scaffold"),
116
+ "migration:doctor": migrationCli("doctor --all"),
117
+ "migration:fixtures": migrationCli("fixtures --all"),
118
+ "migration:verify": migrationCli("verify --all"),
119
+ "migration:fuzz": migrationCli("fuzz --all"),
120
+ };
121
+ await fsp.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, "\t")}\n`, "utf8");
122
+ writeMigrationConfig(projectDir, {
123
+ blocks: [],
124
+ currentMigrationVersion: "v1",
125
+ snapshotDir: "src/migrations/versions",
126
+ supportedMigrationVersions: ["v1"],
127
+ });
128
+ ensureMigrationDirectories(projectDir, []);
129
+ writeInitialMigrationScaffold(projectDir, "v1", []);
130
+ }
131
+ /**
132
+ * Locate a node_modules directory containing `typia` relative to the project
133
+ * tools package root.
134
+ *
135
+ * Search order:
136
+ * 1. `PROJECT_TOOLS_PACKAGE_ROOT/node_modules`
137
+ * 2. The monorepo root resolved from `PROJECT_TOOLS_PACKAGE_ROOT`
138
+ * 3. The monorepo root `node_modules`
139
+ *
140
+ * @returns The first matching path, or `null` when no candidate contains `typia`.
141
+ */
142
+ function resolveScaffoldGeneratorNodeModulesPath() {
143
+ const candidates = [
144
+ path.join(PROJECT_TOOLS_PACKAGE_ROOT, "node_modules"),
145
+ path.resolve(PROJECT_TOOLS_PACKAGE_ROOT, "..", ".."),
146
+ path.resolve(PROJECT_TOOLS_PACKAGE_ROOT, "..", "..", "node_modules"),
147
+ ];
148
+ for (const candidate of candidates) {
149
+ if (fs.existsSync(path.join(candidate, "typia", "package.json"))) {
150
+ return candidate;
151
+ }
152
+ }
153
+ return null;
154
+ }
155
+ /**
156
+ * Temporarily symlink a scaffold generator node_modules directory into the
157
+ * target project while running an async callback.
158
+ *
159
+ * The helper resolves the source path via `resolveScaffoldGeneratorNodeModulesPath()`
160
+ * and uses `EPHEMERAL_NODE_MODULES_LINK_TYPE` for the symlink. The temporary
161
+ * link is removed in the `finally` block so cleanup still happens if the
162
+ * callback throws.
163
+ *
164
+ * @param targetDir Absolute scaffold target directory.
165
+ * @param callback Async work that requires a resolvable `node_modules`.
166
+ * @returns A promise that resolves after the callback and cleanup complete.
167
+ */
168
+ async function withEphemeralScaffoldNodeModules(targetDir, callback) {
169
+ const targetNodeModulesPath = path.join(targetDir, "node_modules");
170
+ if (fs.existsSync(targetNodeModulesPath)) {
171
+ await callback();
172
+ return;
173
+ }
174
+ const sourceNodeModulesPath = resolveScaffoldGeneratorNodeModulesPath();
175
+ if (!sourceNodeModulesPath) {
176
+ throw new Error("Unable to resolve a node_modules directory with typia for scaffold-time REST artifact generation.");
177
+ }
178
+ await fsp.symlink(sourceNodeModulesPath, targetNodeModulesPath, EPHEMERAL_NODE_MODULES_LINK_TYPE);
179
+ try {
180
+ await callback();
181
+ }
182
+ finally {
183
+ await fsp.rm(targetNodeModulesPath, { force: true, recursive: true });
184
+ }
185
+ }
@@ -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
- !isBuiltInTemplateId(templateId) &&
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
- return `${formatRunScript(packageManager, developmentScript)} ${developmentScript === "dev"
59
- ? "watches the relevant sync scripts during local development."
60
- : "remains the primary local entry point."} ${formatRunScript(packageManager, "start")} still runs one-shot syncs before starting, while ${formatRunScript(packageManager, "build")} and ${typecheckCommand} verify that generated metadata/schema artifacts are already current through ${syncCheckCommand}. Run ${syncCommand} manually when you want to refresh generated artifacts before build, typecheck, or commit. ${syncTypesCommand} stays warn-only by default; use \`${failOnLossySyncCommand}\` to fail only on lossy WordPress projections, or \`${strictSyncCommand}\` for a CI-friendly JSON report that fails on all warnings.${advancedPersistenceNote} They do not create migration history.`;
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,35 @@
1
+ import type { PackageManagerId } from "./package-managers.js";
2
+ /**
3
+ * Normalizes scaffolded package-manager specific files for the selected toolchain.
4
+ *
5
+ * @param targetDir Absolute scaffold target directory.
6
+ * @param packageManagerId Selected package manager id.
7
+ * @returns A promise that resolves after any package-manager sidecar files are updated.
8
+ */
9
+ export declare function normalizePackageManagerFiles(targetDir: string, packageManagerId: PackageManagerId): Promise<void>;
10
+ /**
11
+ * Rewrites the generated package.json for the selected package manager.
12
+ *
13
+ * @param targetDir Absolute scaffold target directory.
14
+ * @param packageManagerId Selected package manager id.
15
+ * @returns A promise that resolves after the package.json file is normalized.
16
+ */
17
+ export declare function normalizePackageJson(targetDir: string, packageManagerId: PackageManagerId): Promise<void>;
18
+ /**
19
+ * Removes lockfiles that do not match the selected package manager.
20
+ *
21
+ * @param targetDir Absolute scaffold target directory.
22
+ * @param packageManagerId Selected package manager id.
23
+ * @returns A promise that resolves after stale lockfiles are removed.
24
+ */
25
+ export declare function removeUnexpectedLockfiles(targetDir: string, packageManagerId: PackageManagerId): Promise<void>;
26
+ /**
27
+ * Installs scaffolded project dependencies with the selected package manager.
28
+ *
29
+ * @param options Absolute target directory and selected package manager id.
30
+ * @returns A promise that resolves after the install command completes.
31
+ */
32
+ export declare function defaultInstallDependencies({ projectDir, packageManager, }: {
33
+ projectDir: string;
34
+ packageManager: PackageManagerId;
35
+ }): Promise<void>;
@@ -0,0 +1,79 @@
1
+ import fs from "node:fs";
2
+ import { promises as fsp } from "node:fs";
3
+ import { execSync } from "node:child_process";
4
+ import path from "node:path";
5
+ import { formatInstallCommand, getPackageManager, transformPackageManagerText, } from "./package-managers.js";
6
+ const LOCKFILES = {
7
+ bun: ["bun.lock", "bun.lockb"],
8
+ npm: ["package-lock.json"],
9
+ pnpm: ["pnpm-lock.yaml"],
10
+ yarn: ["yarn.lock"],
11
+ };
12
+ /**
13
+ * Normalizes scaffolded package-manager specific files for the selected toolchain.
14
+ *
15
+ * @param targetDir Absolute scaffold target directory.
16
+ * @param packageManagerId Selected package manager id.
17
+ * @returns A promise that resolves after any package-manager sidecar files are updated.
18
+ */
19
+ export async function normalizePackageManagerFiles(targetDir, packageManagerId) {
20
+ const yarnRcPath = path.join(targetDir, ".yarnrc.yml");
21
+ if (packageManagerId === "yarn") {
22
+ await fsp.writeFile(yarnRcPath, "nodeLinker: node-modules\n", "utf8");
23
+ return;
24
+ }
25
+ await fsp.rm(yarnRcPath, { force: true });
26
+ }
27
+ /**
28
+ * Rewrites the generated package.json for the selected package manager.
29
+ *
30
+ * @param targetDir Absolute scaffold target directory.
31
+ * @param packageManagerId Selected package manager id.
32
+ * @returns A promise that resolves after the package.json file is normalized.
33
+ */
34
+ export async function normalizePackageJson(targetDir, packageManagerId) {
35
+ const packageJsonPath = path.join(targetDir, "package.json");
36
+ if (!fs.existsSync(packageJsonPath)) {
37
+ return;
38
+ }
39
+ const packageManager = getPackageManager(packageManagerId);
40
+ const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, "utf8"));
41
+ packageJson.packageManager = packageManager.packageManagerField;
42
+ if (packageJson.scripts) {
43
+ for (const [key, value] of Object.entries(packageJson.scripts)) {
44
+ if (typeof value === "string") {
45
+ packageJson.scripts[key] = transformPackageManagerText(value, packageManagerId);
46
+ }
47
+ }
48
+ }
49
+ await fsp.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, "\t")}\n`, "utf8");
50
+ }
51
+ /**
52
+ * Removes lockfiles that do not match the selected package manager.
53
+ *
54
+ * @param targetDir Absolute scaffold target directory.
55
+ * @param packageManagerId Selected package manager id.
56
+ * @returns A promise that resolves after stale lockfiles are removed.
57
+ */
58
+ export async function removeUnexpectedLockfiles(targetDir, packageManagerId) {
59
+ const keep = new Set(LOCKFILES[packageManagerId] ?? []);
60
+ const allLockfiles = Object.values(LOCKFILES).flat();
61
+ await Promise.all(allLockfiles.map(async (filename) => {
62
+ if (keep.has(filename)) {
63
+ return;
64
+ }
65
+ await fsp.rm(path.join(targetDir, filename), { force: true });
66
+ }));
67
+ }
68
+ /**
69
+ * Installs scaffolded project dependencies with the selected package manager.
70
+ *
71
+ * @param options Absolute target directory and selected package manager id.
72
+ * @returns A promise that resolves after the install command completes.
73
+ */
74
+ export async function defaultInstallDependencies({ projectDir, packageManager, }) {
75
+ execSync(formatInstallCommand(packageManager), {
76
+ cwd: projectDir,
77
+ stdio: "inherit",
78
+ });
79
+ }
@@ -138,17 +138,7 @@ export interface ScaffoldProjectResult {
138
138
  variables: ScaffoldTemplateVariables;
139
139
  warnings: string[];
140
140
  }
141
- /**
142
- * Builds the generated WordPress wrapper CSS class for a scaffolded block.
143
- *
144
- * Returns `wp-block-{namespace}-{slug}` when a non-empty namespace is present,
145
- * or `wp-block-{slug}` when the namespace is empty or undefined. When the
146
- * normalized namespace equals the normalized slug, appends `-block` so the
147
- * generated class avoids repeated namespace segments without colliding with the
148
- * default core wrapper classes. Both inputs are normalized and validated with
149
- * the same scaffold identifier rules used for block names.
150
- */
151
- export declare function buildBlockCssClassName(namespace: string | undefined, slug: string): string;
141
+ export { buildBlockCssClassName } from "./scaffold-identifiers.js";
152
142
  export declare function isDataStorageMode(value: string): value is DataStorageMode;
153
143
  export declare function isPersistencePolicy(value: string): value is PersistencePolicy;
154
144
  export declare function detectAuthor(): string;
@@ -158,4 +148,3 @@ export declare function resolvePackageManagerId({ packageManager, yes, isInterac
158
148
  export declare function collectScaffoldAnswers({ projectName, templateId, yes, dataStorageMode, namespace, persistencePolicy, phpPrefix, promptText, textDomain, }: CollectScaffoldAnswersOptions): Promise<ScaffoldAnswers>;
159
149
  export declare function getTemplateVariables(templateId: string, answers: ScaffoldAnswers): ScaffoldTemplateVariables;
160
150
  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>;
161
- export {};