wp-typia 0.22.4 → 0.22.6

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.
@@ -4,7 +4,7 @@ import {
4
4
  } from "./cli-1sm60g1z.js";
5
5
  import {
6
6
  discoverMigrationInitLayout
7
- } from "./cli-hb9vpsev.js";
7
+ } from "./cli-2rqf6t0b.js";
8
8
  import"./cli-tke8twkn.js";
9
9
  import {
10
10
  quoteTsString,
@@ -13,7 +13,8 @@ import {
13
13
  snapshotWorkspaceFiles,
14
14
  toPascalCase,
15
15
  updateWorkspaceInventorySource
16
- } from "./cli-qr2ek735.js";
16
+ } from "./cli-5kn2p7ee.js";
17
+ import"./cli-fys8vm2t.js";
17
18
  import {
18
19
  CLI_DIAGNOSTIC_CODES,
19
20
  createCliDiagnosticCodeError
@@ -21,27 +22,26 @@ import {
21
22
  import {
22
23
  parseWorkspacePackageManagerId,
23
24
  tryResolveWorkspaceProject
24
- } from "./cli-btbpt84c.js";
25
+ } from "./cli-hhp1d348.js";
25
26
  import {
26
27
  formatAddDevDependenciesCommand,
27
28
  formatPackageExecCommand,
28
29
  formatRunScript,
29
30
  getPackageManager,
30
31
  transformPackageManagerText
31
- } from "./cli-6bhfzq5e.js";
32
+ } from "./cli-52ke0ptp.js";
32
33
  import {
33
34
  __toESM
34
35
  } from "./cli-xnn9xjcy.js";
35
36
 
36
- // ../wp-typia-project-tools/src/runtime/cli-init.ts
37
- var import_typescript = __toESM(require_typescript(), 1);
38
- import fs from "fs";
37
+ // ../wp-typia-project-tools/src/runtime/cli-init-apply.ts
38
+ import fs3 from "fs";
39
39
  import { promises as fsp } from "fs";
40
+ import path4 from "path";
41
+
42
+ // ../wp-typia-project-tools/src/runtime/cli-init-package-json.ts
43
+ import fs from "fs";
40
44
  import path from "path";
