@wp-typia/project-tools 0.20.1 → 0.21.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.
Files changed (48) hide show
  1. package/dist/runtime/cli-add-block.js +1 -1
  2. package/dist/runtime/cli-add-shared.d.ts +73 -5
  3. package/dist/runtime/cli-add-shared.js +58 -11
  4. package/dist/runtime/cli-add-workspace-ability.js +11 -57
  5. package/dist/runtime/cli-add-workspace-admin-view.d.ts +23 -0
  6. package/dist/runtime/cli-add-workspace-admin-view.js +872 -0
  7. package/dist/runtime/cli-add-workspace-ai-anchors.js +2 -5
  8. package/dist/runtime/cli-add-workspace-ai-source-emitters.d.ts +0 -4
  9. package/dist/runtime/cli-add-workspace-ai-source-emitters.js +7 -17
  10. package/dist/runtime/cli-add-workspace-ai.js +4 -6
  11. package/dist/runtime/cli-add-workspace-assets.d.ts +13 -5
  12. package/dist/runtime/cli-add-workspace-assets.js +290 -106
  13. package/dist/runtime/cli-add-workspace-rest-anchors.js +2 -5
  14. package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +0 -1
  15. package/dist/runtime/cli-add-workspace-rest-source-emitters.js +7 -14
  16. package/dist/runtime/cli-add-workspace-rest.js +4 -6
  17. package/dist/runtime/cli-add-workspace.d.ts +58 -1
  18. package/dist/runtime/cli-add-workspace.js +588 -18
  19. package/dist/runtime/cli-add.d.ts +1 -1
  20. package/dist/runtime/cli-add.js +1 -1
  21. package/dist/runtime/cli-core.d.ts +8 -5
  22. package/dist/runtime/cli-core.js +7 -4
  23. package/dist/runtime/cli-diagnostics.d.ts +84 -1
  24. package/dist/runtime/cli-diagnostics.js +90 -3
  25. package/dist/runtime/cli-doctor-workspace.js +552 -13
  26. package/dist/runtime/cli-doctor.d.ts +4 -2
  27. package/dist/runtime/cli-doctor.js +2 -1
  28. package/dist/runtime/cli-help.js +19 -9
  29. package/dist/runtime/cli-init.d.ts +67 -3
  30. package/dist/runtime/cli-init.js +603 -64
  31. package/dist/runtime/cli-validation.js +4 -3
  32. package/dist/runtime/index.d.ts +9 -4
  33. package/dist/runtime/index.js +7 -3
  34. package/dist/runtime/package-json-types.d.ts +12 -0
  35. package/dist/runtime/package-json-types.js +1 -0
  36. package/dist/runtime/package-versions.d.ts +17 -2
  37. package/dist/runtime/package-versions.js +46 -1
  38. package/dist/runtime/php-utils.d.ts +16 -0
  39. package/dist/runtime/php-utils.js +59 -0
  40. package/dist/runtime/scaffold-answer-resolution.js +35 -10
  41. package/dist/runtime/scaffold-apply-utils.d.ts +2 -3
  42. package/dist/runtime/scaffold-apply-utils.js +3 -43
  43. package/dist/runtime/template-source-cache.d.ts +112 -0
  44. package/dist/runtime/template-source-cache.js +434 -0
  45. package/dist/runtime/template-source-seeds.js +333 -53
  46. package/dist/runtime/workspace-inventory.d.ts +43 -2
  47. package/dist/runtime/workspace-inventory.js +138 -5
  48. package/package.json +2 -2
@@ -1,5 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { CLI_DIAGNOSTIC_CODES, createCliDiagnosticCodeError, } from "./cli-diagnostics.js";
3
4
  /**
4
5
  * Normalize one optional CLI string flag by trimming whitespace and collapsing
5
6
  * empty strings to `undefined`.
@@ -44,7 +45,7 @@ export function resolveLocalCliPathOption(options) {
44
45
  }
45
46
  const resolvedPath = path.resolve(options.cwd, normalizedValue);
46
47
  if (!fs.existsSync(resolvedPath)) {
47
- throw new Error(`\`${options.label}\` path does not exist: ${resolvedPath}. Check the path relative to ${options.cwd}.`);
48
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.INVALID_ARGUMENT, `\`${options.label}\` path does not exist: ${resolvedPath}. Check the path relative to ${options.cwd}.`);
48
49
  }
49
50
  return resolvedPath;
50
51
  }
@@ -58,7 +59,7 @@ export function resolveLocalCliPathOption(options) {
58
59
  */
59
60
  export function assertExternalLayerCompositionOptions(options) {
60
61
  if (options.externalLayerId && !options.externalLayerSource) {
61
- throw new Error("externalLayerId requires externalLayerSource when composing built-in template layers.");
62
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.INVALID_ARGUMENT, "externalLayerId requires externalLayerSource when composing built-in template layers.");
62
63
  }
63
64
  }
64
65
  /**
@@ -85,7 +86,7 @@ export function assertBuiltInTemplateVariantAllowed(options) {
85
86
  if (!options.variant) {
86
87
  return;
87
88
  }
88
- throw new Error(createBuiltInVariantErrorMessage({
89
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.INVALID_ARGUMENT, createBuiltInVariantErrorMessage({
89
90
  templateId: options.templateId,
90
91
  variant: options.variant,
91
92
  }));
@@ -5,8 +5,11 @@
5
5
  * CLI while keeping reusable project logic out of the CLI package itself.
6
6
  * Consumers should prefer these exports for scaffold, add, migrate, doctor,
7
7
  * and workspace-aware helpers such as `getWorkspaceBlockSelectOptions`,
8
- * `runAddBlockCommand`, `runAddVariationCommand`, `runAddPatternCommand`,
9
- * `runAddBindingSourceCommand`, `runAddEditorPluginCommand`,
8
+ * `runAddBlockCommand`, `runAddBlockStyleCommand`,
9
+ * `runAddBlockTransformCommand`, `runAddVariationCommand`,
10
+ * `runAddPatternCommand`, `runAddBindingSourceCommand`,
11
+ * `runAddEditorPluginCommand`,
12
+ * `runAddAdminViewCommand`,
10
13
  * `runAddHookedBlockCommand`,
11
14
  * `HOOKED_BLOCK_POSITION_IDS`, and `runDoctor`.
12
15
  */
