@wp-typia/project-tools 0.22.7 → 0.22.9
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.
- package/dist/runtime/block-targets.d.ts +40 -0
- package/dist/runtime/block-targets.js +71 -0
- package/dist/runtime/built-in-block-artifact-types.js +2 -1
- package/dist/runtime/built-in-block-attribute-specs.js +2 -1
- package/dist/runtime/built-in-block-code-artifacts.js +2 -0
- package/dist/runtime/built-in-block-non-ts-family-artifacts.js +12 -9
- package/dist/runtime/built-in-block-non-ts-render-utils.js +2 -0
- package/dist/runtime/cli-add-block-config.js +2 -1
- package/dist/runtime/cli-add-block.js +16 -4
- package/dist/runtime/cli-add-types.d.ts +8 -0
- package/dist/runtime/cli-add-types.js +11 -0
- package/dist/runtime/cli-add-workspace-ability-scaffold.js +21 -15
- package/dist/runtime/cli-add-workspace-ability.js +2 -2
- package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +17 -13
- package/dist/runtime/cli-add-workspace-admin-view.js +2 -2
- package/dist/runtime/cli-add-workspace-ai.js +2 -2
- package/dist/runtime/cli-add-workspace-assets.js +42 -48
- package/dist/runtime/cli-add-workspace-rest.js +2 -2
- package/dist/runtime/cli-add-workspace.js +6 -38
- package/dist/runtime/cli-add.d.ts +3 -2
- package/dist/runtime/cli-add.js +2 -2
- package/dist/runtime/cli-core.d.ts +4 -2
- package/dist/runtime/cli-core.js +3 -2
- package/dist/runtime/cli-diagnostics.js +6 -0
- package/dist/runtime/cli-doctor-workspace.js +2 -0
- package/dist/runtime/cli-scaffold.js +5 -4
- package/dist/runtime/create-template-validation.d.ts +10 -0
- package/dist/runtime/create-template-validation.js +95 -0
- package/dist/runtime/id-suggestions.d.ts +21 -0
- package/dist/runtime/id-suggestions.js +48 -0
- package/dist/runtime/index.d.ts +5 -2
- package/dist/runtime/index.js +3 -1
- package/dist/runtime/migration-maintenance-verify.js +2 -0
- package/dist/runtime/package-versions.js +15 -2
- package/dist/runtime/php-utils.js +66 -0
- package/dist/runtime/scaffold-answer-resolution.js +5 -108
- package/dist/runtime/scaffold-apply-utils.js +3 -2
- package/dist/runtime/scaffold-identifiers.js +4 -3
- package/dist/runtime/scaffold-template-assertions.d.ts +6 -0
- package/dist/runtime/scaffold-template-assertions.js +33 -0
- package/dist/runtime/scaffold-template-variable-groups.d.ts +2 -0
- package/dist/runtime/scaffold-template-variable-groups.js +7 -0
- package/dist/runtime/scaffold.js +3 -3
- package/dist/runtime/string-case.d.ts +2 -4
- package/dist/runtime/string-case.js +13 -4
- package/dist/runtime/template-builtins.js +11 -8
- package/dist/runtime/template-layers.js +2 -2
- package/dist/runtime/template-source-cache-policy.d.ts +53 -0
- package/dist/runtime/template-source-cache-policy.js +135 -0
- package/dist/runtime/template-source-cache.d.ts +1 -45
- package/dist/runtime/template-source-cache.js +9 -152
- package/dist/runtime/template-source-external.d.ts +3 -0
- package/dist/runtime/template-source-external.js +5 -2
- package/dist/runtime/template-source-remote.d.ts +6 -0
- package/dist/runtime/template-source-remote.js +8 -2
- package/dist/runtime/template-source-seeds.d.ts +13 -0
- package/dist/runtime/template-source-seeds.js +36 -8
- package/dist/runtime/template-source.js +2 -2
- package/dist/runtime/ts-property-names.d.ts +11 -0
- package/dist/runtime/ts-property-names.js +16 -0
- package/dist/runtime/workspace-inventory.d.ts +33 -7
- package/dist/runtime/workspace-inventory.js +94 -41
- package/package.json +11 -1
package/dist/runtime/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
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
|
+
* `getWorkspaceBlockSelectOptionsAsync`,
|
|
8
9
|
* `runAddBlockCommand`, `runAddBlockStyleCommand`,
|
|
9
10
|
* `runAddBlockTransformCommand`, `runAddVariationCommand`,
|
|
10
11
|
* `runAddPatternCommand`, `runAddBindingSourceCommand`,
|
|
@@ -29,11 +30,13 @@ export { manifestAttributeToJsonSchema, projectJsonSchemaDocument, manifestToJso
|
|
|
29
30
|
export { buildCompoundChildStarterManifestDocument, getStarterManifestFiles, stringifyStarterManifest, } from "./starter-manifests.js";
|
|
30
31
|
export type { EndpointAuthIntent, EndpointOpenApiAuthMode, EndpointOpenApiContractDocument, EndpointOpenApiDocumentOptions, EndpointOpenApiEndpointDefinition, EndpointOpenApiMethod, EndpointWordPressAuthDefinition, EndpointWordPressAuthMechanism, JsonSchemaDocument, JsonSchemaProjectionProfile, JsonSchemaObject, NormalizedEndpointAuthDefinition, OpenApiDocument, OpenApiInfo, OpenApiOperation, OpenApiParameter, OpenApiPathItem, OpenApiSecurityScheme, } from "./schema-core.js";
|
|
31
32
|
export { PACKAGE_MANAGER_IDS, PACKAGE_MANAGERS, formatPackageExecCommand, formatInstallCommand, formatRunScript, getPackageManager, getPackageManagerSelectOptions, transformPackageManagerText, } from "./package-managers.js";
|
|
33
|
+
export { suggestCloseId } from "./id-suggestions.js";
|
|
34
|
+
export type { SuggestCloseIdOptions } from "./id-suggestions.js";
|
|
32
35
|
export { clearPackageVersionsCache, getPackageVersions, invalidatePackageVersionsCache, } from "./package-versions.js";
|
|
33
36
|
export type { PackageVersions } from "./package-versions.js";
|
|
34
37
|
export { TEMPLATE_IDS, TEMPLATE_REGISTRY, getTemplateById, getTemplateSelectOptions, listTemplates, } from "./template-registry.js";
|
|
35
38
|
export { EXTERNAL_TEMPLATE_CACHE_TTL_DAYS_ENV, pruneExternalTemplateCache, } from "./template-source-cache.js";
|
|
36
39
|
export type { ExternalTemplateCachePruneOptions, ExternalTemplateCachePruneResult, } from "./template-source-cache.js";
|
|
37
40
|
export { STALE_TEMP_ROOT_MAX_AGE_MS, WP_TYPIA_TEMP_ROOT_PREFIX, cleanupManagedTempRoot, cleanupStaleTempRoots, createManagedTempRoot, getTrackedTempRoots, } from "./temp-roots.js";
|
|
38
|
-
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";
|
|
39
|
-
export type { CliDiagnosticCode, CliDiagnosticCodeError, CliDiagnosticMessage, DoctorCheck, EditorPluginSlotId, HookedBlockPositionId, ReadlinePrompt, } from "./cli-core.js";
|
|
41
|
+
export { createReadlinePrompt, createCliCommandError, createCliDiagnosticCodeError, CliDiagnosticError, CLI_DIAGNOSTIC_CODES, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, getWorkspaceBlockSelectOptionsAsync, 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";
|
|
42
|
+
export type { CliDiagnosticCode, CliDiagnosticCodeError, CliDiagnosticMessage, DoctorCheck, EditorPluginSlotId, HookedBlockPositionId, ReadlinePrompt, WorkspaceBlockSelectOption, } from "./cli-core.js";
|
package/dist/runtime/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
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
|
+
* `getWorkspaceBlockSelectOptionsAsync`,
|
|
8
9
|
* `runAddBlockCommand`, `runAddBlockStyleCommand`,
|
|
9
10
|
* `runAddBlockTransformCommand`, `runAddVariationCommand`,
|
|
10
11
|
* `runAddPatternCommand`, `runAddBindingSourceCommand`,
|
|
@@ -23,8 +24,9 @@ export { parseWorkspacePackageManagerId, resolveWorkspaceProject, tryResolveWork
|
|
|
23
24
|
export { manifestAttributeToJsonSchema, projectJsonSchemaDocument, manifestToJsonSchema, manifestToOpenApi, normalizeEndpointAuthDefinition, } from "./schema-core.js";
|
|
24
25
|
export { buildCompoundChildStarterManifestDocument, getStarterManifestFiles, stringifyStarterManifest, } from "./starter-manifests.js";
|
|
25
26
|
export { PACKAGE_MANAGER_IDS, PACKAGE_MANAGERS, formatPackageExecCommand, formatInstallCommand, formatRunScript, getPackageManager, getPackageManagerSelectOptions, transformPackageManagerText, } from "./package-managers.js";
|
|
27
|
+
export { suggestCloseId } from "./id-suggestions.js";
|
|
26
28
|
export { clearPackageVersionsCache, getPackageVersions, invalidatePackageVersionsCache, } from "./package-versions.js";
|
|
27
29
|
export { TEMPLATE_IDS, TEMPLATE_REGISTRY, getTemplateById, getTemplateSelectOptions, listTemplates, } from "./template-registry.js";
|
|
28
30
|
export { EXTERNAL_TEMPLATE_CACHE_TTL_DAYS_ENV, pruneExternalTemplateCache, } from "./template-source-cache.js";
|
|
29
31
|
export { STALE_TEMP_ROOT_MAX_AGE_MS, WP_TYPIA_TEMP_ROOT_PREFIX, cleanupManagedTempRoot, cleanupStaleTempRoots, createManagedTempRoot, getTrackedTempRoots, } from "./temp-roots.js";
|
|
30
|
-
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";
|
|
32
|
+
export { createReadlinePrompt, createCliCommandError, createCliDiagnosticCodeError, CliDiagnosticError, CLI_DIAGNOSTIC_CODES, formatCliDiagnosticError, formatAddHelpText, formatDoctorCheckLine, formatDoctorSummaryLine, formatHelpText, formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getDoctorChecks, getDoctorFailureDetailLines, getFailingDoctorChecks, getNextSteps, getOptionalOnboarding, getWorkspaceBlockSelectOptions, getWorkspaceBlockSelectOptionsAsync, 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";
|
|
@@ -76,6 +76,8 @@ function recordWorkspaceMigrationTargetAlignment(projectDir, state, recordCheck)
|
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
78
|
try {
|
|
79
|
+
// Maintenance verification is intentionally synchronous so it can compare
|
|
80
|
+
// migration state against a single inventory snapshot.
|
|
79
81
|
const inventory = readWorkspaceInventory(workspace.projectDir);
|
|
80
82
|
const expectedTargets = inventory.blocks.map((block) => `${workspace.workspace.namespace}/${block.slug}`);
|
|
81
83
|
const configuredTargets = state.blocks.map((block) => block.blockName);
|
|
@@ -58,10 +58,23 @@ function createContentFingerprint(source) {
|
|
|
58
58
|
}
|
|
59
59
|
return (hash >>> 0).toString(16);
|
|
60
60
|
}
|
|
61
|
+
function readPackageManifestFile(packageJsonPath) {
|
|
62
|
+
const fileDescriptor = fs.openSync(packageJsonPath, 'r');
|
|
63
|
+
try {
|
|
64
|
+
// Keep cache metadata and manifest contents tied to one opened file, so a
|
|
65
|
+
// concurrent path replacement cannot mix stats from one manifest with
|
|
66
|
+
// contents from another. The content fingerprint remains the final guard.
|
|
67
|
+
const stats = fs.fstatSync(fileDescriptor);
|
|
68
|
+
const source = fs.readFileSync(fileDescriptor, 'utf8');
|
|
69
|
+
return { source, stats };
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
fs.closeSync(fileDescriptor);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
61
75
|
function resolvePackageManifestLocation(packageJsonPath) {
|
|
62
76
|
try {
|
|
63
|
-
const stats =
|
|
64
|
-
const source = fs.readFileSync(packageJsonPath, 'utf8');
|
|
77
|
+
const { source, stats } = readPackageManifestFile(packageJsonPath);
|
|
65
78
|
return {
|
|
66
79
|
cacheKey: `file:${packageJsonPath}:${stats.ino}:${stats.mtimeMs}:${stats.ctimeMs}:${stats.size}:${createContentFingerprint(source)}`,
|
|
67
80
|
packageJsonPath,
|
|
@@ -142,6 +142,9 @@ function matchesPhpFunctionCallAt(source, index, functionName) {
|
|
|
142
142
|
function createPhpScannerState() {
|
|
143
143
|
return {
|
|
144
144
|
heredocDelimiter: "",
|
|
145
|
+
interpolationComment: "",
|
|
146
|
+
interpolationDepth: 0,
|
|
147
|
+
interpolationQuote: "",
|
|
145
148
|
mode: "code",
|
|
146
149
|
};
|
|
147
150
|
}
|
|
@@ -165,11 +168,74 @@ function advancePhpScanner(source, index, state) {
|
|
|
165
168
|
if (character === "\\") {
|
|
166
169
|
return { ambiguous: false, inCode: false, index: index + 2 };
|
|
167
170
|
}
|
|
171
|
+
if (state.mode === "double-quoted" &&
|
|
172
|
+
character === "{" &&
|
|
173
|
+
source[index + 1] === "$") {
|
|
174
|
+
state.mode = "double-quoted-interpolation";
|
|
175
|
+
state.interpolationComment = "";
|
|
176
|
+
state.interpolationDepth = 1;
|
|
177
|
+
state.interpolationQuote = "";
|
|
178
|
+
return { ambiguous: false, inCode: false, index: index + 2 };
|
|
179
|
+
}
|
|
168
180
|
if (character === quote) {
|
|
169
181
|
state.mode = "code";
|
|
170
182
|
}
|
|
171
183
|
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
172
184
|
}
|
|
185
|
+
if (state.mode === "double-quoted-interpolation") {
|
|
186
|
+
if (state.interpolationQuote) {
|
|
187
|
+
if (character === "\\") {
|
|
188
|
+
return { ambiguous: false, inCode: false, index: index + 2 };
|
|
189
|
+
}
|
|
190
|
+
if (character === state.interpolationQuote) {
|
|
191
|
+
state.interpolationQuote = "";
|
|
192
|
+
}
|
|
193
|
+
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
194
|
+
}
|
|
195
|
+
if (state.interpolationComment === "line") {
|
|
196
|
+
if (character === "\r" || character === "\n") {
|
|
197
|
+
state.interpolationComment = "";
|
|
198
|
+
}
|
|
199
|
+
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
200
|
+
}
|
|
201
|
+
if (state.interpolationComment === "block") {
|
|
202
|
+
if (character === "*" && source[index + 1] === "/") {
|
|
203
|
+
state.interpolationComment = "";
|
|
204
|
+
return { ambiguous: false, inCode: false, index: index + 2 };
|
|
205
|
+
}
|
|
206
|
+
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
207
|
+
}
|
|
208
|
+
if (character === "/" && source[index + 1] === "/") {
|
|
209
|
+
state.interpolationComment = "line";
|
|
210
|
+
return { ambiguous: false, inCode: false, index: index + 2 };
|
|
211
|
+
}
|
|
212
|
+
if (character === "#" && source[index + 1] !== "[") {
|
|
213
|
+
state.interpolationComment = "line";
|
|
214
|
+
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
215
|
+
}
|
|
216
|
+
if (character === "/" && source[index + 1] === "*") {
|
|
217
|
+
state.interpolationComment = "block";
|
|
218
|
+
return { ambiguous: false, inCode: false, index: index + 2 };
|
|
219
|
+
}
|
|
220
|
+
if (character === "'" || character === '"') {
|
|
221
|
+
state.interpolationQuote = character;
|
|
222
|
+
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
223
|
+
}
|
|
224
|
+
if (character === "{") {
|
|
225
|
+
state.interpolationDepth += 1;
|
|
226
|
+
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
227
|
+
}
|
|
228
|
+
if (character === "}") {
|
|
229
|
+
state.interpolationDepth -= 1;
|
|
230
|
+
if (state.interpolationDepth <= 0) {
|
|
231
|
+
state.interpolationComment = "";
|
|
232
|
+
state.interpolationDepth = 0;
|
|
233
|
+
state.mode = "double-quoted";
|
|
234
|
+
}
|
|
235
|
+
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
236
|
+
}
|
|
237
|
+
return { ambiguous: false, inCode: false, index: index + 1 };
|
|
238
|
+
}
|
|
173
239
|
if (state.mode === "line-comment") {
|
|
174
240
|
if (character === "\r" || character === "\n") {
|
|
175
241
|
state.mode = "code";
|
|
@@ -1,23 +1,11 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
|
-
import path from 'node:path';
|
|
3
2
|
import { PACKAGE_MANAGER_IDS, getPackageManager, } from './package-managers.js';
|
|
4
3
|
import { normalizeBlockSlug, resolveScaffoldIdentifiers, validateBlockSlug, validateNamespace, } from './scaffold-identifiers.js';
|
|
5
4
|
import { CLI_DIAGNOSTIC_CODES, createCliDiagnosticCodeError, } from './cli-diagnostics.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { parseNpmTemplateLocator } from './template-source-locators.js';
|
|
5
|
+
import { getTemplateById, isBuiltInTemplateId, } from './template-registry.js';
|
|
6
|
+
import { CREATE_TEMPLATE_SELECTION_HINT, validateExplicitCreateTemplateId, } from './create-template-validation.js';
|
|
9
7
|
import { toSnakeCase, toTitleCase, } from './string-case.js';
|
|
10
|
-
const WORKSPACE_TEMPLATE_ALIAS = 'workspace';
|
|
11
|
-
const TEMPLATE_SELECTION_HINT = `--template <${[
|
|
12
|
-
...TEMPLATE_IDS,
|
|
13
|
-
WORKSPACE_TEMPLATE_ALIAS,
|
|
14
|
-
].join('|')}|./path|github:owner/repo/path[#ref]|npm-package>`;
|
|
15
|
-
const TEMPLATE_SUGGESTION_IDS = [...TEMPLATE_IDS, WORKSPACE_TEMPLATE_ALIAS];
|
|
16
8
|
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
|
-
];
|
|
21
9
|
/**
|
|
22
10
|
* Detect the current author name from local Git config.
|
|
23
11
|
*
|
|
@@ -80,80 +68,6 @@ function normalizeQueryPostType(value) {
|
|
|
80
68
|
}
|
|
81
69
|
return value.trim().toLowerCase();
|
|
82
70
|
}
|
|
83
|
-
function normalizeTemplateSelection(templateId) {
|
|
84
|
-
return templateId === WORKSPACE_TEMPLATE_ALIAS
|
|
85
|
-
? OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE
|
|
86
|
-
: templateId;
|
|
87
|
-
}
|
|
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('./') ||
|
|
95
|
-
templateId.startsWith('../') ||
|
|
96
|
-
templateId.startsWith('@') ||
|
|
97
|
-
templateId.startsWith('github:') ||
|
|
98
|
-
templateId.includes('/'));
|
|
99
|
-
}
|
|
100
|
-
function looksLikeExplicitExternalTemplateLocator(templateId) {
|
|
101
|
-
return (looksLikeExplicitNonNpmExternalTemplateLocator(templateId) ||
|
|
102
|
-
parseNpmTemplateLocator(templateId) !== null);
|
|
103
|
-
}
|
|
104
|
-
function getEditDistance(left, right) {
|
|
105
|
-
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
106
|
-
const current = new Array(right.length + 1);
|
|
107
|
-
for (let leftIndex = 0; leftIndex < left.length; leftIndex += 1) {
|
|
108
|
-
current[0] = leftIndex + 1;
|
|
109
|
-
for (let rightIndex = 0; rightIndex < right.length; rightIndex += 1) {
|
|
110
|
-
const substitutionCost = left[leftIndex] === right[rightIndex] ? 0 : 1;
|
|
111
|
-
current[rightIndex + 1] = Math.min(current[rightIndex] + 1, previous[rightIndex + 1] + 1, previous[rightIndex] + substitutionCost);
|
|
112
|
-
}
|
|
113
|
-
for (let index = 0; index < current.length; index += 1) {
|
|
114
|
-
previous[index] = current[index];
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return previous[right.length];
|
|
118
|
-
}
|
|
119
|
-
function findMistypedBuiltInTemplateSuggestion(templateId) {
|
|
120
|
-
const normalizedTemplateId = templateId.trim().toLowerCase();
|
|
121
|
-
if (normalizedTemplateId.length === 0 ||
|
|
122
|
-
looksLikeExplicitNonNpmExternalTemplateLocator(normalizedTemplateId)) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
let bestCandidate = null;
|
|
126
|
-
for (const candidateId of TEMPLATE_SUGGESTION_IDS) {
|
|
127
|
-
const distance = getEditDistance(normalizedTemplateId, candidateId);
|
|
128
|
-
if (bestCandidate === null ||
|
|
129
|
-
distance < bestCandidate.distance) {
|
|
130
|
-
bestCandidate = {
|
|
131
|
-
distance,
|
|
132
|
-
id: candidateId,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return bestCandidate && bestCandidate.distance <= 2
|
|
137
|
-
? bestCandidate.id
|
|
138
|
-
: null;
|
|
139
|
-
}
|
|
140
|
-
function getMistypedBuiltInTemplateMessage(templateId) {
|
|
141
|
-
const suggestion = findMistypedBuiltInTemplateSuggestion(templateId);
|
|
142
|
-
if (!suggestion) {
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
const suggestionDescription = suggestion === WORKSPACE_TEMPLATE_ALIAS
|
|
146
|
-
? 'official workspace scaffold'
|
|
147
|
-
: 'built-in scaffold';
|
|
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.`;
|
|
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
|
-
}
|
|
157
71
|
/**
|
|
158
72
|
* Resolve the scaffold template id from flags, defaults, and interactive selection.
|
|
159
73
|
*
|
|
@@ -162,32 +76,15 @@ function getUnknownTemplateMessage(templateId) {
|
|
|
162
76
|
*/
|
|
163
77
|
export async function resolveTemplateId({ templateId, yes = false, isInteractive = false, selectTemplate, }) {
|
|
164
78
|
if (templateId) {
|
|
165
|
-
|
|
166
|
-
if (isRemovedBuiltInTemplateId(templateId)) {
|
|
167
|
-
throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.UNKNOWN_TEMPLATE, getRemovedBuiltInTemplateMessage(templateId));
|
|
168
|
-
}
|
|
169
|
-
if (normalizedTemplateId === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE) {
|
|
170
|
-
return normalizedTemplateId;
|
|
171
|
-
}
|
|
172
|
-
if (isBuiltInTemplateId(normalizedTemplateId)) {
|
|
173
|
-
return getTemplateById(normalizedTemplateId).id;
|
|
174
|
-
}
|
|
175
|
-
const mistypedBuiltInTemplateMessage = getMistypedBuiltInTemplateMessage(templateId);
|
|
176
|
-
if (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));
|
|
181
|
-
}
|
|
182
|
-
return normalizedTemplateId;
|
|
79
|
+
return validateExplicitCreateTemplateId(templateId);
|
|
183
80
|
}
|
|
184
81
|
if (yes) {
|
|
185
82
|
return 'basic';
|
|
186
83
|
}
|
|
187
84
|
if (!isInteractive || !selectTemplate) {
|
|
188
|
-
throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT, `Template is required in non-interactive mode. Use ${
|
|
85
|
+
throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT, `Template is required in non-interactive mode. Use ${CREATE_TEMPLATE_SELECTION_HINT}.`);
|
|
189
86
|
}
|
|
190
|
-
return
|
|
87
|
+
return validateExplicitCreateTemplateId(await selectTemplate());
|
|
191
88
|
}
|
|
192
89
|
/**
|
|
193
90
|
* Resolve the package manager id from flags, defaults, and interactive selection.
|
|
@@ -15,6 +15,7 @@ import { pathExists, readOptionalUtf8File, } from "./fs-async.js";
|
|
|
15
15
|
import { normalizePackageJson } from "./scaffold-package-manager-files.js";
|
|
16
16
|
export { applyWorkspaceMigrationCapability, isOfficialWorkspaceProject, } from "./scaffold-bootstrap.js";
|
|
17
17
|
import { replaceRepositoryReferencePlaceholders, resolveScaffoldRepositoryReference, } from "./scaffold-repository-reference.js";
|
|
18
|
+
import { isCompoundPersistenceEnabled } from "./scaffold-template-variable-groups.js";
|
|
18
19
|
export { buildGitignore, buildReadme, mergeTextLines } from "./scaffold-documents.js";
|
|
19
20
|
async function reportScaffoldProgress(onProgress, event) {
|
|
20
21
|
await onProgress?.(event);
|
|
@@ -103,7 +104,7 @@ async function withEphemeralScaffoldNodeModules(targetDir, callback) {
|
|
|
103
104
|
*/
|
|
104
105
|
export async function seedBuiltInPersistenceArtifacts(targetDir, templateId, variables) {
|
|
105
106
|
const needsPersistenceArtifacts = templateId === "persistence" ||
|
|
106
|
-
(templateId === "compound" && variables
|
|
107
|
+
(templateId === "compound" && isCompoundPersistenceEnabled(variables));
|
|
107
108
|
if (!needsPersistenceArtifacts) {
|
|
108
109
|
return;
|
|
109
110
|
}
|
|
@@ -256,7 +257,7 @@ export async function applyBuiltInScaffoldProjectFiles({ projectDir, templateDir
|
|
|
256
257
|
await fsp.writeFile(gitignorePath, mergeTextLines(gitignoreContent ?? buildGitignore(), existingGitignore), "utf8");
|
|
257
258
|
await normalizePackageJson(projectDir, packageManager);
|
|
258
259
|
await applyGeneratedProjectDxPackageJson({
|
|
259
|
-
compoundPersistenceEnabled: variables
|
|
260
|
+
compoundPersistenceEnabled: isCompoundPersistenceEnabled(variables),
|
|
260
261
|
packageManager,
|
|
261
262
|
projectDir,
|
|
262
263
|
templateId,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CLI_DIAGNOSTIC_CODES, createCliDiagnosticCodeError, } from "./cli-diagnostics.js";
|
|
1
2
|
import { toKebabCase, toSnakeCase, } from "./string-case.js";
|
|
2
3
|
const BLOCK_SLUG_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
3
4
|
const PHP_PREFIX_PATTERN = /^[a-z_][a-z0-9_]*$/;
|
|
@@ -27,7 +28,7 @@ export function validatePhpPrefix(input) {
|
|
|
27
28
|
export function assertValidIdentifier(label, value, validate) {
|
|
28
29
|
const result = validate(value);
|
|
29
30
|
if (result !== true) {
|
|
30
|
-
throw
|
|
31
|
+
throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.INVALID_ARGUMENT, typeof result === "string" ? `${label}: ${result}` : `${label} is invalid`);
|
|
31
32
|
}
|
|
32
33
|
return value;
|
|
33
34
|
}
|
|
@@ -52,9 +53,9 @@ export function resolveNonEmptyNormalizedBlockSlug(options) {
|
|
|
52
53
|
return normalizedSlug;
|
|
53
54
|
}
|
|
54
55
|
if (options.input.trim().length === 0) {
|
|
55
|
-
throw
|
|
56
|
+
throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.MISSING_ARGUMENT, `${options.label} is required. Use \`${options.usage}\`.`);
|
|
56
57
|
}
|
|
57
|
-
throw
|
|
58
|
+
throw createCliDiagnosticCodeError(CLI_DIAGNOSTIC_CODES.INVALID_ARGUMENT, `${options.label} "${options.input.trim()}" normalizes to an empty slug. Use letters or numbers so wp-typia can generate a block slug.`);
|
|
58
59
|
}
|
|
59
60
|
export function resolveValidatedBlockSlug(value) {
|
|
60
61
|
return assertValidIdentifier("Block slug", normalizeBlockSlug(value), validateBlockSlug);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Revalidates target-language identifiers at the built-in template builder
|
|
3
|
+
* boundary so generated PHP and TypeScript never rely only on upstream CLI
|
|
4
|
+
* normalization.
|
|
5
|
+
*/
|
|
6
|
+
export declare function assertScaffoldTemplateCodeIdentifiers(view: Record<string, unknown>): void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const BLOCK_SLUG_PATTERN = /^[a-z][a-z0-9-]*$/u;
|
|
2
|
+
const PHP_IDENTIFIER_PATTERN = /^[a-z_][a-z0-9_]*$/u;
|
|
3
|
+
const PHP_CONSTANT_IDENTIFIER_PATTERN = /^[A-Z_][A-Z0-9_]*$/u;
|
|
4
|
+
const JAVASCRIPT_IDENTIFIER_PATTERN = /^[A-Za-z_$][\w$]*$/u;
|
|
5
|
+
const QUERY_POST_TYPE_PATTERN = /^[a-z0-9_-]{1,20}$/u;
|
|
6
|
+
function assertOptionalStringPattern(view, key, pattern, description) {
|
|
7
|
+
const value = view[key];
|
|
8
|
+
if (typeof value === "undefined") {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (typeof value !== "string" || !pattern.test(value)) {
|
|
12
|
+
throw new Error(`Unsafe scaffold template variable "${key}" for ${description}: ${JSON.stringify(value)}.`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Revalidates target-language identifiers at the built-in template builder
|
|
17
|
+
* boundary so generated PHP and TypeScript never rely only on upstream CLI
|
|
18
|
+
* normalization.
|
|
19
|
+
*/
|
|
20
|
+
export function assertScaffoldTemplateCodeIdentifiers(view) {
|
|
21
|
+
assertOptionalStringPattern(view, "namespace", BLOCK_SLUG_PATTERN, "block namespace");
|
|
22
|
+
assertOptionalStringPattern(view, "slug", BLOCK_SLUG_PATTERN, "block slug");
|
|
23
|
+
assertOptionalStringPattern(view, "slugKebabCase", BLOCK_SLUG_PATTERN, "block slug");
|
|
24
|
+
assertOptionalStringPattern(view, "textDomain", BLOCK_SLUG_PATTERN, "text domain");
|
|
25
|
+
assertOptionalStringPattern(view, "textdomain", BLOCK_SLUG_PATTERN, "text domain");
|
|
26
|
+
assertOptionalStringPattern(view, "queryPostType", QUERY_POST_TYPE_PATTERN, "query post type");
|
|
27
|
+
assertOptionalStringPattern(view, "phpPrefix", PHP_IDENTIFIER_PATTERN, "PHP identifier");
|
|
28
|
+
assertOptionalStringPattern(view, "slugSnakeCase", PHP_IDENTIFIER_PATTERN, "PHP identifier");
|
|
29
|
+
assertOptionalStringPattern(view, "phpPrefixUpper", PHP_CONSTANT_IDENTIFIER_PATTERN, "PHP constant identifier");
|
|
30
|
+
assertOptionalStringPattern(view, "pascalCase", JAVASCRIPT_IDENTIFIER_PATTERN, "JavaScript identifier");
|
|
31
|
+
assertOptionalStringPattern(view, "slugCamelCase", JAVASCRIPT_IDENTIFIER_PATTERN, "JavaScript identifier");
|
|
32
|
+
assertOptionalStringPattern(view, "titleCase", JAVASCRIPT_IDENTIFIER_PATTERN, "JavaScript identifier");
|
|
33
|
+
}
|
|
@@ -153,4 +153,6 @@ export type PersistenceScaffoldTemplateVariablesLike = ScaffoldTemplateVariableG
|
|
|
153
153
|
};
|
|
154
154
|
export declare function attachScaffoldTemplateVariableGroups<TVariables extends Record<string, string>>(variables: TVariables, groups: ScaffoldTemplateVariableGroups): TVariables & ScaffoldTemplateVariableGroupsCarrier;
|
|
155
155
|
export declare function getScaffoldTemplateVariableGroups(variables: ScaffoldTemplateVariableGroupsCarrier): ScaffoldTemplateVariableGroups;
|
|
156
|
+
export declare function isCompoundPersistenceEnabled(variables: ScaffoldTemplateVariableGroupsCarrier): boolean;
|
|
157
|
+
export declare function getScaffoldAlternateRenderTargets(variables: ScaffoldTemplateVariableGroupsCarrier): ScaffoldAlternateRenderTargetVariableGroup;
|
|
156
158
|
export {};
|
|
@@ -11,3 +11,10 @@ export function attachScaffoldTemplateVariableGroups(variables, groups) {
|
|
|
11
11
|
export function getScaffoldTemplateVariableGroups(variables) {
|
|
12
12
|
return variables[SCAFFOLD_TEMPLATE_VARIABLE_GROUPS];
|
|
13
13
|
}
|
|
14
|
+
export function isCompoundPersistenceEnabled(variables) {
|
|
15
|
+
const compound = getScaffoldTemplateVariableGroups(variables).compound;
|
|
16
|
+
return compound.enabled && compound.persistenceEnabled;
|
|
17
|
+
}
|
|
18
|
+
export function getScaffoldAlternateRenderTargets(variables) {
|
|
19
|
+
return getScaffoldTemplateVariableGroups(variables).alternateRenderTargets;
|
|
20
|
+
}
|
package/dist/runtime/scaffold.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
1
|
import { promises as fsp } from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { getPackageManager } from "./package-managers.js";
|
|
@@ -14,6 +13,7 @@ import { BlockGeneratorService, } from "./block-generator-service.js";
|
|
|
14
13
|
import { getTemplateVariables } from "./scaffold-template-variables.js";
|
|
15
14
|
import { getScaffoldTemplateVariableGroups } from "./scaffold-template-variable-groups.js";
|
|
16
15
|
import { assertExternalLayerCompositionOptions, } from "./cli-validation.js";
|
|
16
|
+
import { pathExists } from "./fs-async.js";
|
|
17
17
|
export const DATA_STORAGE_MODES = ["post-meta", "custom-table"];
|
|
18
18
|
export const PERSISTENCE_POLICIES = ["authenticated", "public"];
|
|
19
19
|
export { buildBlockCssClassName } from "./scaffold-identifiers.js";
|
|
@@ -149,7 +149,7 @@ export async function scaffoldProject({ projectDir, templateId, answers, alterna
|
|
|
149
149
|
title: "Finalizing scaffold output",
|
|
150
150
|
});
|
|
151
151
|
const readmePath = path.join(projectDir, "README.md");
|
|
152
|
-
if (!
|
|
152
|
+
if (!(await pathExists(readmePath))) {
|
|
153
153
|
await fsp.writeFile(readmePath, buildReadme(resolvedTemplateId, variables, resolvedPackageManager, {
|
|
154
154
|
withMigrationUi: isBuiltInTemplate || isWorkspace ? withMigrationUi : false,
|
|
155
155
|
withTestPreset: isBuiltInTemplate ? withTestPreset : false,
|
|
@@ -157,7 +157,7 @@ export async function scaffoldProject({ projectDir, templateId, answers, alterna
|
|
|
157
157
|
}), "utf8");
|
|
158
158
|
}
|
|
159
159
|
const gitignorePath = path.join(projectDir, ".gitignore");
|
|
160
|
-
const existingGitignore =
|
|
160
|
+
const existingGitignore = (await pathExists(gitignorePath))
|
|
161
161
|
? await fsp.readFile(gitignorePath, "utf8")
|
|
162
162
|
: "";
|
|
163
163
|
await fsp.writeFile(gitignorePath, mergeTextLines(buildGitignore(), existingGitignore), "utf8");
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Normalize arbitrary text into a kebab-case identifier.
|
|
3
3
|
* Common acronym runs stay grouped, with a boundary before the next
|
|
4
|
-
* capitalized word
|
|
5
|
-
*
|
|
6
|
-
* word because there is no PascalCase boundary marker before the lowercase
|
|
7
|
-
* suffix.
|
|
4
|
+
* capitalized word or before a narrow allow-list of lowercase WordPress slug
|
|
5
|
+
* terms. Naturalized words such as `RESTful` intentionally stay as one word.
|
|
8
6
|
*
|
|
9
7
|
* @param input Raw text that may contain spaces, punctuation, or camelCase.
|
|
10
8
|
* @returns A lowercase kebab-case string with collapsed separators.
|
|
@@ -6,7 +6,9 @@ const COMMON_ACRONYM_PREFIXES = [
|
|
|
6
6
|
'JSON',
|
|
7
7
|
'REST',
|
|
8
8
|
'UUID',
|
|
9
|
+
'AJAX',
|
|
9
10
|
'API',
|
|
11
|
+
'CPT',
|
|
10
12
|
'CSS',
|
|
11
13
|
'CTA',
|
|
12
14
|
'DOM',
|
|
@@ -20,12 +22,18 @@ const COMMON_ACRONYM_PREFIXES = [
|
|
|
20
22
|
'UI',
|
|
21
23
|
'WP',
|
|
22
24
|
];
|
|
25
|
+
// Keep lowercase suffix splitting narrow so naturalized words such as
|
|
26
|
+
// `RESTful` remain stable while WordPress slug terms like `URLslug` read well.
|
|
27
|
+
const COMMON_ACRONYM_LOWERCASE_SUFFIXES = ['slug'];
|
|
23
28
|
function capitalizeSegment(segment) {
|
|
24
29
|
return segment.charAt(0).toUpperCase() + segment.slice(1);
|
|
25
30
|
}
|
|
26
31
|
function findCommonAcronymPrefix(segment) {
|
|
27
32
|
return COMMON_ACRONYM_PREFIXES.find((prefix) => segment.startsWith(prefix));
|
|
28
33
|
}
|
|
34
|
+
function isCommonAcronymLowercaseSuffix(suffix) {
|
|
35
|
+
return COMMON_ACRONYM_LOWERCASE_SUFFIXES.includes(suffix);
|
|
36
|
+
}
|
|
29
37
|
function splitKnownAcronymSegment(segment) {
|
|
30
38
|
const prefixes = [];
|
|
31
39
|
let remaining = segment;
|
|
@@ -38,6 +46,9 @@ function splitKnownAcronymSegment(segment) {
|
|
|
38
46
|
if (/^[A-Z][a-z]/.test(suffix)) {
|
|
39
47
|
return [...prefixes, prefix, suffix].join('-');
|
|
40
48
|
}
|
|
49
|
+
if (/^[a-z]+$/.test(suffix) && isCommonAcronymLowercaseSuffix(suffix)) {
|
|
50
|
+
return [...prefixes, prefix, suffix].join('-');
|
|
51
|
+
}
|
|
41
52
|
if (!findCommonAcronymPrefix(suffix)) {
|
|
42
53
|
break;
|
|
43
54
|
}
|
|
@@ -52,10 +63,8 @@ function splitAcronymBoundary(value) {
|
|
|
52
63
|
/**
|
|
53
64
|
* Normalize arbitrary text into a kebab-case identifier.
|
|
54
65
|
* Common acronym runs stay grouped, with a boundary before the next
|
|
55
|
-
* capitalized word
|
|
56
|
-
*
|
|
57
|
-
* word because there is no PascalCase boundary marker before the lowercase
|
|
58
|
-
* suffix.
|
|
66
|
+
* capitalized word or before a narrow allow-list of lowercase WordPress slug
|
|
67
|
+
* terms. Naturalized words such as `RESTful` intentionally stay as one word.
|
|
59
68
|
*
|
|
60
69
|
* @param input Raw text that may contain spaces, punctuation, or camelCase.
|
|
61
70
|
* @returns A lowercase kebab-case string with collapsed separators.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import { promises as fsp } from "node:fs";
|
|
3
|
+
import { pathExists } from "./fs-async.js";
|
|
4
4
|
import { createManagedTempRoot } from "./temp-roots.js";
|
|
5
5
|
import { getTemplateById, SHARED_BASE_TEMPLATE_ROOT, SHARED_COMPOUND_TEMPLATE_ROOT, SHARED_MIGRATION_UI_TEMPLATE_ROOT, SHARED_PERSISTENCE_TEMPLATE_ROOT, SHARED_PRESET_TEMPLATE_ROOT, SHARED_REST_HELPER_TEMPLATE_ROOT, SHARED_WORKSPACE_TEMPLATE_ROOT, } from "./template-registry.js";
|
|
6
6
|
const BUILT_IN_SHARED_TEMPLATE_LAYERS = Object.freeze([
|
|
@@ -138,20 +138,23 @@ export function isOmittableBuiltInTemplateLayerDir(templateId, layerDir) {
|
|
|
138
138
|
return (OMITTABLE_BUILT_IN_OVERLAY_TEMPLATE_IDS.has(templateId) &&
|
|
139
139
|
layerDir === getTemplateById(templateId).templateDir);
|
|
140
140
|
}
|
|
141
|
-
function resolveMaterializedTemplateLayerDirs(templateId, layerDirs) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
async function resolveMaterializedTemplateLayerDirs(templateId, layerDirs) {
|
|
142
|
+
const materializedLayerDirs = [];
|
|
143
|
+
for (const layerDir of layerDirs) {
|
|
144
|
+
if (await pathExists(layerDir)) {
|
|
145
|
+
materializedLayerDirs.push(layerDir);
|
|
146
|
+
continue;
|
|
145
147
|
}
|
|
146
148
|
if (isOmittableBuiltInTemplateLayerDir(templateId, layerDir)) {
|
|
147
|
-
|
|
149
|
+
continue;
|
|
148
150
|
}
|
|
149
151
|
throw new Error(`Built-in template layer is missing: ${layerDir}`);
|
|
150
|
-
}
|
|
152
|
+
}
|
|
153
|
+
return materializedLayerDirs;
|
|
151
154
|
}
|
|
152
155
|
async function materializeBuiltInTemplateSource(templateId, layerDirs) {
|
|
153
156
|
const template = getTemplateById(templateId);
|
|
154
|
-
const materializedLayerDirs = resolveMaterializedTemplateLayerDirs(templateId, layerDirs);
|
|
157
|
+
const materializedLayerDirs = await resolveMaterializedTemplateLayerDirs(templateId, layerDirs);
|
|
155
158
|
const { path: tempRoot, cleanup } = await createManagedTempRoot("wp-typia-template-");
|
|
156
159
|
const templateDir = path.join(tempRoot, templateId);
|
|
157
160
|
try {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import { promises as fsp } from "node:fs";
|
|
3
|
+
import { pathExists } from "./fs-async.js";
|
|
4
4
|
import { isPlainObject } from "./object-utils.js";
|
|
5
5
|
import { getBuiltInSharedTemplateLayerDir, isBuiltInSharedTemplateLayerId, } from "./template-builtins.js";
|
|
6
6
|
import { listInterpolatedDirectoryOutputs } from "./template-render.js";
|
|
@@ -56,7 +56,7 @@ function parseLayerDefinition(layerId, value) {
|
|
|
56
56
|
}
|
|
57
57
|
export async function loadExternalTemplateLayerManifest(sourceRoot) {
|
|
58
58
|
const manifestPath = path.join(sourceRoot, TEMPLATE_LAYER_MANIFEST_FILENAME);
|
|
59
|
-
if (!
|
|
59
|
+
if (!(await pathExists(manifestPath))) {
|
|
60
60
|
return null;
|
|
61
61
|
}
|
|
62
62
|
const raw = JSON.parse(await fsp.readFile(manifestPath, "utf8"));
|
|
@@ -0,0 +1,53 @@
|
|
|
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
|
+
* Environment variable that enables TTL-based external template cache pruning.
|
|
13
|
+
*
|
|
14
|
+
* Unset, empty, zero, negative, and non-numeric values keep pruning disabled.
|
|
15
|
+
*/
|
|
16
|
+
export declare const EXTERNAL_TEMPLATE_CACHE_TTL_DAYS_ENV = "WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_TTL_DAYS";
|
|
17
|
+
/**
|
|
18
|
+
* Environment variable that overrides how often TTL pruning may scan the cache.
|
|
19
|
+
*
|
|
20
|
+
* Unset values use the default interval. Zero, negative, and non-numeric values
|
|
21
|
+
* disable scan throttling.
|
|
22
|
+
*/
|
|
23
|
+
export declare const EXTERNAL_TEMPLATE_CACHE_PRUNE_INTERVAL_MS_ENV = "WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_PRUNE_INTERVAL_MS";
|
|
24
|
+
/**
|
|
25
|
+
* Checks whether remote external template source caching is enabled.
|
|
26
|
+
*
|
|
27
|
+
* Caching is enabled by default. Set `WP_TYPIA_EXTERNAL_TEMPLATE_CACHE` to
|
|
28
|
+
* `0`, `false`, `no`, or `off` to force uncached resolution.
|
|
29
|
+
*
|
|
30
|
+
* @param env Environment object to inspect, defaulting to `process.env`.
|
|
31
|
+
* @returns Whether external template source cache reads and writes are enabled.
|
|
32
|
+
*/
|
|
33
|
+
export declare function isExternalTemplateCacheEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Resolves the external template source cache root directory.
|
|
36
|
+
*
|
|
37
|
+
* `WP_TYPIA_EXTERNAL_TEMPLATE_CACHE_DIR` overrides the location. Without an
|
|
38
|
+
* override, wp-typia uses a per-user `wp-typia-template-source-cache-*`
|
|
39
|
+
* directory inside the operating system temp directory.
|
|
40
|
+
*
|
|
41
|
+
* @param env Environment object to inspect, defaulting to `process.env`.
|
|
42
|
+
* @returns Absolute cache root directory path.
|
|
43
|
+
*/
|
|
44
|
+
export declare function getExternalTemplateCacheRoot(env?: NodeJS.ProcessEnv): string;
|
|
45
|
+
export declare function resolveExternalTemplateCacheTtlMs(options?: {
|
|
46
|
+
env?: NodeJS.ProcessEnv;
|
|
47
|
+
ttlDays?: number;
|
|
48
|
+
}): number | null;
|
|
49
|
+
export declare function resolveExternalTemplateCachePruneIntervalMs(options?: {
|
|
50
|
+
env?: NodeJS.ProcessEnv;
|
|
51
|
+
pruneIntervalMs?: number;
|
|
52
|
+
}): number | null;
|
|
53
|
+
export declare function getExternalTemplateCacheNowMs(now: Date | number | undefined): number;
|