wp-typia 0.15.5 → 0.16.0

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.
@@ -9,10 +9,11 @@ import Create from '../src/commands/create.js'
9
9
  import Doctor from '../src/commands/doctor.js'
10
10
  import Mcp from '../src/commands/mcp.js'
11
11
  import Migrate from '../src/commands/migrate.js'
12
+ import Sync from '../src/commands/sync.js'
12
13
  import Templates from '../src/commands/templates.js'
13
14
 
14
15
  // Narrow list of command names to avoid typeof-cycles in types
15
- const names = ['add', 'create', 'doctor', 'mcp', 'migrate', 'templates'] as const
16
+ const names = ['add', 'create', 'doctor', 'mcp', 'migrate', 'sync', 'templates'] as const
16
17
  type GeneratedNames = typeof names[number]
17
18
 
18
19
  const modules: Record<GeneratedNames, Command<any>> = {
@@ -21,6 +22,7 @@ const modules: Record<GeneratedNames, Command<any>> = {
21
22
  'doctor': Doctor,
22
23
  'mcp': Mcp,
23
24
  'migrate': Migrate,
25
+ 'sync': Sync,
24
26
  'templates': Templates
25
27
  } as const
26
28
 
@@ -50,6 +52,11 @@ const metadata: Record<GeneratedNames, GeneratedCommandMeta> = {
50
52
  description: 'Run migration workflows for migration-capable wp-typia projects.',
51
53
  path: './src/commands/migrate'
52
54
  },
55
+ 'sync': {
56
+ name: 'sync',
57
+ description: 'Run the common generated-project sync workflow.',
58
+ path: './src/commands/sync'
59
+ },
53
60
  'templates': {
54
61
  name: 'templates',
55
62
  description: 'Inspect built-in and external scaffold templates.',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wp-typia",
3
- "version": "0.15.5",
3
+ "version": "0.16.0",
4
4
  "description": "Canonical CLI package for wp-typia scaffolding and project workflows",
5
5
  "packageManager": "bun@1.3.11",
6
6
  "type": "module",
@@ -65,7 +65,7 @@
65
65
  "@bunli/tui": "0.6.0",
66
66
  "@bunli/utils": "0.6.0",
67
67
  "@wp-typia/api-client": "^0.4.2",
68
- "@wp-typia/project-tools": "0.15.4",
68
+ "@wp-typia/project-tools": "0.16.0",
69
69
  "better-result": "^2.7.0",
70
70
  "react": "19.2.0",
71
71
  "react-dom": "19.2.0",
@@ -6,6 +6,7 @@ export const WP_TYPIA_BUNLI_MIGRATION_DOC = "docs/bunli-cli-migration.md";
6
6
 
7
7
  export const WP_TYPIA_RESERVED_TOP_LEVEL_COMMAND_NAMES = [
8
8
  "create",
9
+ "sync",
9
10
  "add",
10
11
  "migrate",
11
12
  "templates",
@@ -18,6 +19,7 @@ export const WP_TYPIA_RESERVED_TOP_LEVEL_COMMAND_NAMES = [
18
19
 
19
20
  export const WP_TYPIA_TOP_LEVEL_COMMAND_NAMES = [
20
21
  "create",
22
+ "sync",
21
23
  "add",
22
24
  "migrate",
23
25
  "templates",
@@ -108,6 +110,10 @@ export const WP_TYPIA_FUTURE_COMMAND_TREE = [
108
110
  description: "Scaffold a new wp-typia project.",
109
111
  name: "create",
110
112
  },
113
+ {
114
+ description: "Run the common generated-project sync workflow.",
115
+ name: "sync",
116
+ },
111
117
  {
112
118
  description: "Extend an official wp-typia workspace.",
113
119
  name: "add",
@@ -5,10 +5,12 @@ import { createCommand } from "./commands/create";
5
5
  import { doctorCommand } from "./commands/doctor";
6
6
  import { mcpCommand } from "./commands/mcp";
7
7
  import { migrateCommand } from "./commands/migrate";
8
+ import { syncCommand } from "./commands/sync";
8
9
  import { templatesCommand } from "./commands/templates";
9
10
 
10
11
  export const wpTypiaCommands: Command<any, any>[] = [
11
12
  createCommand,
13
+ syncCommand,
12
14
  addCommand,
13
15
  migrateCommand,
14
16
  templatesCommand,
@@ -0,0 +1,25 @@
1
+ import { defineCommand } from "@bunli/core";
2
+ import { z } from "zod";
3
+
4
+ import { executeSyncCommand } from "../runtime-bridge";
5
+
6
+ export const syncCommand = defineCommand({
7
+ description: "Run the common generated-project sync workflow.",
8
+ handler: async (args) => {
9
+ await executeSyncCommand({
10
+ check: Boolean(args.flags.check),
11
+ cwd: args.cwd,
12
+ });
13
+ },
14
+ name: "sync",
15
+ options: {
16
+ check: {
17
+ argumentKind: "flag" as const,
18
+ description:
19
+ "Check generated artifacts without writing changes. Advanced sync-types-only flags stay on sync-types.",
20
+ schema: z.boolean().default(false),
21
+ },
22
+ },
23
+ });
24
+
25
+ export default syncCommand;
@@ -1,5 +1,10 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+
1
5
  import {
2
6
  createReadlinePrompt,
7
+ formatRunScript,
3
8
  formatAddHelpText,
4
9
  formatMigrationHelpText,
5
10
  formatTemplateDetails,
@@ -44,6 +49,11 @@ type TemplatesExecutionInput = {
44
49
  };
45
50
  };
46
51
 
52
+ type SyncExecutionInput = {
53
+ check?: boolean;
54
+ cwd: string;
55
+ };
56
+
47
57
  type MigrateExecutionInput = {
48
58
  command?: string;
49
59
  cwd: string;
@@ -53,6 +63,14 @@ type MigrateExecutionInput = {
53
63
  };
54
64
 
55
65
  type PrintLine = (line: string) => void;
66
+ type PackageManagerId = "bun" | "npm" | "pnpm" | "yarn";
67
+
68
+ type SyncProjectContext = {
69
+ cwd: string;
70
+ packageJsonPath: string;
71
+ packageManager: PackageManagerId;
72
+ scripts: Partial<Record<"sync" | "sync-rest" | "sync-types", string>>;
73
+ };
56
74
 
57
75
  function printBlock(lines: string[], printLine: PrintLine): void {
58
76
  for (const line of lines) {
@@ -100,6 +118,131 @@ function pushFlag(argv: string[], name: string, value: unknown): void {
100
118
  argv.push(`--${name}`, String(value));
101
119
  }
102
120
 
121
+ function getSyncRootError(cwd: string): Error {
122
+ return new Error(
123
+ `No generated wp-typia project root was found at ${cwd}. Run \`wp-typia sync\` from a scaffolded project or official workspace root.`,
124
+ );
125
+ }
126
+
127
+ function inferSyncPackageManager(cwd: string, packageManagerField?: string): PackageManagerId {
128
+ const field = String(packageManagerField ?? "");
129
+ if (field.startsWith("bun@")) return "bun";
130
+ if (field.startsWith("npm@")) return "npm";
131
+ if (field.startsWith("pnpm@")) return "pnpm";
132
+ if (field.startsWith("yarn@")) return "yarn";
133
+
134
+ if (
135
+ fs.existsSync(path.join(cwd, "bun.lock")) ||
136
+ fs.existsSync(path.join(cwd, "bun.lockb"))
137
+ ) {
138
+ return "bun";
139
+ }
140
+ if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) {
141
+ return "pnpm";
142
+ }
143
+ if (
144
+ fs.existsSync(path.join(cwd, "yarn.lock")) ||
145
+ fs.existsSync(path.join(cwd, ".pnp.cjs")) ||
146
+ fs.existsSync(path.join(cwd, ".pnp.loader.mjs")) ||
147
+ fs.existsSync(path.join(cwd, ".yarnrc.yml"))
148
+ ) {
149
+ return "yarn";
150
+ }
151
+ if (
152
+ fs.existsSync(path.join(cwd, "package-lock.json")) ||
153
+ fs.existsSync(path.join(cwd, "npm-shrinkwrap.json"))
154
+ ) {
155
+ return "npm";
156
+ }
157
+
158
+ return "npm";
159
+ }
160
+
161
+ function resolveSyncProjectContext(cwd: string): SyncProjectContext {
162
+ const packageJsonPath = path.join(cwd, "package.json");
163
+ if (!fs.existsSync(packageJsonPath)) {
164
+ throw getSyncRootError(cwd);
165
+ }
166
+
167
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")) as {
168
+ packageManager?: string;
169
+ scripts?: Record<string, unknown>;
170
+ };
171
+ const scripts = packageJson.scripts ?? {};
172
+ const syncScripts = {
173
+ sync: typeof scripts.sync === "string" ? scripts.sync : undefined,
174
+ "sync-rest":
175
+ typeof scripts["sync-rest"] === "string" ? scripts["sync-rest"] : undefined,
176
+ "sync-types":
177
+ typeof scripts["sync-types"] === "string" ? scripts["sync-types"] : undefined,
178
+ } satisfies SyncProjectContext["scripts"];
179
+
180
+ if (!syncScripts.sync && !syncScripts["sync-types"]) {
181
+ throw new Error(
182
+ `Expected ${packageJsonPath} to define either a \`sync\` or \`sync-types\` script.`,
183
+ );
184
+ }
185
+
186
+ return {
187
+ cwd,
188
+ packageJsonPath,
189
+ packageManager: inferSyncPackageManager(cwd, packageJson.packageManager),
190
+ scripts: syncScripts,
191
+ };
192
+ }
193
+
194
+ function getPackageManagerRunInvocation(
195
+ packageManager: PackageManagerId,
196
+ scriptName: string,
197
+ extraArgs: string[],
198
+ ): { args: string[]; command: string } {
199
+ switch (packageManager) {
200
+ case "bun":
201
+ return { args: ["run", scriptName, ...extraArgs], command: "bun" };
202
+ case "npm":
203
+ return {
204
+ args: ["run", scriptName, ...(extraArgs.length > 0 ? ["--", ...extraArgs] : [])],
205
+ command: "npm",
206
+ };
207
+ case "pnpm":
208
+ return { args: ["run", scriptName, ...extraArgs], command: "pnpm" };
209
+ case "yarn":
210
+ return { args: ["run", scriptName, ...extraArgs], command: "yarn" };
211
+ }
212
+ }
213
+
214
+ function runProjectScript(
215
+ project: SyncProjectContext,
216
+ scriptName: "sync" | "sync-rest" | "sync-types",
217
+ extraArgs: string[],
218
+ ): void {
219
+ const script = project.scripts[scriptName];
220
+ if (!script) {
221
+ return;
222
+ }
223
+
224
+ const invocation = getPackageManagerRunInvocation(
225
+ project.packageManager,
226
+ scriptName,
227
+ extraArgs,
228
+ );
229
+
230
+ const result = spawnSync(invocation.command, invocation.args, {
231
+ cwd: project.cwd,
232
+ shell: process.platform === "win32",
233
+ stdio: "inherit",
234
+ });
235
+
236
+ if (result.error || result.status !== 0) {
237
+ throw new Error(
238
+ `\`${formatRunScript(project.packageManager, scriptName, extraArgs.join(" "))}\` failed.`,
239
+ {
240
+ cause: result.error,
241
+ },
242
+ );
243
+ }
244
+ }
245
+
103
246
  const PACKAGE_MANAGER_PROMPT_OPTIONS = [
104
247
  { label: "npm", value: "npm", hint: "Use npm" },
105
248
  { label: "pnpm", value: "pnpm", hint: "Use pnpm" },
@@ -384,6 +527,25 @@ export async function executeDoctorCommand(cwd: string): Promise<void> {
384
527
  await runDoctor(cwd);
385
528
  }
386
529
 
530
+ export async function executeSyncCommand({
531
+ check = false,
532
+ cwd,
533
+ }: SyncExecutionInput): Promise<void> {
534
+ const project = resolveSyncProjectContext(cwd);
535
+ const extraArgs = check ? ["--check"] : [];
536
+
537
+ if (project.scripts.sync) {
538
+ runProjectScript(project, "sync", extraArgs);
539
+ return;
540
+ }
541
+
542
+ runProjectScript(project, "sync-types", extraArgs);
543
+
544
+ if (project.scripts["sync-rest"]) {
545
+ runProjectScript(project, "sync-rest", extraArgs);
546
+ }
547
+ }
548
+
387
549
  export async function executeMigrateCommand({
388
550
  command,
389
551
  cwd,