@@ -26,7 +29,9 @@ export { manifestAttributeToJsonSchema, projectJsonSchemaDocument, manifestToJso
26
29
  export { buildCompoundChildStarterManifestDocument, getStarterManifestFiles, stringifyStarterManifest, } from "./starter-manifests.js";
27
30
  export type { EndpointAuthIntent, EndpointOpenApiAuthMode, EndpointOpenApiContractDocument, EndpointOpenApiDocumentOptions, EndpointOpenApiEndpointDefinition, EndpointOpenApiMethod, EndpointWordPressAuthDefinition, EndpointWordPressAuthMechanism, JsonSchemaDocument, JsonSchemaProjectionProfile, JsonSchemaObject, NormalizedEndpointAuthDefinition, OpenApiDocument, OpenApiInfo, OpenApiOperation, OpenApiParameter, OpenApiPathItem, OpenApiSecurityScheme, } from "./schema-core.js";
28
31
  export { PACKAGE_MANAGER_IDS, PACKAGE_MANAGERS, formatPackageExecCommand, formatInstallCommand, formatRunScript, getPackageManager, getPackageManagerSelectOptions, transformPackageManagerText, } from "./package-managers.js";
32
+ export { clearPackageVersionsCache, getPackageVersions, invalidatePackageVersionsCache, } from "./package-versions.js";
33
+ export type { PackageVersions } from "./package-versions.js";
29
34
  export { TEMPLATE_IDS, TEMPLATE_REGISTRY, getTemplateById, getTemplateSelectOptions, listTemplates, } from "./template-registry.js";
30
35
  export { STALE_TEMP_ROOT_MAX_AGE_MS, WP_TYPIA_TEMP_ROOT_PREFIX, cleanupManagedTempRoot, cleanupStaleTempRoots, createManagedTempRoot, getTrackedTempRoots, } from "./temp-roots.js";
31
- export { createReadlinePrompt, createCliCommandError, CliDiagnosticError, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, HOOKED_BLOCK_POSITION_IDS, EDITOR_PLUGIN_SLOT_IDS, isCliDiagnosticError, runAddAbilityCommand, runAddAiFeatureCommand, runAddBindingSourceCommand, runAddBlockCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
32
- export type { CliDiagnosticMessage, DoctorCheck, EditorPluginSlotId, HookedBlockPositionId, ReadlinePrompt, } from "./cli-core.js";
36
+ export { createReadlinePrompt, createCliCommandError, createCliDiagnosticCodeError, CliDiagnosticError, CLI_DIAGNOSTIC_CODES, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, HOOKED_BLOCK_POSITION_IDS, EDITOR_PLUGIN_SLOT_IDS, isCliDiagnosticError, runAddAdminViewCommand, runAddAbilityCommand, runAddAiFeatureCommand, runAddBindingSourceCommand, runAddBlockCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
37
+ export type { CliDiagnosticCode, CliDiagnosticCodeError, CliDiagnosticMessage, DoctorCheck, EditorPluginSlotId, HookedBlockPositionId, ReadlinePrompt, } from "./cli-core.js";
@@ -5,8 +5,11 @@
5
5
  * CLI while keeping reusable project logic out of the CLI package itself.
6
6
  * Consumers should prefer these exports for scaffold, add, migrate, doctor,
7
7
  * and workspace-aware helpers such as `getWorkspaceBlockSelectOptions`,
8
- * `runAddBlockCommand`, `runAddVariationCommand`, `runAddPatternCommand`,
9
- * `runAddBindingSourceCommand`, `runAddEditorPluginCommand`,
8
+ * `runAddBlockCommand`, `runAddBlockStyleCommand`,
9
+ * `runAddBlockTransformCommand`, `runAddVariationCommand`,
10
+ * `runAddPatternCommand`, `runAddBindingSourceCommand`,
11
+ * `runAddEditorPluginCommand`,
12
+ * `runAddAdminViewCommand`,
10
13
  * `runAddHookedBlockCommand`,
11
14
  * `HOOKED_BLOCK_POSITION_IDS`, and `runDoctor`.
12
15
  */
@@ -20,6 +23,7 @@ export { parseWorkspacePackageManagerId, resolveWorkspaceProject, tryResolveWork
20
23
  export { manifestAttributeToJsonSchema, projectJsonSchemaDocument, manifestToJsonSchema, manifestToOpenApi, normalizeEndpointAuthDefinition, } from "./schema-core.js";
21
24
  export { buildCompoundChildStarterManifestDocument, getStarterManifestFiles, stringifyStarterManifest, } from "./starter-manifests.js";
22
25
  export { PACKAGE_MANAGER_IDS, PACKAGE_MANAGERS, formatPackageExecCommand, formatInstallCommand, formatRunScript, getPackageManager, getPackageManagerSelectOptions, transformPackageManagerText, } from "./package-managers.js";
26
+ export { clearPackageVersionsCache, getPackageVersions, invalidatePackageVersionsCache, } from "./package-versions.js";
23
27
  export { TEMPLATE_IDS, TEMPLATE_REGISTRY, getTemplateById, getTemplateSelectOptions, listTemplates, } from "./template-registry.js";
24
28
  export { STALE_TEMP_ROOT_MAX_AGE_MS, WP_TYPIA_TEMP_ROOT_PREFIX, cleanupManagedTempRoot, cleanupStaleTempRoots, createManagedTempRoot, getTrackedTempRoots, } from "./temp-roots.js";
25
- export { createReadlinePrompt, createCliCommandError, CliDiagnosticError, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, HOOKED_BLOCK_POSITION_IDS, EDITOR_PLUGIN_SLOT_IDS, isCliDiagnosticError, runAddAbilityCommand, runAddAiFeatureCommand, runAddBindingSourceCommand, runAddBlockCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
29
+ export { createReadlinePrompt, createCliCommandError, createCliDiagnosticCodeError, CliDiagnosticError, CLI_DIAGNOSTIC_CODES, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, HOOKED_BLOCK_POSITION_IDS, EDITOR_PLUGIN_SLOT_IDS, isCliDiagnosticError, runAddAdminViewCommand, runAddAbilityCommand, runAddAiFeatureCommand, runAddBindingSourceCommand, runAddBlockCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runDoctor, runAddVariationCommand, runScaffoldFlow, } from "./cli-core.js";
@@ -0,0 +1,12 @@
1
+ export interface GeneratedPackageJson {
2
+ dependencies?: Record<string, string>;
3
+ devDependencies?: Record<string, string>;
4
+ packageManager?: string;
5
+ scripts?: Record<string, string>;
6
+ wpTypia?: {
7
+ projectType?: string;
8
+ templatePackage?: string;
9
+ [key: string]: unknown;
10
+ };
11
+ [key: string]: unknown;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,9 +1,13 @@
1
- interface PackageVersions {
1
+ export interface PackageVersions {
2
2
  apiClientPackageVersion: string;
3
3
  blockRuntimePackageVersion: string;
4
4
  blockTypesPackageVersion: string;
5
5
  projectToolsPackageVersion: string;
6
6
  restPackageVersion: string;
7
+ tsxPackageVersion: string;
8
+ typiaPackageVersion: string;
9
+ typiaUnpluginPackageVersion: string;
10
+ typescriptPackageVersion: string;
7
11
  wpTypiaPackageExactVersion: string;
8
12
  wpTypiaPackageVersion: string;
9
13
  }
@@ -14,6 +18,17 @@ interface PackageVersions {
14
18
  * manifests when they want the next lookup to recompute version metadata
15
19
  * synchronously from disk.
16
20
  */
21
+ export declare function clearPackageVersionsCache(): void;
22
+ /**
23
+ * Backwards-compatible alias for integrations that adopted the original
24
+ * internal invalidation name before the public cache policy was documented.
25
+ */
17
26
  export declare function invalidatePackageVersionsCache(): void;
27
+ /**
28
+ * Resolve package versions used in generated manifests and onboarding text.
29
+ *
30
+ * The lookup keeps a process-local cached result while recomputing manifest
31
+ * fingerprints on each call. When the relevant package metadata changes on
32
+ * disk, the cache key changes and the returned version object is refreshed.
33
+ */
18
34
  export declare function getPackageVersions(): PackageVersions;
19
- export {};
@@ -5,6 +5,8 @@ import { PROJECT_TOOLS_PACKAGE_ROOT } from "./template-registry.js";
5
5
  const require = createRequire(import.meta.url);
6
6
  const DEFAULT_VERSION_RANGE = "^0.0.0";
7
7
  const DEFAULT_EXACT_VERSION = "0.0.0";
8
+ const DEFAULT_TSX_PACKAGE_VERSION = "^4.20.5";
9
+ const DEFAULT_TYPIA_UNPLUGIN_PACKAGE_VERSION = "^12.0.1";
8
10
  let cachedPackageVersions = null;
9
11
  function getErrorCode(error) {
10
12
  return typeof error === "object" && error !== null && "code" in error
@@ -31,6 +33,10 @@ function normalizeExactVersion(value) {
31
33
  }
32
34
  return trimmed.replace(/^[~^<>=]+/, "");
33
35
  }
36
+ function normalizeVersionRangeWithFallback(value, fallback) {
37
+ const normalized = normalizeVersionRange(value);
38
+ return normalized === DEFAULT_VERSION_RANGE ? fallback : normalized;
39
+ }
34
40
  function createContentFingerprint(source) {
35
41
  let hash = 2166136261;
36
42
  for (let index = 0; index < source.length; index += 1) {
@@ -91,11 +97,26 @@ function composePackageVersionsCacheKey(locations) {
91
97
  * manifests when they want the next lookup to recompute version metadata
92
98
  * synchronously from disk.
93
99
  */
94
- export function invalidatePackageVersionsCache() {
100
+ export function clearPackageVersionsCache() {
95
101
  cachedPackageVersions = null;
96
102
  }
103
+ /**
104
+ * Backwards-compatible alias for integrations that adopted the original
105
+ * internal invalidation name before the public cache policy was documented.
106
+ */
107
+ export function invalidatePackageVersionsCache() {
108
+ clearPackageVersionsCache();
109
+ }
110
+ /**
111
+ * Resolve package versions used in generated manifests and onboarding text.
112
+ *
113
+ * The lookup keeps a process-local cached result while recomputing manifest
114
+ * fingerprints on each call. When the relevant package metadata changes on
115
+ * disk, the cache key changes and the returned version object is refreshed.
116
+ */
97
117
  export function getPackageVersions() {
98
118
  const createManifestLocation = resolvePackageManifestLocation(path.join(PROJECT_TOOLS_PACKAGE_ROOT, "package.json"));
119
+ const monorepoManifestLocation = resolvePackageManifestLocation(path.join(PROJECT_TOOLS_PACKAGE_ROOT, "..", "..", "package.json"));
99
120
  const blockRuntimeManifestLocation = resolvePackageManifestLocation(path.join(PROJECT_TOOLS_PACKAGE_ROOT, "..", "wp-typia-block-runtime", "package.json"));
100
121
  const wpTypiaManifestLocation = resolvePackageManifestLocation(path.join(PROJECT_TOOLS_PACKAGE_ROOT, "..", "wp-typia", "package.json"));
101
122
  const installedProjectToolsManifestLocation = resolveInstalledPackageManifestLocation("@wp-typia/project-tools");
@@ -103,9 +124,14 @@ export function getPackageVersions() {
103
124
  const installedBlockRuntimeManifestLocation = resolveInstalledPackageManifestLocation("@wp-typia/block-runtime");
104
125
  const installedBlockTypesManifestLocation = resolveInstalledPackageManifestLocation("@wp-typia/block-types");
105
126
  const installedRestManifestLocation = resolveInstalledPackageManifestLocation("@wp-typia/rest");
127
+ const installedTsxManifestLocation = resolveInstalledPackageManifestLocation("tsx");
128
+ const installedTypiaManifestLocation = resolveInstalledPackageManifestLocation("typia");
129
+ const installedTypiaUnpluginManifestLocation = resolveInstalledPackageManifestLocation("@typia/unplugin");
130
+ const installedTypescriptManifestLocation = resolveInstalledPackageManifestLocation("typescript");
106
131
  const installedWpTypiaManifestLocation = resolveInstalledPackageManifestLocation("wp-typia");
107
132
  const cacheKey = composePackageVersionsCacheKey([
108
133
  createManifestLocation,
134
+ monorepoManifestLocation,
109
135
  blockRuntimeManifestLocation,
110
136
  wpTypiaManifestLocation,
111
137
  installedProjectToolsManifestLocation,
@@ -113,6 +139,10 @@ export function getPackageVersions() {
113
139
  installedBlockRuntimeManifestLocation,
114
140
  installedBlockTypesManifestLocation,
115
141
  installedRestManifestLocation,
142
+ installedTsxManifestLocation,
143
+ installedTypiaManifestLocation,
144
+ installedTypiaUnpluginManifestLocation,
145
+ installedTypescriptManifestLocation,
116
146
  installedWpTypiaManifestLocation,
117
147
  ]);
118
148
  if (cachedPackageVersions?.cacheKey === cacheKey) {
@@ -121,6 +151,7 @@ export function getPackageVersions() {
121
151
  const createManifest = readPackageManifest(createManifestLocation) ??
122
152
  readPackageManifest(installedProjectToolsManifestLocation) ??
123
153
  {};
154
+ const monorepoManifest = readPackageManifest(monorepoManifestLocation) ?? {};
124
155
  const blockRuntimeManifest = readPackageManifest(blockRuntimeManifestLocation) ??
125
156
  readPackageManifest(installedBlockRuntimeManifestLocation) ??
126
157
  {};
@@ -139,6 +170,20 @@ export function getPackageVersions() {
139
170
  projectToolsPackageVersion: normalizeVersionRange(createManifest.version),
140
171
  restPackageVersion: normalizeVersionRange(createManifest.dependencies?.["@wp-typia/rest"] ??
141
172
  readPackageManifest(installedRestManifestLocation)?.version),
173
+ tsxPackageVersion: normalizeVersionRangeWithFallback(monorepoManifest.dependencies?.tsx ??
174
+ monorepoManifest.devDependencies?.tsx ??
175
+ readPackageManifest(installedTsxManifestLocation)?.version, DEFAULT_TSX_PACKAGE_VERSION),
176
+ typiaPackageVersion: normalizeVersionRangeWithFallback(monorepoManifest.dependencies?.typia ??
177
+ monorepoManifest.devDependencies?.typia ??
178
+ createManifest.dependencies?.typia ??
179
+ readPackageManifest(installedTypiaManifestLocation)?.version, DEFAULT_VERSION_RANGE),
180
+ typiaUnpluginPackageVersion: normalizeVersionRangeWithFallback(monorepoManifest.dependencies?.["@typia/unplugin"] ??
181
+ monorepoManifest.devDependencies?.["@typia/unplugin"] ??
182
+ readPackageManifest(installedTypiaUnpluginManifestLocation)?.version, DEFAULT_TYPIA_UNPLUGIN_PACKAGE_VERSION),
183
+ typescriptPackageVersion: normalizeVersionRangeWithFallback(monorepoManifest.dependencies?.typescript ??
184
+ monorepoManifest.devDependencies?.typescript ??
185
+ createManifest.dependencies?.typescript ??
186
+ readPackageManifest(installedTypescriptManifestLocation)?.version, DEFAULT_VERSION_RANGE),
142
187
  wpTypiaPackageExactVersion: normalizeExactVersion(wpTypiaManifest.version),
143
188
  wpTypiaPackageVersion: normalizeVersionRange(wpTypiaManifest.version),
144
189
  };
@@ -0,0 +1,16 @@
1
+ export type PhpFunctionRange = {
2
+ end: number;
3
+ source: string;
4
+ start: number;
5
+ };
6
+ export type PhpFunctionRangeOptions = {
7
+ includeTrailingNewlines?: boolean;
8
+ };
9
+ export type ReplacePhpFunctionDefinitionOptions = PhpFunctionRangeOptions & {
10
+ trimReplacementStart?: boolean;
11
+ };
12
+ export declare function escapeRegex(value: string): string;
13
+ export declare function quotePhpString(value: string): string;
14
+ export declare function hasPhpFunctionDefinition(source: string, functionName: string): boolean;
15
+ export declare function findPhpFunctionRange(source: string, functionName: string, options?: PhpFunctionRangeOptions): PhpFunctionRange | null;
16
+ export declare function replacePhpFunctionDefinition(source: string, functionName: string, replacement: string, options?: ReplacePhpFunctionDefinitionOptions): string | null;
@@ -0,0 +1,59 @@
1
+ export function escapeRegex(value) {
2
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
3
+ }
4
+ export function quotePhpString(value) {
5
+ return `'${value.replace(/\\/gu, "\\\\").replace(/'/gu, "\\'")}'`;
6
+ }
7
+ export function hasPhpFunctionDefinition(source, functionName) {
8
+ return new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\(`, "u").test(source);
9
+ }
10
+ export function findPhpFunctionRange(source, functionName, options = {}) {
11
+ const signaturePattern = new RegExp(`function\\s+${escapeRegex(functionName)}\\s*\\([^)]*\\)\\s*(?::\\s*[^{};]+)?\\s*\\{`, "u");
12
+ const signatureMatch = signaturePattern.exec(source);
13
+ if (!signatureMatch) {
14
+ return null;
15
+ }
16
+ const functionStart = signatureMatch.index;
17
+ const openBraceOffset = signatureMatch[0].lastIndexOf("{");
18
+ if (openBraceOffset === -1) {
19
+ return null;
20
+ }
21
+ const openBraceIndex = functionStart + openBraceOffset;
22
+ let depth = 0;
23
+ for (let index = openBraceIndex; index < source.length; index += 1) {
24
+ const character = source[index];
25
+ if (character === "{") {
26
+ depth += 1;
27
+ continue;
28
+ }
29
+ if (character !== "}") {
30
+ continue;
31
+ }
32
+ depth -= 1;
33
+ if (depth === 0) {
34
+ let end = index + 1;
35
+ if (options.includeTrailingNewlines ?? true) {
36
+ while (end < source.length && /[\r\n]/u.test(source[end] ?? "")) {
37
+ end += 1;
38
+ }
39
+ }
40
+ return {
41
+ end,
42
+ source: source.slice(functionStart, end),
43
+ start: functionStart,
44
+ };
45
+ }
46
+ }
47
+ return null;
48
+ }
49
+ export function replacePhpFunctionDefinition(source, functionName, replacement, options = {}) {
50
+ const functionRange = findPhpFunctionRange(source, functionName, options);
51
+ if (!functionRange) {
52
+ return null;
53
+ }
54
+ return [
55
+ source.slice(0, functionRange.start),
56
+ options.trimReplacementStart ? replacement.trimStart() : replacement,
57
+ source.slice(functionRange.end),
58
+ ].join("");
59
+ }
@@ -1,8 +1,11 @@
1
1
  import { execSync } from 'node:child_process';
2
+ import path from 'node:path';
2
3
  import { PACKAGE_MANAGER_IDS, getPackageManager, } from './package-managers.js';
3
4
  import { normalizeBlockSlug, resolveScaffoldIdentifiers, validateBlockSlug, validateNamespace, } from './scaffold-identifiers.js';
5
+ import { CLI_DIAGNOSTIC_CODES, createCliDiagnosticCodeError, } from './cli-diagnostics.js';
4
6
  import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, TEMPLATE_IDS, getTemplateById, isBuiltInTemplateId, } from './template-registry.js';
5
7
  import { getRemovedBuiltInTemplateMessage, isRemovedBuiltInTemplateId, } from './template-defaults.js';
8
+ import { parseNpmTemplateLocator } from './template-source-locators.js';
6
9
  import { toSnakeCase, toTitleCase, } from './string-case.js';
7
10
  const WORKSPACE_TEMPLATE_ALIAS = 'workspace';
8
11
  const TEMPLATE_SELECTION_HINT = `--template <${[
@@ -11,6 +14,10 @@ const TEMPLATE_SELECTION_HINT = `--template <${[
11
14
  ].join('|')}|./path|github:owner/repo/path[#ref]|npm-package>`;
12
15
  const TEMPLATE_SUGGESTION_IDS = [...TEMPLATE_IDS, WORKSPACE_TEMPLATE_ALIAS];
13
16
  const QUERY_POST_TYPE_RULE = 'Use lowercase, 1-20 chars, and only a-z, 0-9, "_" or "-".';
17
+ const USER_FACING_TEMPLATE_IDS = [
18
+ ...TEMPLATE_IDS,
19
+ WORKSPACE_TEMPLATE_ALIAS,
20
+ ];
14
21
  /**
15
22
  * Detect the current author name from local Git config.
16
23
  *
@@ -69,7 +76,7 @@ function normalizeQueryPostType(value) {
69
76
  }
70
77
  const validationResult = validateQueryPostType(value);
71
78
  if (validationResult !== true) {
72
- throw new Error(validationResult);
79
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.INVALID_ARGUMENT, validationResult);
73
80
  }
74
81
  return value.trim().toLowerCase();
75
82
  }
@@ -78,14 +85,22 @@ function normalizeTemplateSelection(templateId) {
78
85
  ? OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE
79
86
  : templateId;
80
87
  }
81
- function looksLikeExplicitExternalTemplateLocator(templateId) {
82
- return (templateId.startsWith('./') ||
88
+ function looksLikeWindowsAbsoluteTemplatePath(templateId) {
89
+ return /^[a-z]:[\\/]/iu.test(templateId) || /^\\\\[^\\]+\\[^\\]+/u.test(templateId);
90
+ }
91
+ function looksLikeExplicitNonNpmExternalTemplateLocator(templateId) {
92
+ return (path.isAbsolute(templateId) ||
93
+ looksLikeWindowsAbsoluteTemplatePath(templateId) ||
94
+ templateId.startsWith('./') ||
83
95
  templateId.startsWith('../') ||
84
- templateId.startsWith('/') ||
85
96
  templateId.startsWith('@') ||
86
97
  templateId.startsWith('github:') ||
87
98
  templateId.includes('/'));
88
99
  }
100
+ function looksLikeExplicitExternalTemplateLocator(templateId) {
101
+ return (looksLikeExplicitNonNpmExternalTemplateLocator(templateId) ||
102
+ parseNpmTemplateLocator(templateId) !== null);
103
+ }
89
104
  function getEditDistance(left, right) {
90
105
  const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
91
106
  const current = new Array(right.length + 1);
@@ -104,7 +119,7 @@ function getEditDistance(left, right) {
104
119
  function findMistypedBuiltInTemplateSuggestion(templateId) {
105
120
  const normalizedTemplateId = templateId.trim().toLowerCase();
106
121
  if (normalizedTemplateId.length === 0 ||
107
- looksLikeExplicitExternalTemplateLocator(normalizedTemplateId)) {
122
+ looksLikeExplicitNonNpmExternalTemplateLocator(normalizedTemplateId)) {
108
123
  return null;
109
124
  }
110
125
  let bestCandidate = null;
@@ -132,6 +147,13 @@ function getMistypedBuiltInTemplateMessage(templateId) {
132
147
  : 'built-in scaffold';
133
148
  return `Unknown template "${templateId}". Did you mean "${suggestion}"? Use \`--template ${suggestion}\` for the ${suggestionDescription}, or pass a local path, \`github:owner/repo/path[#ref]\`, or an npm package spec for an external template.`;
134
149
  }
150
+ function getUnknownTemplateMessage(templateId) {
151
+ return [
152
+ `Unknown template "${templateId}". Expected one of: ${USER_FACING_TEMPLATE_IDS.join(', ')}.`,
153
+ 'Run `wp-typia templates list` to inspect available templates.',
154
+ 'Pass an explicit external template locator such as `./path`, `github:owner/repo/path[#ref]`, or `@scope/template` for custom templates.',
155
+ ].join(' ');
156
+ }
135
157
  /**
136
158
  * Resolve the scaffold template id from flags, defaults, and interactive selection.
137
159
  *
@@ -142,7 +164,7 @@ export async function resolveTemplateId({ templateId, yes = false, isInteractive
142
164
  if (templateId) {
143
165
  const normalizedTemplateId = normalizeTemplateSelection(templateId);
144
166
  if (isRemovedBuiltInTemplateId(templateId)) {
145
- throw new Error(getRemovedBuiltInTemplateMessage(templateId));
167
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.UNKNOWN_TEMPLATE, getRemovedBuiltInTemplateMessage(templateId));
146
168
  }
147
169
  if (normalizedTemplateId === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE) {
148
170
  return normalizedTemplateId;
@@ -152,7 +174,10 @@ export async function resolveTemplateId({ templateId, yes = false, isInteractive
152
174
  }
153
175
  const mistypedBuiltInTemplateMessage = getMistypedBuiltInTemplateMessage(templateId);
154
176
  if (mistypedBuiltInTemplateMessage) {
155
- throw new Error(mistypedBuiltInTemplateMessage);
177
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.UNKNOWN_TEMPLATE, mistypedBuiltInTemplateMessage);
178
+ }
179
+ if (!looksLikeExplicitExternalTemplateLocator(normalizedTemplateId)) {
180
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.UNKNOWN_TEMPLATE, getUnknownTemplateMessage(templateId));
156
181
  }
157
182
  return normalizedTemplateId;
158
183
  }
@@ -160,7 +185,7 @@ export async function resolveTemplateId({ templateId, yes = false, isInteractive
160
185
  return 'basic';
161
186
  }
162
187
  if (!isInteractive || !selectTemplate) {
163
- throw new Error(`Template is required in non-interactive mode. Use ${TEMPLATE_SELECTION_HINT}.`);
188
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT, `Template is required in non-interactive mode. Use ${TEMPLATE_SELECTION_HINT}.`);
164
189
  }
165
190
  return normalizeTemplateSelection(await selectTemplate());
166
191
  }
@@ -178,7 +203,7 @@ export async function resolvePackageManagerId({ packageManager, yes = false, isI
178
203
  return 'npm';
179
204
  }
180
205
  if (!isInteractive || !selectPackageManager) {
181
- throw new Error(`Package manager is required in non-interactive mode. Use --package-manager <${PACKAGE_MANAGER_IDS.join('|')}>.`);
206
+ throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT, `Package manager is required in non-interactive mode. Use --package-manager <${PACKAGE_MANAGER_IDS.join('|')}>.`);
182
207
  }
183
208
  return selectPackageManager();
184
209
  }
@@ -190,7 +215,7 @@ export async function resolvePackageManagerId({ packageManager, yes = false, isI
190
215
  */
191
216
  export async function collectScaffoldAnswers({ projectName, templateId, yes = false, dataStorageMode, namespace, persistencePolicy, phpPrefix, promptText, queryPostType, textDomain, }) {
192
217
  const defaults = getDefaultAnswers(projectName, templateId);
193
- if (yes) {
218
+ if (yes || (!isBuiltInTemplateId(templateId) && !promptText)) {
194
219
  const identifiers = resolveScaffoldIdentifiers({
195
220
  namespace: namespace ?? defaults.namespace,
196
221
  phpPrefix,
@@ -2,8 +2,9 @@ import { type BuiltInBlockArtifact } from "./built-in-block-artifacts.js";
2
2
  import type { BuiltInCodeArtifact } from "./built-in-block-code-artifacts.js";
3
3
  import { type BuiltInTemplateId } from "./template-registry.js";
4
4
  import type { PackageManagerId } from "./package-managers.js";
5
+ export { applyWorkspaceMigrationCapability, isOfficialWorkspaceProject, } from "./scaffold-bootstrap.js";
5
6
  import type { ScaffoldProgressEvent, ScaffoldTemplateVariables } from "./scaffold.js";
6
- export { buildGitignore, buildReadme, mergeTextLines, } from "./scaffold-documents.js";
7
+ export { buildGitignore, buildReadme, mergeTextLines } from "./scaffold-documents.js";
7
8
  export interface InstallDependenciesOptions {
8
9
  packageManager: PackageManagerId;
9
10
  projectDir: string;
@@ -27,8 +28,6 @@ export declare function replaceTextRecursively(targetDir: string, packageManager
27
28
  repositoryReference?: string;
28
29
  }): Promise<void>;
29
30
  export declare function defaultInstallDependencies({ projectDir, packageManager, }: InstallDependenciesOptions): Promise<void>;
30
- export declare function isOfficialWorkspaceProject(projectDir: string): boolean;
31
- export declare function applyWorkspaceMigrationCapability(projectDir: string, packageManager: PackageManagerId): Promise<void>;
32
31
  /**
33
32
  * Applies a built-in scaffold into the target directory, including generated
34
33
  * code artifacts, starter manifests, preset files, and placeholder rewrites.
@@ -5,19 +5,17 @@ import { execSync } from "node:child_process";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { applyGeneratedProjectDxPackageJson, applyLocalDevPresetFiles, } from "./local-dev-presets.js";
7
7
  import { applyMigrationUiCapability } from "./migration-ui-capability.js";
8
- import { getPackageVersions } from "./package-versions.js";
9
- import { ensureMigrationDirectories, writeInitialMigrationScaffold, writeMigrationConfig, } from "./migration-project.js";
10
8
  import { syncPersistenceRestArtifacts, } from "./persistence-rest-artifacts.js";
11
9
  import { buildGitignore, buildReadme, mergeTextLines, } from "./scaffold-documents.js";
12
10
  import { getStarterManifestFiles, stringifyStarterManifest, } from "./starter-manifests.js";
13
11
  import { formatNonEmptyTargetDirectoryError, } from "./scaffold-bootstrap.js";
14
12
  import { stringifyBuiltInBlockJsonDocument, } from "./built-in-block-artifacts.js";
15
- import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, } from "./template-registry.js";
16
13
  import { copyInterpolatedDirectory } from "./template-render.js";
17
- import { formatInstallCommand, formatPackageExecCommand, transformPackageManagerText, } from "./package-managers.js";
14
+ import { formatInstallCommand, transformPackageManagerText, } from "./package-managers.js";
18
15
  import { normalizePackageJson } from "./scaffold-package-manager-files.js";
16
+ export { applyWorkspaceMigrationCapability, isOfficialWorkspaceProject, } from "./scaffold-bootstrap.js";
19
17
  import { replaceRepositoryReferencePlaceholders, resolveScaffoldRepositoryReference, } from "./scaffold-repository-reference.js";
20
- export { buildGitignore, buildReadme, mergeTextLines, } from "./scaffold-documents.js";
18
+ export { buildGitignore, buildReadme, mergeTextLines } from "./scaffold-documents.js";
21
19
  async function reportScaffoldProgress(onProgress, event) {
22
20
  await onProgress?.(event);
23
21
  }
@@ -208,44 +206,6 @@ export async function defaultInstallDependencies({ projectDir, packageManager, }
208
206
  stdio: "inherit",
209
207
  });
210
208
  }
211
- export function isOfficialWorkspaceProject(projectDir) {
212
- const packageJsonPath = path.join(projectDir, "package.json");
213
- if (!fs.existsSync(packageJsonPath)) {
214
- return false;
215
- }
216
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
217
- return (packageJson.wpTypia?.projectType === "workspace" &&
218
- packageJson.wpTypia?.templatePackage === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE);
219
- }
220
- export async function applyWorkspaceMigrationCapability(projectDir, packageManager) {
221
- const packageJsonPath = path.join(projectDir, "package.json");
222
- const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, "utf8"));
223
- const wpTypiaPackageVersion = getPackageVersions().wpTypiaPackageVersion;
224
- const canonicalCliSpecifier = wpTypiaPackageVersion === "^0.0.0"
225
- ? "wp-typia"
226
- : `wp-typia@${wpTypiaPackageVersion.replace(/^[~^]/u, "")}`;
227
- const migrationCli = (args) => formatPackageExecCommand(packageManager, canonicalCliSpecifier, `migrate ${args}`);
228
- packageJson.scripts = {
229
- ...(packageJson.scripts ?? {}),
230
- "migration:init": migrationCli("init --current-migration-version v1"),
231
- "migration:snapshot": migrationCli("snapshot"),
232
- "migration:diff": migrationCli("diff"),
233
- "migration:scaffold": migrationCli("scaffold"),
234
- "migration:doctor": migrationCli("doctor --all"),
235
- "migration:fixtures": migrationCli("fixtures --all"),
236
- "migration:verify": migrationCli("verify --all"),
237
- "migration:fuzz": migrationCli("fuzz --all"),
238
- };
239
- await fsp.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, "\t")}\n`, "utf8");
240
- writeMigrationConfig(projectDir, {
241
- blocks: [],
242
- currentMigrationVersion: "v1",
243
- snapshotDir: "src/migrations/versions",
244
- supportedMigrationVersions: ["v1"],
245
- });
246
- ensureMigrationDirectories(projectDir, []);
247
- writeInitialMigrationScaffold(projectDir, "v1", []);
248
- }
249
209
  /**
250
210
  * Applies a built-in scaffold into the target directory, including generated
251
211
  * code artifacts, starter manifests, preset files, and placeholder rewrites.
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Environment variable that disables external template cache reads and writes.
3
+ *
4
+ * Set to `0`, `false`, `no`, or `off` to bypass the cache.
5
+ */
6
+ export declare const EXTERNAL_TEMPLATE_CACHE_ENV = "WP_TYPIA_EXTERNAL_TEMPLATE_CACHE";
7
+ /**
8
+ * Environment variable that overrides the external template cache root.
9
+ */
10
+ export declare const EXTERNAL_TEMPLATE_CACHE_DIR_ENV = "WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_DIR";
11
+ /**
12
+ * Serializable metadata recorded in the cache marker for diagnostics.
13
+ */
14
+ type ExternalTemplateCacheMetadata = Record<string, string | null>;
15
+ /**
16
+ * Describes a deterministic external template cache entry.
17
+ *
18
+ * `namespace` scopes independent cache families, `keyParts` identify the exact
19
+ * source/integrity tuple, and `metadata` is persisted to the marker file.
20
+ */
21
+ export interface ExternalTemplateCacheDescriptor {
22
+ /**
23
+ * Ordered values that deterministically identify one cached template source.
24
+ */
25
+ keyParts: readonly string[];
26
+ /**
27
+ * Diagnostic values persisted to the cache marker after sanitization.
28
+ */
29
+ metadata: ExternalTemplateCacheMetadata;
30
+ /**
31
+ * Cache family scope, stored as a single safe directory segment.
32
+ */
33
+ namespace: string;
34
+ }
35
+ /**
36
+ * Result returned when a cache entry is reused or populated.
37
+ */
38
+ export interface ExternalTemplateCacheResolution {
39
+ /**
40
+ * Whether the returned source directory came from an existing cache entry.
41
+ */
42
+ cacheHit: boolean;
43
+ /**
44
+ * Populated or reused template source directory.
45
+ */
46
+ sourceDir: string;
47
+ }
48
+ /**
49
+ * Metadata-only lookup descriptor for finding an existing reusable cache entry.
50
+ */
51
+ export interface ExternalTemplateCacheLookupDescriptor {
52
+ /**
53
+ * Metadata fields that must match the sanitized marker metadata.
54
+ */
55
+ metadata: ExternalTemplateCacheMetadata;
56
+ /**
57
+ * Cache family scope, stored as a single safe directory segment.
58
+ */
59
+ namespace: string;
60
+ }
61
+ /**
62
+ * Checks whether remote external template source caching is enabled.
63
+ *
64
+ * Caching is enabled by default. Set `WP_TYPIA_EXTERNAL_TEMPLATE_CACHE` to
65
+ * `0`, `false`, `no`, or `off` to force uncached resolution.
66
+ *
67
+ * @param env Environment object to inspect, defaulting to `process.env`.
68
+ * @returns Whether external template source cache reads and writes are enabled.
69
+ */
70
+ export declare function isExternalTemplateCacheEnabled(env?: NodeJS.ProcessEnv): boolean;
71
+ /**
72
+ * Resolves the external template source cache root directory.
73
+ *
74
+ * `WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_DIR` overrides the location. Without an
75
+ * override, wp-typia uses a per-user `wp-typia-template-source-cache-*`
76
+ * directory inside the operating system temp directory.
77
+ *
78
+ * @param env Environment object to inspect, defaulting to `process.env`.
79
+ * @returns Absolute cache root directory path.
80
+ */
81
+ export declare function getExternalTemplateCacheRoot(env?: NodeJS.ProcessEnv): string;
82
+ /**
83
+ * Creates a deterministic cache key from source identity and integrity parts.
84
+ *
85
+ * @param keyParts Ordered values that identify one cached template source.
86
+ * @returns SHA-256 hex digest of the JSON-serialized key parts.
87
+ */
88
+ export declare function createExternalTemplateCacheKey(keyParts: readonly string[]): string;
89
+ /**
90
+ * Finds a reusable cache entry whose marker metadata includes the expected fields.
91
+ *
92
+ * This lookup is intended for resilient fallbacks where a caller cannot compute
93
+ * the exact deterministic key but can safely reuse a previously validated local
94
+ * cache entry for the same source identity.
95
+ *
96
+ * @param descriptor Cache namespace and marker metadata fields to match.
97
+ * @returns Existing cache resolution details, or `null` when no safe entry exists.
98
+ */
99
+ export declare function findReusableExternalTemplateSourceCache(descriptor: ExternalTemplateCacheLookupDescriptor): Promise<ExternalTemplateCacheResolution | null>;
100
+ /**
101
+ * Resolves or populates a cached external template source directory.
102
+ *
103
+ * Returns `null` when caching is disabled. Cache misses populate a temporary
104
+ * directory first and then atomically move it into place; concurrent writers
105
+ * that lose the race reuse the completed marker/source pair.
106
+ *
107
+ * @param descriptor Namespace, key parts, and metadata for the cache entry.
108
+ * @param populateSourceDir Callback that writes the guarded source on a miss.
109
+ * @returns Cache resolution details, or `null` when caching is disabled.
110
+ */
111
+ export declare function resolveExternalTemplateSourceCache(descriptor: ExternalTemplateCacheDescriptor, populateSourceDir: (sourceDir: string) => Promise<void>): Promise<ExternalTemplateCacheResolution | null>;
112
+ export {};