41
- import { analyzeSourceTypes } from "@wp-typia/block-runtime/metadata-parser";
42
- var SUPPORTED_RETROFIT_LAYOUT_NOTE = "Supported retrofit layouts currently mirror the migration bootstrap detector: `src/block.json` + `src/types.ts` + `src/save.tsx`, legacy root `block.json` + `src/types.ts` + `src/save.tsx`, or multi-block `src/blocks/*/block.json` workspaces.";
43
- var RETROFIT_APPLY_PREVIEW_NOTE = "If you rerun with `wp-typia init --apply`, package.json and generated helper files are snapshotted and rolled back automatically if a write fails.";
44
- var RETROFIT_ROLLBACK_NOTE = "Apply mode writes package.json and generated helper files with rollback-on-failure protection.";
45
45
  var BASE_RETROFIT_SCRIPTS = {
46
46
  sync: "tsx scripts/sync-project.ts",
47
47
  "sync-types": "tsx scripts/sync-types-to-block-json.ts",
@@ -55,9 +55,6 @@ var BASE_RETROFIT_DEV_DEPENDENCIES = [
55
55
  "typescript",
56
56
  "typia"
57
57
  ];
58
- function normalizeRelativePath(value) {
59
- return value.replace(/\\/gu, "/");
60
- }
61
58
  function readProjectPackageJson(projectDir) {
62
59
  const packageJsonPath = path.join(projectDir, "package.json");
63
60
  if (!fs.existsSync(packageJsonPath)) {
@@ -164,14 +161,80 @@ function buildPackageManagerFieldChange(packageJson, packageManager, options = {
164
161
  requiredValue
165
162
  };
166
163
  }
164
+ function hasExistingWpTypiaProjectSurface(projectDir, packageJson) {
165
+ const scripts = packageJson?.scripts ?? {};
166
+ const hasSyncSurface = typeof scripts.sync === "string" || typeof scripts["sync-types"] === "string";
167
+ const hasHelperFiles = [
168
+ path.join("scripts", "block-config.ts"),
169
+ path.join("scripts", "sync-project.ts"),
170
+ path.join("scripts", "sync-types-to-block-json.ts")
171
+ ].every((relativePath) => fs.existsSync(path.join(projectDir, relativePath)));
172
+ const hasRuntimeDeps = typeof getExistingDependencyVersion(packageJson, "@wp-typia/block-runtime") === "string" && typeof getExistingDependencyVersion(packageJson, "@wp-typia/block-types") === "string";
173
+ return hasSyncSurface && hasHelperFiles && hasRuntimeDeps;
174
+ }
175
+ function buildRequiredDevDependencyMapEntries() {
176
+ return Object.entries(buildRequiredDevDependencyMap()).map(([name, version]) => `${name}@${version.replace(/^workspace:/u, "")}`);
177
+ }
178
+ function setDependencyVersion(packageJson, name, requiredValue) {
179
+ if (packageJson.devDependencies?.[name] !== undefined) {
180
+ packageJson.devDependencies[name] = requiredValue;
181
+ return;
182
+ }
183
+ if (packageJson.dependencies?.[name] !== undefined) {
184
+ packageJson.dependencies[name] = requiredValue;
185
+ return;
186
+ }
187
+ packageJson.devDependencies ??= {};
188
+ packageJson.devDependencies[name] = requiredValue;
189
+ }
190
+ function buildNextProjectPackageJson(options) {
191
+ const nextPackageJson = options.packageJson ? JSON.parse(JSON.stringify(options.packageJson)) : {
192
+ name: options.projectName,
193
+ private: true
194
+ };
195
+ nextPackageJson.devDependencies ??= {};
196
+ nextPackageJson.scripts ??= {};
197
+ for (const dependencyChange of options.packageChanges.addDevDependencies) {
198
+ setDependencyVersion(nextPackageJson, dependencyChange.name, dependencyChange.requiredValue);
199
+ }
200
+ if (options.packageChanges.packageManagerField) {
201
+ nextPackageJson.packageManager = options.packageChanges.packageManagerField.requiredValue;
202
+ } else if (!nextPackageJson.packageManager && options.packageManager !== "npm") {
203
+ nextPackageJson.packageManager = getPackageManager(options.packageManager).packageManagerField;
204
+ }
205
+ for (const scriptChange of options.packageChanges.scripts) {
206
+ nextPackageJson.scripts[scriptChange.name] = scriptChange.requiredValue;
207
+ }
208
+ return nextPackageJson;
209
+ }
210
+ function buildProjectPackageJsonSource(packageJson) {
211
+ return `${JSON.stringify(packageJson, null, 2)}
212
+ `;
213
+ }
214
+
215
+ // ../wp-typia-project-tools/src/runtime/cli-init-plan.ts
216
+ var import_typescript = __toESM(require_typescript(), 1);
217
+ import fs2 from "fs";
218
+ import path2 from "path";
219
+ import { analyzeSourceTypes } from "@wp-typia/block-runtime/metadata-parser";
220
+
221
+ // ../wp-typia-project-tools/src/runtime/cli-init-types.ts
222
+ var SUPPORTED_RETROFIT_LAYOUT_NOTE = "Supported retrofit layouts currently mirror the migration bootstrap detector: `src/block.json` + `src/types.ts` + `src/save.tsx`, legacy root `block.json` + `src/types.ts` + `src/save.tsx`, or multi-block `src/blocks/*/block.json` workspaces.";
223
+ var RETROFIT_APPLY_PREVIEW_NOTE = "If you rerun with `wp-typia init --apply`, package.json and generated helper files are snapshotted and rolled back automatically if a write fails.";
224
+ var RETROFIT_ROLLBACK_NOTE = "Apply mode writes package.json and generated helper files with rollback-on-failure protection.";
225
+
226
+ // ../wp-typia-project-tools/src/runtime/cli-init-plan.ts
227
+ function normalizeRelativePath(value) {
228
+ return value.replace(/\\/gu, "/");
229
+ }
167
230
  function buildGeneratedArtifactPaths(blockJsonFile, manifestFile) {
168
- const manifestDir = path.dirname(manifestFile);
231
+ const manifestDir = path2.dirname(manifestFile);
169
232
  const artifactPaths = [
170
233
  blockJsonFile,
171
234
  manifestFile,
172
- path.join(manifestDir, "typia.schema.json"),
173
- path.join(manifestDir, "typia-validator.php"),
174
- path.join(manifestDir, "typia.openapi.json")
235
+ path2.join(manifestDir, "typia.schema.json"),
236
+ path2.join(manifestDir, "typia-validator.php"),
237
+ path2.join(manifestDir, "typia.openapi.json")
175
238
  ];
176
239
  return Array.from(new Set(artifactPaths.map((filePath) => normalizeRelativePath(filePath))));
177
240
  }
@@ -192,8 +255,8 @@ function isObjectLikeSourceType(projectDir, typesFile, sourceTypeName) {
192
255
  return analyzedTypes[sourceTypeName]?.kind === "object";
193
256
  }
194
257
  function inferRetrofitAttributeTypeName(projectDir, block) {
195
- const typesPath = path.join(projectDir, block.typesFile);
196
- const typesSource = fs.readFileSync(typesPath, "utf8");
258
+ const typesPath = path2.join(projectDir, block.typesFile);
259
+ const typesSource = fs2.readFileSync(typesPath, "utf8");
197
260
  const blockNameSegments = block.blockName.split("/");
198
261
  const slug = blockNameSegments[blockNameSegments.length - 1] ?? block.key;
199
262
  const candidateNames = collectNamedSourceTypeCandidates(typesSource);
@@ -227,69 +290,326 @@ function buildRetrofitBlockTarget(projectDir, block) {
227
290
  typesFile: block.typesFile
228
291
  };
229
292
  }
230
- function buildRetrofitBlockConfigEntry(target) {
231
- return [
232
- "\t{",
233
- ` slug: ${quoteTsString(target.slug)},`,
234
- ` attributeTypeName: ${quoteTsString(target.attributeTypeName)},`,
235
- ` blockJsonFile: ${quoteTsString(target.blockJsonFile)},`,
236
- ` manifestFile: ${quoteTsString(target.manifestFile)},`,
237
- ` typesFile: ${quoteTsString(target.typesFile)},`,
238
- "\t},"
239
- ].join(`
240
- `);
293
+ function buildInitLayoutDetails(projectDir) {
294
+ try {
295
+ const discoveredLayout = discoverMigrationInitLayout(projectDir);
296
+ const discoveredBlocks = discoveredLayout.mode === "multi" ? discoveredLayout.blocks : [discoveredLayout.block];
297
+ let blockTargets;
298
+ try {
299
+ blockTargets = discoveredBlocks.map((block) => buildRetrofitBlockTarget(projectDir, block));
300
+ } catch (error) {
301
+ const message = error instanceof Error ? error.message : String(error);
302
+ return {
303
+ blockNames: discoveredBlocks.map((block) => block.blockName),
304
+ blockTargets: [],
305
+ description: "Detected supported block files, but could not infer retrofit block-config metadata automatically yet.",
306
+ generatedArtifacts: [],
307
+ kind: "unsupported",
308
+ notes: [message, SUPPORTED_RETROFIT_LAYOUT_NOTE]
309
+ };
310
+ }
311
+ if (discoveredLayout.mode === "multi") {
312
+ return {
313
+ blockNames: discoveredBlocks.map((block) => block.blockName),
314
+ blockTargets,
315
+ description: `Detected a supported multi-block retrofit candidate (${discoveredBlocks.length} targets).`,
316
+ generatedArtifacts: discoveredBlocks.flatMap((block) => buildGeneratedArtifactPaths(block.blockJsonFile, block.manifestFile)),
317
+ kind: "multi-block",
318
+ notes: [
319
+ "Migration bootstrap can stay optional. Add it later with `wp-typia migrate init --current-migration-version v1` once the typed sync surface is in place."
320
+ ]
321
+ };
322
+ }
323
+ return {
324
+ blockNames: [discoveredLayout.block.blockName],
325
+ blockTargets,
326
+ description: "Detected a supported single-block retrofit candidate.",
327
+ generatedArtifacts: buildGeneratedArtifactPaths(discoveredLayout.block.blockJsonFile, discoveredLayout.block.manifestFile),
328
+ kind: "single-block",
329
+ notes: discoveredLayout.block.blockJsonFile === "block.json" ? [
330
+ "Legacy root `block.json` layouts are still supported for retrofit planning, but newer scaffolds keep generated block metadata under `src/`."
331
+ ] : []
332
+ };
333
+ } catch (error) {
334
+ const message = error instanceof Error ? error.message : String(error);
335
+ return {
336
+ blockNames: [],
337
+ blockTargets: [],
338
+ description: "No supported retrofit layout was auto-detected yet.",
339
+ generatedArtifacts: [],
340
+ kind: "unsupported",
341
+ notes: [message, SUPPORTED_RETROFIT_LAYOUT_NOTE]
342
+ };
343
+ }
241
344
  }
242
- function buildRetrofitBlockConfigSource(targets) {
243
- const blockEntries = targets.map(buildRetrofitBlockConfigEntry).join(`
244
- `);
245
- const baseSource = `export interface WorkspaceBlockConfig {
246
- attributeTypeName: string;
247
- apiTypesFile?: string;
248
- blockJsonFile?: string;
249
- manifestFile?: string;
250
- openApiFile?: string;
251
- restManifest?: ReturnType<
252
- typeof import( '@wp-typia/block-runtime/metadata-core' ).defineEndpointManifest
253
- >;
254
- slug: string;
255
- typesFile: string;
345
+ function buildPlannedFiles(projectDir, layoutKind) {
346
+ if (layoutKind === "unsupported") {
347
+ return [];
348
+ }
349
+ return [
350
+ {
351
+ action: fs2.existsSync(path2.join(projectDir, "scripts", "block-config.ts")) ? "update" : "add",
352
+ path: "scripts/block-config.ts",
353
+ purpose: "Declare the current retrofit block targets so sync-types can regenerate metadata from the existing TypeScript source of truth."
354
+ },
355
+ {
356
+ action: fs2.existsSync(path2.join(projectDir, "scripts", "sync-types-to-block-json.ts")) ? "update" : "add",
357
+ path: "scripts/sync-types-to-block-json.ts",
358
+ purpose: "Generate block.json and Typia metadata artifacts from the current TypeScript source of truth."
359
+ },
360
+ {
361
+ action: fs2.existsSync(path2.join(projectDir, "scripts", "sync-project.ts")) ? "update" : "add",
362
+ path: "scripts/sync-project.ts",
363
+ purpose: "Provide one shared sync entrypoint that can grow into sync-rest or workspace-aware refresh steps later."
364
+ }
365
+ ];
256
366
  }
257
-
258
- export const BLOCKS: WorkspaceBlockConfig[] = [
259
- ${blockEntries}
260
- ];
261
- `;
262
- return `${updateWorkspaceInventorySource(baseSource)}
263
- `;
367
+ function buildChangeSummary(changes, options) {
368
+ const lines = [];
369
+ for (const dependencyChange of changes.packageChanges.addDevDependencies) {
370
+ lines.push(`devDependency ${dependencyChange.action} ${dependencyChange.name} -> ${dependencyChange.requiredValue}`);
371
+ }
372
+ if (changes.packageChanges.packageManagerField) {
373
+ lines.push(`packageManager ${changes.packageChanges.packageManagerField.action} -> ${changes.packageChanges.packageManagerField.requiredValue}`);
374
+ }
375
+ for (const scriptChange of changes.packageChanges.scripts) {
376
+ lines.push(`script ${scriptChange.action} ${scriptChange.name} -> ${scriptChange.requiredValue}`);
377
+ }
378
+ for (const filePlan of changes.plannedFiles) {
379
+ lines.push(`file ${filePlan.action} ${filePlan.path} (${filePlan.purpose})`);
380
+ }
381
+ if (options.includeGeneratedArtifacts) {
382
+ for (const artifactPath of changes.generatedArtifacts) {
383
+ lines.push(`generated artifact ${artifactPath}`);
384
+ }
385
+ }
386
+ return lines;
264
387
  }
265
- function buildRetrofitSyncTypesScriptSource() {
266
- return `/* eslint-disable no-console */
267
- import path from 'node:path';
268
-
269
- import { syncBlockMetadata } from '@wp-typia/block-runtime/metadata-core';
270
-
271
- import { BLOCKS } from './block-config';
272
-
273
- function parseCliOptions( argv: string[] ) {
274
- const options = {
275
- check: false,
276
- };
277
-
278
- for ( const argument of argv ) {
279
- if ( argument === '--check' ) {
280
- options.check = true;
281
- continue;
282
- }
283
-
284
- throw new Error( \`Unknown sync-types flag: \${ argument }\` );
285
- }
286
-
287
- return options;
388
+ function buildNextSteps(options) {
389
+ const cliSpecifier = getWpTypiaCliSpecifier();
390
+ const syncTypesRun = formatRunScript(options.packageManager, "sync-types");
391
+ const syncRun = formatRunScript(options.packageManager, "sync");
392
+ const doctorRun = formatPackageExecCommand(options.packageManager, cliSpecifier, "doctor");
393
+ const migrationInitRun = formatPackageExecCommand(options.packageManager, cliSpecifier, "migrate init --current-migration-version v1");
394
+ const dependencyInstallCommand = formatAddDevDependenciesCommand(options.packageManager, buildRequiredDevDependencyMapEntries());
395
+ if (options.layoutKind === "unsupported") {
396
+ return [
397
+ "Align the project to one of the supported retrofit layouts listed below, then rerun `wp-typia init`.",
398
+ dependencyInstallCommand,
399
+ syncTypesRun,
400
+ doctorRun
401
+ ];
402
+ }
403
+ if (options.commandMode === "apply") {
404
+ return [
405
+ ...options.dependencyChangeCount > 0 ? [
406
+ "Install or reinstall project dependencies so the retrofit sync scripts and metadata generators are available locally.",
407
+ dependencyInstallCommand
408
+ ] : [],
409
+ syncRun,
410
+ doctorRun,
411
+ `Optional migration bootstrap: ${migrationInitRun}`
412
+ ];
413
+ }
414
+ const steps = [
415
+ ...options.hasPlannedChanges ? [
416
+ "Re-run `wp-typia init --apply` to write the planned package.json changes and helper files automatically.",
417
+ ...options.dependencyChangeCount > 0 ? [dependencyInstallCommand] : []
418
+ ] : [],
419
+ syncRun,
420
+ doctorRun,
421
+ `Optional migration bootstrap: ${migrationInitRun}`
422
+ ];
423
+ return steps;
288
424
  }
289
-
290
- async function main() {
291
- const options = parseCliOptions( process.argv.slice( 2 ) );
292
-
425
+ function buildRetrofitPlanSummary(options) {
426
+ if (options.status === "already-initialized") {
427
+ return options.commandMode === "apply" ? "This project already exposes the minimum wp-typia retrofit surface. No files were changed." : "This project already exposes the minimum wp-typia retrofit surface.";
428
+ }
429
+ if (options.commandMode === "apply") {
430
+ return "Applied the minimum wp-typia retrofit surface so package.json and helper scripts are ready for the next install and sync run.";
431
+ }
432
+ return "This command previews the minimum wp-typia adoption layer for the current project without rewriting it into a full scaffold.";
433
+ }
434
+ function createRetrofitPlan(options) {
435
+ const includeGeneratedArtifacts = options.commandMode === "preview-only";
436
+ const plannedChanges = buildChangeSummary({
437
+ generatedArtifacts: options.generatedArtifacts,
438
+ packageChanges: options.packageChanges,
439
+ plannedFiles: options.plannedFiles
440
+ }, {
441
+ includeGeneratedArtifacts
442
+ });
443
+ return {
444
+ blockTargets: options.blockTargets,
445
+ commandMode: options.commandMode,
446
+ detectedLayout: options.detectedLayout,
447
+ generatedArtifacts: options.generatedArtifacts,
448
+ nextSteps: options.nextSteps ?? buildNextSteps({
449
+ commandMode: options.commandMode,
450
+ dependencyChangeCount: options.packageChanges.addDevDependencies.length,
451
+ hasPlannedChanges: plannedChanges.length > 0,
452
+ layoutKind: options.detectedLayout.kind,
453
+ packageManager: options.packageManager
454
+ }),
455
+ notes: options.notes,
456
+ packageChanges: options.packageChanges,
457
+ plannedFiles: options.plannedFiles,
458
+ packageManager: options.packageManager,
459
+ projectDir: options.projectDir,
460
+ projectName: options.projectName,
461
+ status: options.status,
462
+ summary: buildRetrofitPlanSummary({
463
+ commandMode: options.commandMode,
464
+ status: options.status
465
+ })
466
+ };
467
+ }
468
+ function getInitPlan(projectDir, options = {}) {
469
+ const resolvedProjectDir = path2.resolve(projectDir);
470
+ const packageJson = readProjectPackageJson(resolvedProjectDir);
471
+ const packageManager = resolveInitPackageManager(resolvedProjectDir, packageJson, options.packageManager);
472
+ const workspace = tryResolveWorkspaceProject(resolvedProjectDir);
473
+ if (workspace) {
474
+ const workspacePackageJson = readProjectPackageJson(workspace.projectDir);
475
+ const workspacePackageManager = resolveInitPackageManager(workspace.projectDir, workspacePackageJson, options.packageManager);
476
+ const cliSpecifier = getWpTypiaCliSpecifier();
477
+ return createRetrofitPlan({
478
+ blockTargets: [],
479
+ commandMode: "preview-only",
480
+ detectedLayout: {
481
+ blockNames: [],
482
+ description: "Already an official wp-typia workspace.",
483
+ kind: "official-workspace"
484
+ },
485
+ generatedArtifacts: [],
486
+ nextSteps: [
487
+ "Use `wp-typia add <kind> <name>` to extend the official workspace instead of rerunning init.",
488
+ formatRunScript(workspacePackageManager, "sync"),
489
+ formatPackageExecCommand(workspacePackageManager, cliSpecifier, "doctor")
490
+ ],
491
+ notes: [
492
+ "The official workspace template already owns inventory, doctor, and add-command workflows."
493
+ ],
494
+ packageChanges: {
495
+ addDevDependencies: [],
496
+ scripts: []
497
+ },
498
+ packageManager: workspacePackageManager,
499
+ plannedFiles: [],
500
+ projectDir: workspace.projectDir,
501
+ projectName: workspace.packageName,
502
+ status: "already-initialized"
503
+ });
504
+ }
505
+ const projectName = typeof packageJson?.name === "string" && packageJson.name.length > 0 ? packageJson.name : path2.basename(resolvedProjectDir);
506
+ const layout = buildInitLayoutDetails(resolvedProjectDir);
507
+ const dependencyChanges = buildDependencyChanges(packageJson);
508
+ const scriptChanges = buildScriptChanges(packageJson, packageManager);
509
+ const packageManagerFieldChange = buildPackageManagerFieldChange(packageJson, packageManager, {
510
+ persistExplicitOverride: typeof options.packageManager === "string"
511
+ });
512
+ const rawPlannedFiles = layout.kind === "generated-project" || layout.kind === "official-workspace" ? [] : buildPlannedFiles(resolvedProjectDir, layout.kind);
513
+ const hasExistingSurface = hasExistingWpTypiaProjectSurface(resolvedProjectDir, packageJson);
514
+ const status = hasExistingSurface && dependencyChanges.length === 0 && scriptChanges.length === 0 && packageManagerFieldChange === undefined ? "already-initialized" : "preview";
515
+ const plannedFiles = status === "already-initialized" ? [] : rawPlannedFiles;
516
+ const detectedLayout = status === "already-initialized" && hasExistingSurface ? {
517
+ blockNames: layout.blockNames,
518
+ description: layout.kind === "unsupported" ? "Already exposes the minimum wp-typia sync surface." : `Already exposes the minimum wp-typia sync surface for ${layout.kind === "multi-block" ? "a multi-block project" : "a single-block project"}.`,
519
+ kind: "generated-project"
520
+ } : {
521
+ blockNames: layout.blockNames,
522
+ description: layout.description,
523
+ kind: layout.kind
524
+ };
525
+ return createRetrofitPlan({
526
+ blockTargets: layout.blockTargets,
527
+ commandMode: "preview-only",
528
+ detectedLayout,
529
+ generatedArtifacts: status === "already-initialized" && detectedLayout.kind === "generated-project" ? [] : layout.generatedArtifacts,
530
+ notes: Array.from(new Set([
531
+ "Preview only: `wp-typia init` does not write files yet.",
532
+ RETROFIT_APPLY_PREVIEW_NOTE,
533
+ ...layout.notes
534
+ ])),
535
+ packageChanges: {
536
+ addDevDependencies: dependencyChanges,
537
+ ...packageManagerFieldChange ? { packageManagerField: packageManagerFieldChange } : {},
538
+ scripts: scriptChanges
539
+ },
540
+ packageManager,
541
+ plannedFiles,
542
+ projectDir: resolvedProjectDir,
543
+ projectName,
544
+ status
545
+ });
546
+ }
547
+
548
+ // ../wp-typia-project-tools/src/runtime/cli-init-templates.ts
549
+ import path3 from "path";
550
+ function buildRetrofitBlockConfigEntry(target) {
551
+ return [
552
+ "\t{",
553
+ ` slug: ${quoteTsString(target.slug)},`,
554
+ ` attributeTypeName: ${quoteTsString(target.attributeTypeName)},`,
555
+ ` blockJsonFile: ${quoteTsString(target.blockJsonFile)},`,
556
+ ` manifestFile: ${quoteTsString(target.manifestFile)},`,
557
+ ` typesFile: ${quoteTsString(target.typesFile)},`,
558
+ "\t},"
559
+ ].join(`
560
+ `);
561
+ }
562
+ function buildRetrofitBlockConfigSource(targets) {
563
+ const blockEntries = targets.map(buildRetrofitBlockConfigEntry).join(`
564
+ `);
565
+ const baseSource = `export interface WorkspaceBlockConfig {
566
+ attributeTypeName: string;
567
+ apiTypesFile?: string;
568
+ blockJsonFile?: string;
569
+ manifestFile?: string;
570
+ openApiFile?: string;
571
+ restManifest?: ReturnType<
572
+ typeof import( '@wp-typia/block-runtime/metadata-core' ).defineEndpointManifest
573
+ >;
574
+ slug: string;
575
+ typesFile: string;
576
+ }
577
+
578
+ export const BLOCKS: WorkspaceBlockConfig[] = [
579
+ ${blockEntries}
580
+ ];
581
+ `;
582
+ return `${updateWorkspaceInventorySource(baseSource)}
583
+ `;
584
+ }
585
+ function buildRetrofitSyncTypesScriptSource() {
586
+ return `/* eslint-disable no-console */
587
+ import path from 'node:path';
588
+
589
+ import { syncBlockMetadata } from '@wp-typia/block-runtime/metadata-core';
590
+
591
+ import { BLOCKS } from './block-config';
592
+
593
+ function parseCliOptions( argv: string[] ) {
594
+ const options = {
595
+ check: false,
596
+ };
597
+
598
+ for ( const argument of argv ) {
599
+ if ( argument === '--check' ) {
600
+ options.check = true;
601
+ continue;
602
+ }
603
+
604
+ throw new Error( \`Unknown sync-types flag: \${ argument }\` );
605
+ }
606
+
607
+ return options;
608
+ }
609
+
610
+ async function main() {
611
+ const options = parseCliOptions( process.argv.slice( 2 ) );
612
+
293
613
  if ( BLOCKS.length === 0 ) {
294
614
  console.log(
295
615
  options.check
@@ -442,241 +762,18 @@ main().catch( ( error ) => {
442
762
  } );
443
763
  `;
444
764
  }
445
- function buildLayoutDetails(projectDir) {
446
- try {
447
- const discoveredLayout = discoverMigrationInitLayout(projectDir);
448
- const discoveredBlocks = discoveredLayout.mode === "multi" ? discoveredLayout.blocks : [discoveredLayout.block];
449
- let blockTargets;
450
- try {
451
- blockTargets = discoveredBlocks.map((block) => buildRetrofitBlockTarget(projectDir, block));
452
- } catch (error) {
453
- const message = error instanceof Error ? error.message : String(error);
454
- return {
455
- blockNames: discoveredBlocks.map((block) => block.blockName),
456
- blockTargets: [],
457
- description: "Detected supported block files, but could not infer retrofit block-config metadata automatically yet.",
458
- generatedArtifacts: [],
459
- kind: "unsupported",
460
- notes: [message, SUPPORTED_RETROFIT_LAYOUT_NOTE]
461
- };
462
- }
463
- if (discoveredLayout.mode === "multi") {
464
- return {
465
- blockNames: discoveredBlocks.map((block) => block.blockName),
466
- blockTargets,
467
- description: `Detected a supported multi-block retrofit candidate (${discoveredBlocks.length} targets).`,
468
- generatedArtifacts: discoveredBlocks.flatMap((block) => buildGeneratedArtifactPaths(block.blockJsonFile, block.manifestFile)),
469
- kind: "multi-block",
470
- notes: [
471
- "Migration bootstrap can stay optional. Add it later with `wp-typia migrate init --current-migration-version v1` once the typed sync surface is in place."
472
- ]
473
- };
474
- }
475
- return {
476
- blockNames: [discoveredLayout.block.blockName],
477
- blockTargets,
478
- description: "Detected a supported single-block retrofit candidate.",
479
- generatedArtifacts: buildGeneratedArtifactPaths(discoveredLayout.block.blockJsonFile, discoveredLayout.block.manifestFile),
480
- kind: "single-block",
481
- notes: discoveredLayout.block.blockJsonFile === "block.json" ? [
482
- "Legacy root `block.json` layouts are still supported for retrofit planning, but newer scaffolds keep generated block metadata under `src/`."
483
- ] : []
484
- };
485
- } catch (error) {
486
- const message = error instanceof Error ? error.message : String(error);
487
- return {
488
- blockNames: [],
489
- blockTargets: [],
490
- description: "No supported retrofit layout was auto-detected yet.",
491
- generatedArtifacts: [],
492
- kind: "unsupported",
493
- notes: [message, SUPPORTED_RETROFIT_LAYOUT_NOTE]
494
- };
495
- }
496
- }
497
- function hasExistingWpTypiaProjectSurface(projectDir, packageJson) {
498
- const scripts = packageJson?.scripts ?? {};
499
- const hasSyncSurface = typeof scripts.sync === "string" || typeof scripts["sync-types"] === "string";
500
- const hasHelperFiles = [
501
- path.join("scripts", "block-config.ts"),
502
- path.join("scripts", "sync-project.ts"),
503
- path.join("scripts", "sync-types-to-block-json.ts")
504
- ].every((relativePath) => fs.existsSync(path.join(projectDir, relativePath)));
505
- const hasRuntimeDeps = typeof getExistingDependencyVersion(packageJson, "@wp-typia/block-runtime") === "string" && typeof getExistingDependencyVersion(packageJson, "@wp-typia/block-types") === "string";
506
- return hasSyncSurface && hasHelperFiles && hasRuntimeDeps;
507
- }
508
- function buildPlannedFiles(projectDir, layoutKind) {
509
- if (layoutKind === "unsupported") {
510
- return [];
511
- }
512
- return [
513
- {
514
- action: fs.existsSync(path.join(projectDir, "scripts", "block-config.ts")) ? "update" : "add",
515
- path: "scripts/block-config.ts",
516
- purpose: "Declare the current retrofit block targets so sync-types can regenerate metadata from the existing TypeScript source of truth."
517
- },
518
- {
519
- action: fs.existsSync(path.join(projectDir, "scripts", "sync-types-to-block-json.ts")) ? "update" : "add",
520
- path: "scripts/sync-types-to-block-json.ts",
521
- purpose: "Generate block.json and Typia metadata artifacts from the current TypeScript source of truth."
522
- },
523
- {
524
- action: fs.existsSync(path.join(projectDir, "scripts", "sync-project.ts")) ? "update" : "add",
525
- path: "scripts/sync-project.ts",
526
- purpose: "Provide one shared sync entrypoint that can grow into sync-rest or workspace-aware refresh steps later."
527
- }
528
- ];
529
- }
530
- function buildChangeSummary(changes, options) {
531
- const lines = [];
532
- for (const dependencyChange of changes.packageChanges.addDevDependencies) {
533
- lines.push(`devDependency ${dependencyChange.action} ${dependencyChange.name} -> ${dependencyChange.requiredValue}`);
534
- }
535
- if (changes.packageChanges.packageManagerField) {
536
- lines.push(`packageManager ${changes.packageChanges.packageManagerField.action} -> ${changes.packageChanges.packageManagerField.requiredValue}`);
537
- }
538
- for (const scriptChange of changes.packageChanges.scripts) {
539
- lines.push(`script ${scriptChange.action} ${scriptChange.name} -> ${scriptChange.requiredValue}`);
540
- }
541
- for (const filePlan of changes.plannedFiles) {
542
- lines.push(`file ${filePlan.action} ${filePlan.path} (${filePlan.purpose})`);
543
- }
544
- if (options.includeGeneratedArtifacts) {
545
- for (const artifactPath of changes.generatedArtifacts) {
546
- lines.push(`generated artifact ${artifactPath}`);
547
- }
548
- }
549
- return lines;
550
- }
551
- function buildNextSteps(options) {
552
- const cliSpecifier = getWpTypiaCliSpecifier();
553
- const syncTypesRun = formatRunScript(options.packageManager, "sync-types");
554
- const syncRun = formatRunScript(options.packageManager, "sync");
555
- const doctorRun = formatPackageExecCommand(options.packageManager, cliSpecifier, "doctor");
556
- const migrationInitRun = formatPackageExecCommand(options.packageManager, cliSpecifier, "migrate init --current-migration-version v1");
557
- const dependencyInstallCommand = formatAddDevDependenciesCommand(options.packageManager, buildRequiredDevDependencyMapEntries());
558
- if (options.layoutKind === "unsupported") {
559
- return [
560
- "Align the project to one of the supported retrofit layouts listed below, then rerun `wp-typia init`.",
561
- dependencyInstallCommand,
562
- syncTypesRun,
563
- doctorRun
564
- ];
565
- }
566
- if (options.commandMode === "apply") {
567
- return [
568
- ...options.dependencyChangeCount > 0 ? [
569
- "Install or reinstall project dependencies so the retrofit sync scripts and metadata generators are available locally.",
570
- dependencyInstallCommand
571
- ] : [],
572
- syncRun,
573
- doctorRun,
574
- `Optional migration bootstrap: ${migrationInitRun}`
575
- ];
576
- }
577
- const steps = [
578
- ...options.hasPlannedChanges ? [
579
- "Re-run `wp-typia init --apply` to write the planned package.json changes and helper files automatically.",
580
- ...options.dependencyChangeCount > 0 ? [dependencyInstallCommand] : []
581
- ] : [],
582
- syncRun,
583
- doctorRun,
584
- `Optional migration bootstrap: ${migrationInitRun}`
585
- ];
586
- return steps;
587
- }
588
- function buildRequiredDevDependencyMapEntries() {
589
- return Object.entries(buildRequiredDevDependencyMap()).map(([name, version]) => `${name}@${version.replace(/^workspace:/u, "")}`);
590
- }
591
- function buildRetrofitPlanSummary(options) {
592
- if (options.status === "already-initialized") {
593
- return options.commandMode === "apply" ? "This project already exposes the minimum wp-typia retrofit surface. No files were changed." : "This project already exposes the minimum wp-typia retrofit surface.";
594
- }
595
- if (options.commandMode === "apply") {
596
- return "Applied the minimum wp-typia retrofit surface so package.json and helper scripts are ready for the next install and sync run.";
597
- }
598
- return "This command previews the minimum wp-typia adoption layer for the current project without rewriting it into a full scaffold.";
599
- }
600
- function createRetrofitPlan(options) {
601
- const includeGeneratedArtifacts = options.commandMode === "preview-only";
602
- const plannedChanges = buildChangeSummary({
603
- generatedArtifacts: options.generatedArtifacts,
604
- packageChanges: options.packageChanges,
605
- plannedFiles: options.plannedFiles
606
- }, {
607
- includeGeneratedArtifacts
608
- });
609
- return {
610
- blockTargets: options.blockTargets,
611
- commandMode: options.commandMode,
612
- detectedLayout: options.detectedLayout,
613
- generatedArtifacts: options.generatedArtifacts,
614
- nextSteps: options.nextSteps ?? buildNextSteps({
615
- commandMode: options.commandMode,
616
- dependencyChangeCount: options.packageChanges.addDevDependencies.length,
617
- hasPlannedChanges: plannedChanges.length > 0,
618
- layoutKind: options.detectedLayout.kind,
619
- packageManager: options.packageManager
620
- }),
621
- notes: options.notes,
622
- packageChanges: options.packageChanges,
623
- plannedFiles: options.plannedFiles,
624
- packageManager: options.packageManager,
625
- projectDir: options.projectDir,
626
- projectName: options.projectName,
627
- status: options.status,
628
- summary: buildRetrofitPlanSummary({
629
- commandMode: options.commandMode,
630
- status: options.status
631
- })
632
- };
633
- }
634
- function setDependencyVersion(packageJson, name, requiredValue) {
635
- if (packageJson.devDependencies?.[name] !== undefined) {
636
- packageJson.devDependencies[name] = requiredValue;
637
- return;
638
- }
639
- if (packageJson.dependencies?.[name] !== undefined) {
640
- packageJson.dependencies[name] = requiredValue;
641
- return;
642
- }
643
- packageJson.devDependencies ??= {};
644
- packageJson.devDependencies[name] = requiredValue;
645
- }
646
- function buildNextProjectPackageJson(options) {
647
- const nextPackageJson = options.packageJson ? JSON.parse(JSON.stringify(options.packageJson)) : {
648
- name: options.projectName,
649
- private: true
650
- };
651
- nextPackageJson.devDependencies ??= {};
652
- nextPackageJson.scripts ??= {};
653
- for (const dependencyChange of options.packageChanges.addDevDependencies) {
654
- setDependencyVersion(nextPackageJson, dependencyChange.name, dependencyChange.requiredValue);
655
- }
656
- if (options.packageChanges.packageManagerField) {
657
- nextPackageJson.packageManager = options.packageChanges.packageManagerField.requiredValue;
658
- } else if (!nextPackageJson.packageManager && options.packageManager !== "npm") {
659
- nextPackageJson.packageManager = getPackageManager(options.packageManager).packageManagerField;
660
- }
661
- for (const scriptChange of options.packageChanges.scripts) {
662
- nextPackageJson.scripts[scriptChange.name] = scriptChange.requiredValue;
663
- }
664
- return nextPackageJson;
665
- }
666
- function buildProjectPackageJsonSource(packageJson) {
667
- return `${JSON.stringify(packageJson, null, 2)}
668
- `;
669
- }
670
765
  function buildRetrofitHelperFiles(blockTargets) {
671
766
  return {
672
- [path.join("scripts", "block-config.ts")]: buildRetrofitBlockConfigSource(blockTargets),
673
- [path.join("scripts", "sync-project.ts")]: buildRetrofitSyncProjectScriptSource(),
674
- [path.join("scripts", "sync-types-to-block-json.ts")]: buildRetrofitSyncTypesScriptSource()
767
+ [path3.join("scripts", "block-config.ts")]: buildRetrofitBlockConfigSource(blockTargets),
768
+ [path3.join("scripts", "sync-project.ts")]: buildRetrofitSyncProjectScriptSource(),
769
+ [path3.join("scripts", "sync-types-to-block-json.ts")]: buildRetrofitSyncTypesScriptSource()
675
770
  };
676
771
  }
772
+
773
+ // ../wp-typia-project-tools/src/runtime/cli-init-apply.ts
677
774
  async function createRetrofitMutationSnapshot(projectDir, filePaths) {
678
- const scriptsDir = path.join(projectDir, "scripts");
679
- const scriptsDirExisted = fs.existsSync(scriptsDir);
775
+ const scriptsDir = path4.join(projectDir, "scripts");
776
+ const scriptsDirExisted = fs3.existsSync(scriptsDir);
680
777
  const fileSources = await snapshotWorkspaceFiles(filePaths);
681
778
  const targetPaths = fileSources.filter((entry) => entry.source === null).map((entry) => entry.filePath);
682
779
  if (!scriptsDirExisted) {
@@ -690,95 +787,22 @@ async function createRetrofitMutationSnapshot(projectDir, filePaths) {
690
787
  }
691
788
  async function writeRetrofitFiles(options) {
692
789
  const helperFiles = buildRetrofitHelperFiles(options.blockTargets);
693
- const scriptsDir = path.join(options.projectDir, "scripts");
790
+ const scriptsDir = path4.join(options.projectDir, "scripts");
694
791
  await fsp.mkdir(scriptsDir, { recursive: true });
695
- await fsp.writeFile(path.join(options.projectDir, "package.json"), buildProjectPackageJsonSource(options.packageJson), "utf8");
792
+ await fsp.writeFile(path4.join(options.projectDir, "package.json"), buildProjectPackageJsonSource(options.packageJson), "utf8");
696
793
  for (const [relativePath, source] of Object.entries(helperFiles)) {
697
- await fsp.writeFile(path.join(options.projectDir, relativePath), source, "utf8");
794
+ await fsp.writeFile(path4.join(options.projectDir, relativePath), source, "utf8");
698
795
  }
699
796
  }
700
797
  function buildApplyFailureError(error) {
701
798
  const message = error instanceof Error ? error.message : String(error);
702
799
  return createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.INVALID_ARGUMENT, `Unable to apply the retrofit init plan safely. The command restored the previous package.json/helper-file snapshot. ${message}`, error instanceof Error ? { cause: error } : undefined);
703
800
  }
704
- function getInitPlan(projectDir, options = {}) {
705
- const resolvedProjectDir = path.resolve(projectDir);
706
- const packageJson = readProjectPackageJson(resolvedProjectDir);
707
- const packageManager = resolveInitPackageManager(resolvedProjectDir, packageJson, options.packageManager);
708
- const workspace = tryResolveWorkspaceProject(resolvedProjectDir);
709
- if (workspace) {
710
- const workspacePackageJson = readProjectPackageJson(workspace.projectDir);
711
- const workspacePackageManager = resolveInitPackageManager(workspace.projectDir, workspacePackageJson, options.packageManager);
712
- const cliSpecifier = getWpTypiaCliSpecifier();
713
- return createRetrofitPlan({
714
- blockTargets: [],
715
- commandMode: "preview-only",
716
- detectedLayout: {
717
- blockNames: [],
718
- description: "Already an official wp-typia workspace.",
719
- kind: "official-workspace"
720
- },
721
- generatedArtifacts: [],
722
- nextSteps: [
723
- "Use `wp-typia add <kind> <name>` to extend the official workspace instead of rerunning init.",
724
- formatRunScript(workspacePackageManager, "sync"),
725
- formatPackageExecCommand(workspacePackageManager, cliSpecifier, "doctor")
726
- ],
727
- notes: [
728
- "The official workspace template already owns inventory, doctor, and add-command workflows."
729
- ],
730
- packageChanges: {
731
- addDevDependencies: [],
732
- scripts: []
733
- },
734
- packageManager: workspacePackageManager,
735
- plannedFiles: [],
736
- projectDir: workspace.projectDir,
737
- projectName: workspace.packageName,
738
- status: "already-initialized"
739
- });
740
- }
741
- const projectName = typeof packageJson?.name === "string" && packageJson.name.length > 0 ? packageJson.name : path.basename(resolvedProjectDir);
742
- const layout = buildLayoutDetails(resolvedProjectDir);
743
- const dependencyChanges = buildDependencyChanges(packageJson);
744
- const scriptChanges = buildScriptChanges(packageJson, packageManager);
745
- const packageManagerFieldChange = buildPackageManagerFieldChange(packageJson, packageManager, {
746
- persistExplicitOverride: typeof options.packageManager === "string"
747
- });
748
- const rawPlannedFiles = layout.kind === "generated-project" || layout.kind === "official-workspace" ? [] : buildPlannedFiles(resolvedProjectDir, layout.kind);
749
- const hasExistingSurface = hasExistingWpTypiaProjectSurface(resolvedProjectDir, packageJson);
750
- const status = hasExistingSurface && dependencyChanges.length === 0 && scriptChanges.length === 0 && packageManagerFieldChange === undefined ? "already-initialized" : "preview";
751
- const plannedFiles = status === "already-initialized" ? [] : rawPlannedFiles;
752
- const detectedLayout = status === "already-initialized" && hasExistingSurface ? {
753
- blockNames: layout.blockNames,
754
- description: layout.kind === "unsupported" ? "Already exposes the minimum wp-typia sync surface." : `Already exposes the minimum wp-typia sync surface for ${layout.kind === "multi-block" ? "a multi-block project" : "a single-block project"}.`,
755
- kind: "generated-project"
756
- } : {
757
- blockNames: layout.blockNames,
758
- description: layout.description,
759
- kind: layout.kind
760
- };
761
- return createRetrofitPlan({
762
- blockTargets: layout.blockTargets,
763
- commandMode: "preview-only",
764
- detectedLayout,
765
- generatedArtifacts: status === "already-initialized" && detectedLayout.kind === "generated-project" ? [] : layout.generatedArtifacts,
766
- notes: Array.from(new Set([
767
- "Preview only: `wp-typia init` does not write files yet.",
768
- RETROFIT_APPLY_PREVIEW_NOTE,
769
- ...layout.notes
770
- ])),
771
- packageChanges: {
772
- addDevDependencies: dependencyChanges,
773
- ...packageManagerFieldChange ? { packageManagerField: packageManagerFieldChange } : {},
774
- scripts: scriptChanges
775
- },
776
- packageManager,
777
- plannedFiles,
778
- projectDir: resolvedProjectDir,
779
- projectName,
780
- status
781
- });
801
+ function toApplyNotes(previewNotes) {
802
+ return Array.from(new Set([
803
+ ...previewNotes.filter((note) => note !== "Preview only: `wp-typia init` does not write files yet." && note !== RETROFIT_APPLY_PREVIEW_NOTE),
804
+ RETROFIT_ROLLBACK_NOTE
805
+ ]));
782
806
  }
783
807
  async function applyInitPlan(projectDir, options = {}) {
784
808
  const previewPlan = getInitPlan(projectDir, options);
@@ -789,10 +813,7 @@ async function applyInitPlan(projectDir, options = {}) {
789
813
  return createRetrofitPlan({
790
814
  ...previewPlan,
791
815
  commandMode: "apply",
792
- notes: Array.from(new Set([
793
- ...previewPlan.notes.filter((note) => note !== "Preview only: `wp-typia init` does not write files yet." && note !== RETROFIT_APPLY_PREVIEW_NOTE),
794
- RETROFIT_ROLLBACK_NOTE
795
- ])),
816
+ notes: toApplyNotes(previewPlan.notes),
796
817
  status: "already-initialized"
797
818
  });
798
819
  }
@@ -804,8 +825,8 @@ async function applyInitPlan(projectDir, options = {}) {
804
825
  });
805
826
  const helperFiles = buildRetrofitHelperFiles(previewPlan.blockTargets);
806
827
  const filePaths = [
807
- path.join(previewPlan.projectDir, "package.json"),
808
- ...Object.keys(helperFiles).map((relativePath) => path.join(previewPlan.projectDir, relativePath))
828
+ path4.join(previewPlan.projectDir, "package.json"),
829
+ ...Object.keys(helperFiles).map((relativePath) => path4.join(previewPlan.projectDir, relativePath))
809
830
  ];
810
831
  const mutationSnapshot = await createRetrofitMutationSnapshot(previewPlan.projectDir, filePaths);
811
832
  try {
@@ -821,13 +842,12 @@ async function applyInitPlan(projectDir, options = {}) {
821
842
  return createRetrofitPlan({
822
843
  ...previewPlan,
823
844
  commandMode: "apply",
824
- notes: Array.from(new Set([
825
- ...previewPlan.notes.filter((note) => note !== "Preview only: `wp-typia init` does not write files yet." && note !== RETROFIT_APPLY_PREVIEW_NOTE),
826
- RETROFIT_ROLLBACK_NOTE
827
- ])),
845
+ notes: toApplyNotes(previewPlan.notes),
828
846
  status: "applied"
829
847
  });
830
848
  }
849
+
850
+ // ../wp-typia-project-tools/src/runtime/cli-init.ts
831
851
  async function runInitCommand(options) {
832
852
  return options.apply ? applyInitPlan(options.projectDir, {
833
853
  packageManager: options.packageManager
@@ -841,4 +861,4 @@ export {
841
861
  applyInitPlan
842
862
  };
843
863
 
844
- //# debugId=E7F537E7B32AAF5464756E2164756E21
864
+ //# debugId=9445E5AD8C9D8E6964756E2164756E21