@wp-typia/project-tools 0.22.5 → 0.22.7

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 (69) hide show
  1. package/dist/runtime/ai-feature-capability.js +20 -0
  2. package/dist/runtime/cli-add-block-json.d.ts +2 -2
  3. package/dist/runtime/cli-add-block-json.js +5 -4
  4. package/dist/runtime/cli-add-block.js +16 -11
  5. package/dist/runtime/cli-add-collision.js +213 -136
  6. package/dist/runtime/cli-add-help.js +1 -1
  7. package/dist/runtime/cli-add-kind-ids.d.ts +11 -0
  8. package/dist/runtime/cli-add-kind-ids.js +20 -0
  9. package/dist/runtime/cli-add-types.d.ts +2 -8
  10. package/dist/runtime/cli-add-types.js +1 -17
  11. package/dist/runtime/cli-add-workspace-ability-scaffold.d.ts +3 -1
  12. package/dist/runtime/cli-add-workspace-ability-scaffold.js +22 -5
  13. package/dist/runtime/cli-add-workspace-ability.d.ts +4 -0
  14. package/dist/runtime/cli-add-workspace-ability.js +5 -1
  15. package/dist/runtime/cli-add-workspace-admin-view-source.d.ts +7 -0
  16. package/dist/runtime/cli-add-workspace-admin-view-source.js +9 -10
  17. package/dist/runtime/cli-add-workspace-admin-view-types.d.ts +0 -2
  18. package/dist/runtime/cli-add-workspace-admin-view-types.js +0 -3
  19. package/dist/runtime/cli-add-workspace-ai-scaffold.js +14 -6
  20. package/dist/runtime/cli-add-workspace.js +8 -11
  21. package/dist/runtime/cli-doctor-workspace-bindings.js +2 -3
  22. package/dist/runtime/cli-doctor-workspace-blocks.js +2 -3
  23. package/dist/runtime/cli-doctor-workspace-features.js +6 -11
  24. package/dist/runtime/cli-doctor-workspace-shared.d.ts +8 -0
  25. package/dist/runtime/cli-doctor-workspace-shared.js +10 -0
  26. package/dist/runtime/cli-help.js +1 -1
  27. package/dist/runtime/cli-init-apply.d.ts +15 -0
  28. package/dist/runtime/cli-init-apply.js +99 -0
  29. package/dist/runtime/cli-init-package-json.d.ts +19 -0
  30. package/dist/runtime/cli-init-package-json.js +191 -0
  31. package/dist/runtime/cli-init-plan-presentation.d.ts +16 -0
  32. package/dist/runtime/cli-init-plan-presentation.js +74 -0
  33. package/dist/runtime/cli-init-plan.d.ts +39 -0
  34. package/dist/runtime/cli-init-plan.js +303 -0
  35. package/dist/runtime/cli-init-templates.d.ts +27 -0
  36. package/dist/runtime/cli-init-templates.js +244 -0
  37. package/dist/runtime/cli-init-types.d.ts +84 -0
  38. package/dist/runtime/cli-init-types.js +3 -0
  39. package/dist/runtime/cli-init.d.ts +4 -100
  40. package/dist/runtime/cli-init.js +6 -878
  41. package/dist/runtime/fs-async.d.ts +28 -0
  42. package/dist/runtime/fs-async.js +53 -0
  43. package/dist/runtime/package-managers.js +1 -1
  44. package/dist/runtime/package-versions.d.ts +1 -1
  45. package/dist/runtime/package-versions.js +1 -1
  46. package/dist/runtime/php-utils.d.ts +16 -0
  47. package/dist/runtime/php-utils.js +258 -1
  48. package/dist/runtime/scaffold-apply-utils.js +10 -20
  49. package/dist/runtime/scaffold-bootstrap.js +6 -8
  50. package/dist/runtime/scaffold-compatibility.d.ts +15 -3
  51. package/dist/runtime/scaffold-compatibility.js +42 -11
  52. package/dist/runtime/scaffold-documents.js +12 -0
  53. package/dist/runtime/scaffold-package-manager-files.js +4 -3
  54. package/dist/runtime/string-case.d.ts +5 -0
  55. package/dist/runtime/string-case.js +54 -2
  56. package/dist/runtime/template-source-cache.d.ts +19 -0
  57. package/dist/runtime/template-source-cache.js +164 -28
  58. package/dist/runtime/template-source-external.d.ts +7 -0
  59. package/dist/runtime/template-source-external.js +22 -5
  60. package/dist/runtime/template-source-normalization.d.ts +1 -1
  61. package/dist/runtime/template-source-normalization.js +12 -12
  62. package/dist/runtime/template-source-remote.d.ts +14 -0
  63. package/dist/runtime/template-source-remote.js +91 -15
  64. package/dist/runtime/template-source.js +35 -25
  65. package/dist/runtime/typia-llm.js +7 -0
  66. package/dist/runtime/version-floor.js +8 -2
  67. package/dist/runtime/workspace-inventory.d.ts +16 -14
  68. package/dist/runtime/workspace-inventory.js +58 -14
  69. package/package.json +6 -1
@@ -10,7 +10,7 @@ import { getWorkspaceBootstrapPath, patchFile, } from "./cli-add-shared.js";
10
10
  import { appendPhpSnippetBeforeClosingTag, executeWorkspaceMutationPlan, insertPhpSnippetBeforeWorkspaceAnchors, } from "./cli-add-workspace-mutation.js";
11
11
  import { updatePluginHeaderCompatibility, } from "./scaffold-compatibility.js";
12
12
  import { DEFAULT_WORDPRESS_ABILITIES_VERSION, DEFAULT_WORDPRESS_CORE_ABILITIES_VERSION, } from "./package-versions.js";
13
- import { escapeRegex, findPhpFunctionRange, hasPhpFunctionDefinition, replacePhpFunctionDefinition, } from "./php-utils.js";
13
+ import { escapeRegex, findPhpFunctionRange, hasPhpFunctionCall, hasPhpFunctionDefinition, replacePhpFunctionDefinition, } from "./php-utils.js";
14
14
  import { toPascalCase } from "./string-case.js";
15
15
  function resolveManagedDependencyVersion(existingVersion, requiredVersion) {
16
16
  if (!existingVersion) {
@@ -130,9 +130,18 @@ function ${enqueueFunctionName}() {
130
130
  if (!hasPhpFunctionDefinition(nextSource, enqueueFunctionName)) {
131
131
  nextSource = insertPhpSnippetBeforeWorkspaceAnchors(nextSource, enqueueFunction);
132
132
  }
133
- else if (!findPhpFunctionRange(nextSource, enqueueFunctionName)?.source.includes("wp_enqueue_script_module")) {
134
- nextSource =
135
- replacePhpFunctionDefinition(nextSource, enqueueFunctionName, enqueueFunction, { trimReplacementStart: true }) ?? nextSource;
133
+ else {
134
+ const functionRange = findPhpFunctionRange(nextSource, enqueueFunctionName);
135
+ const functionSource = functionRange
136
+ ? nextSource.slice(functionRange.start, functionRange.end)
137
+ : "";
138
+ if (!hasPhpFunctionCall(functionSource, "wp_enqueue_script_module")) {
139
+ const replacedSource = replacePhpFunctionDefinition(nextSource, enqueueFunctionName, enqueueFunction, { trimReplacementStart: true });
140
+ if (!replacedSource) {
141
+ throw new Error(`Unable to repair ${path.basename(bootstrapPath)} for ${enqueueFunctionName}.`);
142
+ }
143
+ nextSource = replacedSource;
144
+ }
136
145
  }
137
146
  if (!nextSource.includes(loadHook)) {
138
147
  nextSource = appendPhpSnippetBeforeClosingTag(nextSource, loadHook);
@@ -301,6 +310,7 @@ async function ensureAbilityWebpackAnchors(workspace) {
301
310
  * Write generated workflow ability sources and patch shared workspace anchors.
302
311
  */
303
312
  export async function scaffoldAbilityWorkspace({ abilitySlug, compatibilityPolicy, workspace, }) {
313
+ const compatibilityWarnings = [];
304
314
  const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
305
315
  const bootstrapPath = getWorkspaceBootstrapPath(workspace);
306
316
  const buildScriptPath = path.join(workspace.projectDir, "scripts", "build-workspace.mjs");
@@ -331,7 +341,11 @@ export async function scaffoldAbilityWorkspace({ abilitySlug, compatibilityPolic
331
341
  await fsp.mkdir(abilityDir, { recursive: true });
332
342
  await fsp.mkdir(path.dirname(phpFilePath), { recursive: true });
333
343
  await ensureAbilityBootstrapAnchors(workspace);
334
- await patchFile(bootstrapPath, (source) => updatePluginHeaderCompatibility(source, compatibilityPolicy));
344
+ await patchFile(bootstrapPath, (source) => updatePluginHeaderCompatibility(source, compatibilityPolicy, {
345
+ onWarning: (warning) => {
346
+ compatibilityWarnings.push(warning);
347
+ },
348
+ }));
335
349
  await ensureAbilityPackageScripts(workspace);
336
350
  await ensureAbilitySyncProjectAnchors(workspace);
337
351
  await ensureAbilityBuildScriptAnchors(workspace);
@@ -363,4 +377,7 @@ export async function scaffoldAbilityWorkspace({ abilitySlug, compatibilityPolic
363
377
  });
364
378
  },
365
379
  });
380
+ return {
381
+ warnings: compatibilityWarnings,
382
+ };
366
383
  }
@@ -1,8 +1,12 @@
1
1
  import { type RunAddAbilityCommandOptions } from "./cli-add-shared.js";
2
2
  /**
3
3
  * Add one typed workflow ability scaffold to an official workspace project.
4
+ *
5
+ * @returns Generated ability metadata plus non-fatal scaffold warnings, such
6
+ * as repaired malformed existing plugin header floor values.
4
7
  */
5
8
  export declare function runAddAbilityCommand({ abilityName, cwd, }: RunAddAbilityCommandOptions): Promise<{
6
9
  abilitySlug: string;
7
10
  projectDir: string;
11
+ warnings: string[];
8
12
  }>;
@@ -5,6 +5,9 @@ import { resolveWorkspaceProject } from "./workspace-project.js";
5
5
  import { REQUIRED_WORKSPACE_ABILITY_COMPATIBILITY, resolveScaffoldCompatibilityPolicy, } from "./scaffold-compatibility.js";
6
6
  /**
7
7
  * Add one typed workflow ability scaffold to an official workspace project.
8
+ *
9
+ * @returns Generated ability metadata plus non-fatal scaffold warnings, such
10
+ * as repaired malformed existing plugin header floor values.
8
11
  */
9
12
  export async function runAddAbilityCommand({ abilityName, cwd = process.cwd(), }) {
10
13
  const workspace = resolveWorkspaceProject(cwd);
@@ -12,7 +15,7 @@ export async function runAddAbilityCommand({ abilityName, cwd = process.cwd(), }
12
15
  const inventory = readWorkspaceInventory(workspace.projectDir);
13
16
  assertAbilityDoesNotExist(workspace.projectDir, abilitySlug, inventory);
14
17
  const compatibilityPolicy = resolveScaffoldCompatibilityPolicy(REQUIRED_WORKSPACE_ABILITY_COMPATIBILITY);
15
- await scaffoldAbilityWorkspace({
18
+ const scaffoldResult = await scaffoldAbilityWorkspace({
16
19
  abilitySlug,
17
20
  compatibilityPolicy,
18
21
  workspace,
@@ -20,5 +23,6 @@ export async function runAddAbilityCommand({ abilityName, cwd = process.cwd(), }
20
23
  return {
21
24
  abilitySlug,
22
25
  projectDir: workspace.projectDir,
26
+ warnings: scaffoldResult.warnings,
23
27
  };
24
28
  }
@@ -1,4 +1,11 @@
1
1
  import { type AdminViewRestResource, type AdminViewSource } from './cli-add-workspace-admin-view-types.js';
2
+ /**
3
+ * Assert that admin-view package dependencies are available before file writes.
4
+ *
5
+ * This is a no-op while `@wp-typia/dataviews` is publicly available on npm.
6
+ * The exported seam is retained so future availability checks can stay
7
+ * centralized ahead of scaffold mutations.
8
+ */
2
9
  export declare function assertAdminViewPackageAvailability(): void;
3
10
  export declare function parseAdminViewSource(source?: string): AdminViewSource | undefined;
4
11
  export declare function resolveRestResourceSource(restResources: AdminViewRestResource[], source: AdminViewSource | undefined): AdminViewRestResource | undefined;
@@ -1,15 +1,14 @@
1
1
  import { assertValidGeneratedSlug } from './cli-add-shared.js';
2
- import { CLI_DIAGNOSTIC_CODES, createCliDiagnosticCodeError, } from './cli-diagnostics.js';
3
- import { ADMIN_VIEW_ALLOW_UNPUBLISHED_DATAVIEWS_ENV, ADMIN_VIEW_CORE_DATA_ENTITY_KIND_IDS, ADMIN_VIEW_CORE_DATA_ENTITY_NAME_PATTERN, ADMIN_VIEW_CORE_DATA_ENTITY_SEGMENT_PATTERN, ADMIN_VIEW_CORE_DATA_SOURCE_KIND, ADMIN_VIEW_PUBLIC_INSTALLS_ENABLED, ADMIN_VIEW_REST_SOURCE_KIND, ADMIN_VIEW_SOURCE_USAGE, isAdminViewCoreDataSource, isAdminViewRestResourceSource, } from './cli-add-workspace-admin-view-types.js';
4
- function isAdminViewUnpublishedDataViewsOverrideEnabled() {
5
- return (process.env[ADMIN_VIEW_ALLOW_UNPUBLISHED_DATAVIEWS_ENV]?.trim() === '1');
6
- }
2
+ import { ADMIN_VIEW_CORE_DATA_ENTITY_KIND_IDS, ADMIN_VIEW_CORE_DATA_ENTITY_NAME_PATTERN, ADMIN_VIEW_CORE_DATA_ENTITY_SEGMENT_PATTERN, ADMIN_VIEW_CORE_DATA_SOURCE_KIND, ADMIN_VIEW_REST_SOURCE_KIND, ADMIN_VIEW_SOURCE_USAGE, isAdminViewCoreDataSource, isAdminViewRestResourceSource, } from './cli-add-workspace-admin-view-types.js';
3
+ /**
4
+ * Assert that admin-view package dependencies are available before file writes.
5
+ *
6
+ * This is a no-op while `@wp-typia/dataviews` is publicly available on npm.
7
+ * The exported seam is retained so future availability checks can stay
8
+ * centralized ahead of scaffold mutations.
9
+ */
7
10
  export function assertAdminViewPackageAvailability() {
8
- if (isAdminViewUnpublishedDataViewsOverrideEnabled() ||
9
- ADMIN_VIEW_PUBLIC_INSTALLS_ENABLED) {
10
- return;
11
- }
12
- throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.INVALID_ARGUMENT, '`wp-typia add admin-view` is temporarily unavailable because `@wp-typia/dataviews` is not published to npm for public installs yet.');
11
+ return;
13
12
  }
14
13
  function assertValidCoreDataEntitySegment(label, value) {
15
14
  const trimmed = value.trim();
@@ -10,8 +10,6 @@ export declare const ADMIN_VIEWS_ASSET = "build/admin-views/index.asset.php";
10
10
  export declare const ADMIN_VIEWS_STYLE = "build/admin-views/style-index.css";
11
11
  export declare const ADMIN_VIEWS_STYLE_RTL = "build/admin-views/style-index-rtl.css";
12
12
  export declare const ADMIN_VIEWS_PHP_GLOB = "/inc/admin-views/*.php";
13
- export declare const ADMIN_VIEW_ALLOW_UNPUBLISHED_DATAVIEWS_ENV = "WP_TYPIA_ALLOW_UNPUBLISHED_DATAVIEWS";
14
- export declare const ADMIN_VIEW_PUBLIC_INSTALLS_ENABLED = false;
15
13
  export interface AdminViewRestResourceSource {
16
14
  kind: typeof ADMIN_VIEW_REST_SOURCE_KIND;
17
15
  slug: string;
@@ -12,9 +12,6 @@ export const ADMIN_VIEWS_ASSET = 'build/admin-views/index.asset.php';
12
12
  export const ADMIN_VIEWS_STYLE = 'build/admin-views/style-index.css';
13
13
  export const ADMIN_VIEWS_STYLE_RTL = 'build/admin-views/style-index-rtl.css';
14
14
  export const ADMIN_VIEWS_PHP_GLOB = '/inc/admin-views/*.php';
15
- export const ADMIN_VIEW_ALLOW_UNPUBLISHED_DATAVIEWS_ENV = 'WP_TYPIA_ALLOW_UNPUBLISHED_DATAVIEWS';
16
- // Lift this gate in the same release that publishes @wp-typia/dataviews.
17
- export const ADMIN_VIEW_PUBLIC_INSTALLS_ENABLED = false;
18
15
  export function isAdminViewCoreDataSource(source) {
19
16
  return source?.kind === ADMIN_VIEW_CORE_DATA_SOURCE_KIND;
20
17
  }
@@ -14,6 +14,7 @@ import { syncAiFeatureRestArtifacts, syncAiFeatureSchemaArtifact, } from "./ai-f
14
14
  * Write generated AI feature sources and patch shared workspace anchors.
15
15
  */
16
16
  export async function scaffoldAiFeatureWorkspace({ aiFeatureSlug, compatibilityPolicy, namespace, workspace, }) {
17
+ const compatibilityWarnings = [];
17
18
  const blockConfigPath = path.join(workspace.projectDir, "scripts", "block-config.ts");
18
19
  const bootstrapPath = getWorkspaceBootstrapPath(workspace);
19
20
  const packageJsonPath = path.join(workspace.projectDir, "package.json");
@@ -40,7 +41,11 @@ export async function scaffoldAiFeatureWorkspace({ aiFeatureSlug, compatibilityP
40
41
  await fsp.mkdir(aiFeatureDir, { recursive: true });
41
42
  await fsp.mkdir(path.dirname(phpFilePath), { recursive: true });
42
43
  await ensureAiFeatureBootstrapAnchors(workspace);
43
- await patchFile(bootstrapPath, (source) => updatePluginHeaderCompatibility(source, compatibilityPolicy));
44
+ await patchFile(bootstrapPath, (source) => updatePluginHeaderCompatibility(source, compatibilityPolicy, {
45
+ onWarning: (warning) => {
46
+ compatibilityWarnings.push(warning);
47
+ },
48
+ }));
44
49
  const packageScriptChanges = await ensureAiFeaturePackageScripts(workspace);
45
50
  await ensureAiFeatureSyncProjectAnchors(workspace);
46
51
  await ensureAiFeatureSyncRestAnchors(workspace);
@@ -76,11 +81,14 @@ export async function scaffoldAiFeatureWorkspace({ aiFeatureSlug, compatibilityP
76
81
  transformSource: ensureBlockConfigCanAddRestManifests,
77
82
  });
78
83
  return {
79
- warnings: packageScriptChanges.addedProjectToolsDependency
80
- ? [
81
- "Added `@wp-typia/project-tools` to devDependencies for `sync-ai`. If this workspace was already installed, rerun your package manager install command before the first `wp-typia sync ai`.",
82
- ]
83
- : [],
84
+ warnings: [
85
+ ...compatibilityWarnings,
86
+ ...(packageScriptChanges.addedProjectToolsDependency
87
+ ? [
88
+ "Added `@wp-typia/project-tools` to devDependencies for `sync-ai`. If this workspace was already installed, rerun your package manager install command before the first `wp-typia sync ai`.",
89
+ ]
90
+ : []),
91
+ ],
84
92
  };
85
93
  },
86
94
  });
@@ -1,6 +1,6 @@
1
- import fs from "node:fs";
2
1
  import { promises as fsp } from "node:fs";
3
2
  import path from "node:path";
3
+ import { pathExists } from "./fs-async.js";
4
4
  import { resolveWorkspaceProject } from "./workspace-project.js";
5
5
  import { appendWorkspaceInventoryEntries, readWorkspaceInventory } from "./workspace-inventory.js";
6
6
  import { toKebabCase, toSnakeCase, toTitleCase } from "./string-case.js";
@@ -381,8 +381,7 @@ async function writeVariationRegistry(projectDir, blockSlug, variationSlug) {
381
381
  const variationsDir = path.join(projectDir, "src", "blocks", blockSlug, "variations");
382
382
  const variationsIndexPath = path.join(variationsDir, "index.ts");
383
383
  await fsp.mkdir(variationsDir, { recursive: true });
384
- const existingVariationSlugs = fs
385
- .readdirSync(variationsDir)
384
+ const existingVariationSlugs = (await fsp.readdir(variationsDir))
386
385
  .filter((entry) => entry.endsWith(".ts") && entry !== "index.ts")
387
386
  .map((entry) => entry.replace(/\.ts$/u, ""));
388
387
  const nextVariationSlugs = Array.from(new Set([...existingVariationSlugs, variationSlug])).sort();
@@ -392,8 +391,7 @@ async function writeBlockStyleRegistry(projectDir, blockSlug, styleSlug) {
392
391
  const stylesDir = path.join(projectDir, "src", "blocks", blockSlug, "styles");
393
392
  const stylesIndexPath = path.join(stylesDir, "index.ts");
394
393
  await fsp.mkdir(stylesDir, { recursive: true });
395
- const existingStyleSlugs = fs
396
- .readdirSync(stylesDir)
394
+ const existingStyleSlugs = (await fsp.readdir(stylesDir))
397
395
  .filter((entry) => entry.endsWith(".ts") && entry !== "index.ts")
398
396
  .map((entry) => entry.replace(/\.ts$/u, ""));
399
397
  const nextStyleSlugs = Array.from(new Set([...existingStyleSlugs, styleSlug])).sort();
@@ -403,8 +401,7 @@ async function writeBlockTransformRegistry(projectDir, blockSlug, transformSlug)
403
401
  const transformsDir = path.join(projectDir, "src", "blocks", blockSlug, "transforms");
404
402
  const transformsIndexPath = path.join(transformsDir, "index.ts");
405
403
  await fsp.mkdir(transformsDir, { recursive: true });
406
- const existingTransformSlugs = fs
407
- .readdirSync(transformsDir)
404
+ const existingTransformSlugs = (await fsp.readdir(transformsDir))
408
405
  .filter((entry) => entry.endsWith(".ts") && entry !== "index.ts")
409
406
  .map((entry) => entry.replace(/\.ts$/u, ""));
410
407
  const nextTransformSlugs = Array.from(new Set([...existingTransformSlugs, transformSlug])).sort();
@@ -495,7 +492,7 @@ export async function runAddVariationCommand({ blockName, cwd = process.cwd(), v
495
492
  const variationsDir = path.join(workspace.projectDir, "src", "blocks", blockSlug, "variations");
496
493
  const variationFilePath = path.join(variationsDir, `${variationSlug}.ts`);
497
494
  const variationsIndexPath = path.join(variationsDir, "index.ts");
498
- const shouldRemoveVariationsDirOnRollback = !fs.existsSync(variationsDir);
495
+ const shouldRemoveVariationsDirOnRollback = !(await pathExists(variationsDir));
499
496
  const mutationSnapshot = {
500
497
  fileSources: await snapshotWorkspaceFiles([
501
498
  blockConfigPath,
@@ -555,7 +552,7 @@ export async function runAddBlockStyleCommand({ blockName, cwd = process.cwd(),
555
552
  const stylesDir = path.join(workspace.projectDir, "src", "blocks", blockSlug, "styles");
556
553
  const styleFilePath = path.join(stylesDir, `${styleSlug}.ts`);
557
554
  const stylesIndexPath = path.join(stylesDir, "index.ts");
558
- const shouldRemoveStylesDirOnRollback = !fs.existsSync(stylesDir);
555
+ const shouldRemoveStylesDirOnRollback = !(await pathExists(stylesDir));
559
556
  const mutationSnapshot = {
560
557
  fileSources: await snapshotWorkspaceFiles([
561
558
  blockConfigPath,
@@ -624,7 +621,7 @@ export async function runAddBlockTransformCommand({ cwd = process.cwd(), fromBlo
624
621
  const transformsDir = path.join(workspace.projectDir, "src", "blocks", target.blockSlug, "transforms");
625
622
  const transformFilePath = path.join(transformsDir, `${transformSlug}.ts`);
626
623
  const transformsIndexPath = path.join(transformsDir, "index.ts");
627
- const shouldRemoveTransformsDirOnRollback = !fs.existsSync(transformsDir);
624
+ const shouldRemoveTransformsDirOnRollback = !(await pathExists(transformsDir));
628
625
  const mutationSnapshot = {
629
626
  fileSources: await snapshotWorkspaceFiles([
630
627
  blockConfigPath,
@@ -695,7 +692,7 @@ export async function runAddHookedBlockCommand({ anchorBlockName, blockName, cwd
695
692
  if (resolvedAnchorBlockName === selfHookAnchor) {
696
693
  throw new Error("`wp-typia add hooked-block` cannot hook a block relative to its own block name.");
697
694
  }
698
- const { blockJson, blockJsonPath } = readWorkspaceBlockJson(workspace.projectDir, blockSlug);
695
+ const { blockJson, blockJsonPath } = await readWorkspaceBlockJson(workspace.projectDir, blockSlug);
699
696
  const blockJsonRelativePath = path.relative(workspace.projectDir, blockJsonPath);
700
697
  const blockHooks = getMutableBlockHooks(blockJson, blockJsonRelativePath);
701
698
  if (Object.prototype.hasOwnProperty.call(blockHooks, resolvedAnchorBlockName)) {
@@ -1,11 +1,10 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { parseScaffoldBlockMetadata } from "@wp-typia/block-runtime/blocks";
4
- import { checkExistingFiles, createDoctorCheck, WORKSPACE_BINDING_EDITOR_ASSET, WORKSPACE_BINDING_EDITOR_SCRIPT, WORKSPACE_BINDING_SERVER_GLOB, } from "./cli-doctor-workspace-shared.js";
4
+ import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_BINDING_EDITOR_ASSET, WORKSPACE_BINDING_EDITOR_SCRIPT, WORKSPACE_BINDING_SERVER_GLOB, } from "./cli-doctor-workspace-shared.js";
5
5
  import { escapeRegex } from "./php-utils.js";
6
6
  function checkWorkspaceBindingBootstrap(projectDir, packageName) {
7
- const packageBaseName = packageName.split("/").pop() ?? packageName;
8
- const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
7
+ const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
9
8
  if (!fs.existsSync(bootstrapPath)) {
10
9
  return createDoctorCheck("Binding bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
11
10
  }
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { parseScaffoldBlockMetadata } from "@wp-typia/block-runtime/blocks";
4
- import { checkExistingFiles, createDoctorCheck, WORKSPACE_FULL_BLOCK_NAME_PATTERN, WORKSPACE_GENERATED_BLOCK_ARTIFACTS, } from "./cli-doctor-workspace-shared.js";
4
+ import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_FULL_BLOCK_NAME_PATTERN, WORKSPACE_GENERATED_BLOCK_ARTIFACTS, } from "./cli-doctor-workspace-shared.js";
5
5
  import { HOOKED_BLOCK_ANCHOR_PATTERN, HOOKED_BLOCK_POSITION_SET, } from "./hooked-blocks.js";
6
6
  import { hasExecutablePattern, hasUncommentedPattern, maskTypeScriptCommentsAndLiterals, } from "./ts-source-masking.js";
7
7
  const WORKSPACE_COLLECTION_IMPORT_LINE = "import '../../collection';";
@@ -306,8 +306,7 @@ function checkWorkspaceBlockIframeCompatibility(projectDir, blockSlug) {
306
306
  ];
307
307
  }
308
308
  function checkWorkspacePatternBootstrap(projectDir, packageName) {
309
- const packageBaseName = packageName.split("/").pop() ?? packageName;
310
- const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
309
+ const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
311
310
  if (!fs.existsSync(bootstrapPath)) {
312
311
  return createDoctorCheck("Pattern bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
313
312
  }
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { EDITOR_PLUGIN_SLOT_IDS, REST_RESOURCE_METHOD_IDS, REST_RESOURCE_NAMESPACE_PATTERN, resolveEditorPluginSlotAlias, } from "./cli-add-shared.js";
4
- import { checkExistingFiles, createDoctorCheck, WORKSPACE_ABILITY_EDITOR_ASSET, WORKSPACE_ABILITY_EDITOR_SCRIPT, WORKSPACE_ABILITY_GLOB, WORKSPACE_ADMIN_VIEW_ASSET, WORKSPACE_ADMIN_VIEW_GLOB, WORKSPACE_ADMIN_VIEW_SCRIPT, WORKSPACE_ADMIN_VIEW_STYLE, WORKSPACE_AI_FEATURE_GLOB, WORKSPACE_EDITOR_PLUGIN_EDITOR_ASSET, WORKSPACE_EDITOR_PLUGIN_EDITOR_SCRIPT, WORKSPACE_EDITOR_PLUGIN_EDITOR_STYLE, WORKSPACE_REST_RESOURCE_GLOB, } from "./cli-doctor-workspace-shared.js";
4
+ import { checkExistingFiles, createDoctorCheck, resolveWorkspaceBootstrapPath, WORKSPACE_ABILITY_EDITOR_ASSET, WORKSPACE_ABILITY_EDITOR_SCRIPT, WORKSPACE_ABILITY_GLOB, WORKSPACE_ADMIN_VIEW_ASSET, WORKSPACE_ADMIN_VIEW_GLOB, WORKSPACE_ADMIN_VIEW_SCRIPT, WORKSPACE_ADMIN_VIEW_STYLE, WORKSPACE_AI_FEATURE_GLOB, WORKSPACE_EDITOR_PLUGIN_EDITOR_ASSET, WORKSPACE_EDITOR_PLUGIN_EDITOR_SCRIPT, WORKSPACE_EDITOR_PLUGIN_EDITOR_STYLE, WORKSPACE_REST_RESOURCE_GLOB, } from "./cli-doctor-workspace-shared.js";
5
5
  import { escapeRegex } from "./php-utils.js";
6
6
  function getWorkspaceRestResourceRequiredFiles(restResource) {
7
7
  const schemaNames = new Set();
@@ -46,8 +46,7 @@ function checkWorkspaceRestResourceConfig(restResource) {
46
46
  : "REST resource namespace or methods are invalid");
47
47
  }
48
48
  function checkWorkspaceRestResourceBootstrap(projectDir, packageName, phpPrefix) {
49
- const packageBaseName = packageName.split("/").pop() ?? packageName;
50
- const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
49
+ const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
51
50
  if (!fs.existsSync(bootstrapPath)) {
52
51
  return createDoctorCheck("REST resource bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
53
52
  }
@@ -93,8 +92,7 @@ function checkWorkspaceAbilityConfig(projectDir, ability) {
93
92
  }
94
93
  }
95
94
  function checkWorkspaceAbilityBootstrap(projectDir, packageName, phpPrefix) {
96
- const packageBaseName = packageName.split("/").pop() ?? packageName;
97
- const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
95
+ const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
98
96
  if (!fs.existsSync(bootstrapPath)) {
99
97
  return createDoctorCheck("Ability bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
100
98
  }
@@ -169,8 +167,7 @@ function checkWorkspaceAiFeatureConfig(aiFeature) {
169
167
  : "AI feature namespace is invalid");
170
168
  }
171
169
  function checkWorkspaceAiFeatureBootstrap(projectDir, packageName, phpPrefix) {
172
- const packageBaseName = packageName.split("/").pop() ?? packageName;
173
- const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
170
+ const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
174
171
  if (!fs.existsSync(bootstrapPath)) {
175
172
  return createDoctorCheck("AI feature bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
176
173
  }
@@ -204,8 +201,7 @@ function checkWorkspaceEditorPluginConfig(editorPlugin) {
204
201
  : `Unsupported editor plugin slot "${editorPlugin.slot}". Expected one of: ${EDITOR_PLUGIN_SLOT_IDS.join(", ")} or legacy aliases PluginSidebar, PluginDocumentSettingPanel.`);
205
202
  }
206
203
  function checkWorkspaceEditorPluginBootstrap(projectDir, packageName, phpPrefix) {
207
- const packageBaseName = packageName.split("/").pop() ?? packageName;
208
- const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
204
+ const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
209
205
  if (!fs.existsSync(bootstrapPath)) {
210
206
  return createDoctorCheck("Editor plugin bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
211
207
  }
@@ -269,8 +265,7 @@ function checkWorkspaceAdminViewConfig(adminView, inventory) {
269
265
  : "Admin view source must use rest-resource:<slug> with a list-capable REST resource or core-data:<postType|taxonomy>/<name>");
270
266
  }
271
267
  function checkWorkspaceAdminViewBootstrap(projectDir, packageName, phpPrefix) {
272
- const packageBaseName = packageName.split("/").pop() ?? packageName;
273
- const bootstrapPath = path.join(projectDir, `${packageBaseName}.php`);
268
+ const bootstrapPath = resolveWorkspaceBootstrapPath(projectDir, packageName);
274
269
  if (!fs.existsSync(bootstrapPath)) {
275
270
  return createDoctorCheck("Admin view bootstrap", "fail", `Missing ${path.basename(bootstrapPath)}`);
276
271
  }
@@ -58,6 +58,14 @@ export declare function createDoctorScopeCheck(status: DoctorCheck["status"], de
58
58
  * @returns Relative PHP bootstrap filename for the workspace root.
59
59
  */
60
60
  export declare function getWorkspaceBootstrapRelativePath(packageName: string): string;
61
+ /**
62
+ * Resolve the expected workspace bootstrap file inside a project root.
63
+ *
64
+ * @param projectDir Absolute workspace root directory.
65
+ * @param packageName Package name used to derive the plugin bootstrap basename.
66
+ * @returns Absolute PHP bootstrap file path for the workspace root.
67
+ */
68
+ export declare function resolveWorkspaceBootstrapPath(projectDir: string, packageName: string): string;
61
69
  /**
62
70
  * Verify that every referenced relative file exists inside a workspace.
63
71
  *
@@ -71,6 +71,16 @@ export function createDoctorScopeCheck(status, detail) {
71
71
  export function getWorkspaceBootstrapRelativePath(packageName) {
72
72
  return `${packageName.split("/").pop() ?? packageName}.php`;
73
73
  }
74
+ /**
75
+ * Resolve the expected workspace bootstrap file inside a project root.
76
+ *
77
+ * @param projectDir Absolute workspace root directory.
78
+ * @param packageName Package name used to derive the plugin bootstrap basename.
79
+ * @returns Absolute PHP bootstrap file path for the workspace root.
80
+ */
81
+ export function resolveWorkspaceBootstrapPath(projectDir, packageName) {
82
+ return path.join(projectDir, getWorkspaceBootstrapRelativePath(packageName));
83
+ }
74
84
  /**
75
85
  * Verify that every referenced relative file exists inside a workspace.
76
86
  *
@@ -52,7 +52,7 @@ Notes:
52
52
  \`add admin-view\` scaffolds an opt-in DataViews-powered WordPress admin screen under \`src/admin-views/\`.
53
53
  Pass \`--source rest-resource:<slug>\` to reuse a list-capable REST resource.
54
54
  Pass \`--source core-data:postType/post\` or \`--source core-data:taxonomy/category\` to bind a WordPress-owned entity collection.
55
- Public installs currently gate this workflow until \`@wp-typia/dataviews\` is published to npm.
55
+ Generated admin-view workspaces add \`@wp-typia/dataviews\` and the needed WordPress DataViews packages as opt-in dependencies.
56
56
  \`query-loop\` is create-only. Use \`wp-typia create <project-dir> --template query-loop\`; \`wp-typia add block\` accepts only basic, interactivity, persistence, and compound families.
57
57
  \`add variation\` uses an existing workspace block from \`scripts/block-config.ts\`.
58
58
  \`add style\` registers a Block Styles option for an existing generated block.
@@ -0,0 +1,15 @@
1
+ import { type RetrofitInitPlan } from "./cli-init-types.js";
2
+ /**
3
+ * Apply the previewed retrofit init plan to disk.
4
+ *
5
+ * The command snapshots package.json and generated helper targets before
6
+ * writing, then rolls those files back automatically if any write fails.
7
+ *
8
+ * @param projectDir Project root that should receive the retrofit surface.
9
+ * @param options Optional package-manager override used for emitted scripts and
10
+ * follow-up guidance.
11
+ * @returns The applied retrofit init plan describing the persisted changes.
12
+ */
13
+ export declare function applyInitPlan(projectDir: string, options?: {
14
+ packageManager?: string;
15
+ }): Promise<RetrofitInitPlan>;
@@ -0,0 +1,99 @@
1
+ import fs from "node:fs";
2
+ import { promises as fsp } from "node:fs";
3
+ import path from "node:path";
4
+ import { CLI_DIAGNOSTIC_CODES, createCliDiagnosticCodeError, } from "./cli-diagnostics.js";
5
+ import { rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
6
+ import { buildNextProjectPackageJson, buildProjectPackageJsonSource, readProjectPackageJson, } from "./cli-init-package-json.js";
7
+ import { createRetrofitPlan, getInitPlan } from "./cli-init-plan.js";
8
+ import { buildRetrofitHelperFiles } from "./cli-init-templates.js";
9
+ import { RETROFIT_APPLY_PREVIEW_NOTE, RETROFIT_ROLLBACK_NOTE, } from "./cli-init-types.js";
10
+ async function createRetrofitMutationSnapshot(projectDir, filePaths) {
11
+ const scriptsDir = path.join(projectDir, "scripts");
12
+ const scriptsDirExisted = fs.existsSync(scriptsDir);
13
+ const fileSources = await snapshotWorkspaceFiles(filePaths);
14
+ const targetPaths = fileSources
15
+ .filter((entry) => entry.source === null)
16
+ .map((entry) => entry.filePath);
17
+ if (!scriptsDirExisted) {
18
+ targetPaths.push(scriptsDir);
19
+ }
20
+ return {
21
+ fileSources,
22
+ snapshotDirs: [],
23
+ targetPaths,
24
+ };
25
+ }
26
+ async function writeRetrofitFiles(options) {
27
+ const helperFiles = buildRetrofitHelperFiles(options.blockTargets);
28
+ const scriptsDir = path.join(options.projectDir, "scripts");
29
+ await fsp.mkdir(scriptsDir, { recursive: true });
30
+ await fsp.writeFile(path.join(options.projectDir, "package.json"), buildProjectPackageJsonSource(options.packageJson), "utf8");
31
+ for (const [relativePath, source] of Object.entries(helperFiles)) {
32
+ await fsp.writeFile(path.join(options.projectDir, relativePath), source, "utf8");
33
+ }
34
+ }
35
+ function buildApplyFailureError(error) {
36
+ const message = error instanceof Error ? error.message : String(error);
37
+ 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);
38
+ }
39
+ function toApplyNotes(previewNotes) {
40
+ return Array.from(new Set([
41
+ ...previewNotes.filter((note) => note !== "Preview only: `wp-typia init` does not write files yet." &&
42
+ note !== RETROFIT_APPLY_PREVIEW_NOTE),
43
+ RETROFIT_ROLLBACK_NOTE,
44
+ ]));
45
+ }
46
+ /**
47
+ * Apply the previewed retrofit init plan to disk.
48
+ *
49
+ * The command snapshots package.json and generated helper targets before
50
+ * writing, then rolls those files back automatically if any write fails.
51
+ *
52
+ * @param projectDir Project root that should receive the retrofit surface.
53
+ * @param options Optional package-manager override used for emitted scripts and
54
+ * follow-up guidance.
55
+ * @returns The applied retrofit init plan describing the persisted changes.
56
+ */
57
+ export async function applyInitPlan(projectDir, options = {}) {
58
+ const previewPlan = getInitPlan(projectDir, options);
59
+ if (previewPlan.detectedLayout.kind === "unsupported") {
60
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.INVALID_ARGUMENT, "`wp-typia init --apply` requires a supported retrofit layout. Run `wp-typia init` first to inspect the preview plan and any blocking notes.");
61
+ }
62
+ if (previewPlan.status === "already-initialized") {
63
+ return createRetrofitPlan({
64
+ ...previewPlan,
65
+ commandMode: "apply",
66
+ notes: toApplyNotes(previewPlan.notes),
67
+ status: "already-initialized",
68
+ });
69
+ }
70
+ const nextPackageJson = buildNextProjectPackageJson({
71
+ packageChanges: previewPlan.packageChanges,
72
+ packageJson: readProjectPackageJson(previewPlan.projectDir),
73
+ packageManager: previewPlan.packageManager,
74
+ projectName: previewPlan.projectName,
75
+ });
76
+ const helperFiles = buildRetrofitHelperFiles(previewPlan.blockTargets);
77
+ const filePaths = [
78
+ path.join(previewPlan.projectDir, "package.json"),
79
+ ...Object.keys(helperFiles).map((relativePath) => path.join(previewPlan.projectDir, relativePath)),
80
+ ];
81
+ const mutationSnapshot = await createRetrofitMutationSnapshot(previewPlan.projectDir, filePaths);
82
+ try {
83
+ await writeRetrofitFiles({
84
+ blockTargets: previewPlan.blockTargets,
85
+ packageJson: nextPackageJson,
86
+ projectDir: previewPlan.projectDir,
87
+ });
88
+ }
89
+ catch (error) {
90
+ await rollbackWorkspaceMutation(mutationSnapshot);
91
+ throw buildApplyFailureError(error);
92
+ }
93
+ return createRetrofitPlan({
94
+ ...previewPlan,
95
+ commandMode: "apply",
96
+ notes: toApplyNotes(previewPlan.notes),
97
+ status: "applied",
98
+ });
99
+ }
@@ -0,0 +1,19 @@
1
+ import { type PackageManagerId } from "./package-managers.js";
2
+ import type { InitDependencyChange, InitPackageManagerFieldChange, InitScriptChange, ProjectPackageJson, RetrofitInitPlan } from "./cli-init-types.js";
3
+ export declare function readProjectPackageJson(projectDir: string): ProjectPackageJson | null;
4
+ export declare function resolveInitPackageManager(projectDir: string, packageJson: ProjectPackageJson | null, override?: string): PackageManagerId;
5
+ export declare function getWpTypiaCliSpecifier(): string;
6
+ export declare function buildDependencyChanges(packageJson: ProjectPackageJson | null): InitDependencyChange[];
7
+ export declare function buildScriptChanges(packageJson: ProjectPackageJson | null, packageManager: PackageManagerId): InitScriptChange[];
8
+ export declare function buildPackageManagerFieldChange(packageJson: ProjectPackageJson | null, packageManager: PackageManagerId, options?: {
9
+ persistExplicitOverride?: boolean;
10
+ }): InitPackageManagerFieldChange | undefined;
11
+ export declare function hasExistingWpTypiaProjectSurface(projectDir: string, packageJson: ProjectPackageJson | null): boolean;
12
+ export declare function buildRequiredDevDependencyMapEntries(): string[];
13
+ export declare function buildNextProjectPackageJson(options: {
14
+ packageChanges: RetrofitInitPlan["packageChanges"];
15
+ packageJson: ProjectPackageJson | null;
16
+ packageManager: PackageManagerId;
17
+ projectName: string;
18
+ }): ProjectPackageJson;
19
+ export declare function buildProjectPackageJsonSource(packageJson: ProjectPackageJson): string;