@wp-typia/project-tools 0.22.8 → 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/cli-add-block.js +12 -1
- package/dist/runtime/cli-add-types.d.ts +8 -0
- package/dist/runtime/cli-add-types.js +11 -0
- 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-doctor-workspace.js +2 -0
- package/dist/runtime/cli-scaffold.js +3 -3
- package/dist/runtime/create-template-validation.js +2 -28
- 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/scaffold.js +3 -3
- package/dist/runtime/template-builtins.js +11 -8
- package/dist/runtime/template-layers.js +2 -2
- 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/workspace-inventory.d.ts +20 -6
- package/dist/runtime/workspace-inventory.js +59 -32
- package/package.json +6 -1
|
@@ -9,7 +9,7 @@ import { copyInterpolatedDirectory, listInterpolatedDirectoryOutputs, } from "./
|
|
|
9
9
|
import { appendWorkspaceInventoryEntries, } from "./workspace-inventory.js";
|
|
10
10
|
import { createManagedTempRoot } from "./temp-roots.js";
|
|
11
11
|
import { resolveWorkspaceProject, } from "./workspace-project.js";
|
|
12
|
-
import { ADD_BLOCK_TEMPLATE_IDS, buildWorkspacePhpPrefix, isAddBlockTemplateId, patchFile, readOptionalFile, rollbackWorkspaceMutation, snapshotWorkspaceFiles, } from "./cli-add-shared.js";
|
|
12
|
+
import { ADD_BLOCK_TEMPLATE_IDS, buildWorkspacePhpPrefix, isAddBlockTemplateId, patchFile, readOptionalFile, rollbackWorkspaceMutation, snapshotWorkspaceFiles, suggestAddBlockTemplateId, } from "./cli-add-shared.js";
|
|
13
13
|
import { resolveNonEmptyNormalizedBlockSlug, } from "./scaffold-identifiers.js";
|
|
14
14
|
import { buildConfigEntries, buildMigrationBlocks, buildServerTemplateRoot, } from "./cli-add-block-config.js";
|
|
15
15
|
import { COMPOUND_SHARED_SUPPORT_FILES, collectLegacyCompoundValidatorPaths, ensureBlockConfigCanAddRestManifests, ensureCompoundWorkspaceSupportFiles, } from "./cli-add-block-legacy-validator.js";
|
|
@@ -80,6 +80,13 @@ async function assertWorkspaceDependenciesInstalled(workspace) {
|
|
|
80
80
|
}
|
|
81
81
|
throw new Error(`Workspace dependencies have not been installed yet. Run \`${formatInstallCommand(workspace.packageManager)}\` from the workspace root before using \`wp-typia add block ...\`.`);
|
|
82
82
|
}
|
|
83
|
+
function getMistypedAddBlockTemplateMessage(templateId) {
|
|
84
|
+
const suggestion = suggestAddBlockTemplateId(templateId);
|
|
85
|
+
if (!suggestion) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
return `Unknown add-block template "${templateId}". Did you mean "${suggestion}"? Use \`--template ${suggestion}\`, or run \`wp-typia templates list\` to inspect available templates.`;
|
|
89
|
+
}
|
|
83
90
|
async function copyScaffoldedBlockSlice(projectDir, templateId, tempProjectDir, variables, legacyValidatorPaths = []) {
|
|
84
91
|
if (templateId === "compound") {
|
|
85
92
|
await ensureCompoundWorkspaceSupportFiles(projectDir, tempProjectDir, legacyValidatorPaths);
|
|
@@ -287,6 +294,10 @@ export async function runAddBlockCommand({ alternateRenderTargets, blockName, cw
|
|
|
287
294
|
throw new Error("`wp-typia add block --template query-loop` is not supported. Query Loop is a create-time `core/query` variation scaffold, so use `wp-typia create <project-dir> --template query-loop` instead.");
|
|
288
295
|
}
|
|
289
296
|
if (!isAddBlockTemplateId(templateId)) {
|
|
297
|
+
const mistypedAddBlockTemplateMessage = getMistypedAddBlockTemplateMessage(templateId);
|
|
298
|
+
if (mistypedAddBlockTemplateMessage) {
|
|
299
|
+
throw new Error(mistypedAddBlockTemplateMessage);
|
|
300
|
+
}
|
|
290
301
|
throw new Error(`Unknown add-block template "${templateId}". Expected one of: ${ADD_BLOCK_TEMPLATE_IDS.join(", ")}. Run \`wp-typia templates list\` to inspect available templates.`);
|
|
291
302
|
}
|
|
292
303
|
const resolvedTemplateId = templateId;
|
|
@@ -42,6 +42,14 @@ export declare const ADD_BLOCK_TEMPLATE_IDS: readonly ["basic", "interactivity",
|
|
|
42
42
|
* Union of supported built-in block template ids.
|
|
43
43
|
*/
|
|
44
44
|
export type AddBlockTemplateId = (typeof ADD_BLOCK_TEMPLATE_IDS)[number];
|
|
45
|
+
/**
|
|
46
|
+
* Suggest the closest supported add-block template id for typo diagnostics.
|
|
47
|
+
*
|
|
48
|
+
* @param templateId Raw `wp-typia add block --template` value.
|
|
49
|
+
* @returns The closest supported template id when it is within the shared
|
|
50
|
+
* close-id threshold, otherwise `null`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function suggestAddBlockTemplateId(templateId: string): AddBlockTemplateId | null;
|
|
45
53
|
/**
|
|
46
54
|
* Options for `wp-typia add variation`.
|
|
47
55
|
*
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { suggestCloseId } from "./id-suggestions.js";
|
|
1
2
|
export { ADD_KIND_IDS } from "./cli-add-kind-ids.js";
|
|
2
3
|
/**
|
|
3
4
|
* Supported plugin-level REST resource methods accepted by
|
|
@@ -46,3 +47,13 @@ export const ADD_BLOCK_TEMPLATE_IDS = [
|
|
|
46
47
|
"persistence",
|
|
47
48
|
"compound",
|
|
48
49
|
];
|
|
50
|
+
/**
|
|
51
|
+
* Suggest the closest supported add-block template id for typo diagnostics.
|
|
52
|
+
*
|
|
53
|
+
* @param templateId Raw `wp-typia add block --template` value.
|
|
54
|
+
* @returns The closest supported template id when it is within the shared
|
|
55
|
+
* close-id threshold, otherwise `null`.
|
|
56
|
+
*/
|
|
57
|
+
export function suggestAddBlockTemplateId(templateId) {
|
|
58
|
+
return suggestCloseId(templateId, ADD_BLOCK_TEMPLATE_IDS);
|
|
59
|
+
}
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
* - `cli-add-block` for built-in block scaffolding
|
|
8
8
|
* - `cli-add-workspace` for workspace mutation commands
|
|
9
9
|
*/
|
|
10
|
-
export { ADD_BLOCK_TEMPLATE_IDS, ADD_KIND_IDS, EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, isAddBlockTemplateId, } from "./cli-add-shared.js";
|
|
10
|
+
export { ADD_BLOCK_TEMPLATE_IDS, ADD_KIND_IDS, EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, isAddBlockTemplateId, suggestAddBlockTemplateId, } from "./cli-add-shared.js";
|
|
11
11
|
export type { AddBlockTemplateId, AddKindId, EditorPluginSlotId, } from "./cli-add-shared.js";
|
|
12
12
|
export { runAddBlockCommand, seedWorkspaceMigrationProject, } from "./cli-add-block.js";
|
|
13
13
|
export { runAddAdminViewCommand, runAddAbilityCommand, runAddAiFeatureCommand, runAddBindingSourceCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, } from "./cli-add-workspace.js";
|
|
14
|
-
export { getWorkspaceBlockSelectOptions } from "./workspace-inventory.js";
|
|
14
|
+
export { getWorkspaceBlockSelectOptions, getWorkspaceBlockSelectOptionsAsync, } from "./workspace-inventory.js";
|
|
15
|
+
export type { WorkspaceBlockSelectOption } from "./workspace-inventory.js";
|
package/dist/runtime/cli-add.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - `cli-add-block` for built-in block scaffolding
|
|
8
8
|
* - `cli-add-workspace` for workspace mutation commands
|
|
9
9
|
*/
|
|
10
|
-
export { ADD_BLOCK_TEMPLATE_IDS, ADD_KIND_IDS, EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, isAddBlockTemplateId, } from "./cli-add-shared.js";
|
|
10
|
+
export { ADD_BLOCK_TEMPLATE_IDS, ADD_KIND_IDS, EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, isAddBlockTemplateId, suggestAddBlockTemplateId, } from "./cli-add-shared.js";
|
|
11
11
|
export { runAddBlockCommand, seedWorkspaceMigrationProject, } from "./cli-add-block.js";
|
|
12
12
|
export { runAddAdminViewCommand, runAddAbilityCommand, runAddAiFeatureCommand, runAddBindingSourceCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, } from "./cli-add-workspace.js";
|
|
13
|
-
export { getWorkspaceBlockSelectOptions } from "./workspace-inventory.js";
|
|
13
|
+
export { getWorkspaceBlockSelectOptions, getWorkspaceBlockSelectOptionsAsync, } from "./workspace-inventory.js";
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* `runAddAdminViewCommand` for DataViews-backed admin screen scaffolds,
|
|
12
12
|
* `runAddAbilityCommand` for typed workflow ability scaffolds,
|
|
13
13
|
* and `HOOKED_BLOCK_POSITION_IDS`,
|
|
14
|
-
* `getWorkspaceBlockSelectOptions`,
|
|
14
|
+
* `getWorkspaceBlockSelectOptions`, `getWorkspaceBlockSelectOptionsAsync`, and
|
|
15
|
+
* `seedWorkspaceMigrationProject` for
|
|
15
16
|
* explicit `wp-typia add` flows,
|
|
16
17
|
* `runAddAiFeatureCommand` for server-owned WordPress AI feature scaffolds,
|
|
17
18
|
* `runAddRestResourceCommand` for plugin-level REST resource scaffolds,
|
|
@@ -31,11 +32,12 @@
|
|
|
31
32
|
export { getDoctorChecks, runDoctor, type DoctorCheck } from "./cli-doctor.js";
|
|
32
33
|
export { createCliCommandError, createCliDiagnosticCodeError, CliDiagnosticError, CLI_DIAGNOSTIC_CODE_METADATA, CLI_DIAGNOSTIC_CODES, formatCliDiagnosticError, formatDoctorCheckLine, formatDoctorSummaryLine, getCliDiagnosticCodeMetadata, getDoctorFailureDetailLines, getFailingDoctorChecks, isCliDiagnosticError, } from "./cli-diagnostics.js";
|
|
33
34
|
export type { CliDiagnosticCode, CliDiagnosticCodeError, CliDiagnosticMessage, } from "./cli-diagnostics.js";
|
|
34
|
-
export { EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, getWorkspaceBlockSelectOptions, runAddAdminViewCommand, runAddAbilityCommand, runAddBindingSourceCommand, runAddAiFeatureCommand, runAddBlockCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, seedWorkspaceMigrationProject, } from "./cli-add.js";
|
|
35
|
+
export { EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, getWorkspaceBlockSelectOptions, getWorkspaceBlockSelectOptionsAsync, runAddAdminViewCommand, runAddAbilityCommand, runAddBindingSourceCommand, runAddAiFeatureCommand, runAddBlockCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, seedWorkspaceMigrationProject, } from "./cli-add.js";
|
|
35
36
|
export { COMPOUND_INNER_BLOCKS_PRESET_IDS, getCompoundInnerBlocksPresetDefinition, } from "./compound-inner-blocks.js";
|
|
36
37
|
export type { CompoundInnerBlocksPresetId } from "./compound-inner-blocks.js";
|
|
37
38
|
export { HOOKED_BLOCK_POSITION_IDS } from "./hooked-blocks.js";
|
|
38
39
|
export type { EditorPluginSlotId } from "./cli-add.js";
|
|
40
|
+
export type { WorkspaceBlockSelectOption } from "./workspace-inventory.js";
|
|
39
41
|
export type { HookedBlockPositionId } from "./hooked-blocks.js";
|
|
40
42
|
export { formatHelpText } from "./cli-help.js";
|
|
41
43
|
export { getNextSteps, getOptionalOnboarding, runScaffoldFlow, } from "./cli-scaffold.js";
|
package/dist/runtime/cli-core.js
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* `runAddAdminViewCommand` for DataViews-backed admin screen scaffolds,
|
|
12
12
|
* `runAddAbilityCommand` for typed workflow ability scaffolds,
|
|
13
13
|
* and `HOOKED_BLOCK_POSITION_IDS`,
|
|
14
|
-
* `getWorkspaceBlockSelectOptions`,
|
|
14
|
+
* `getWorkspaceBlockSelectOptions`, `getWorkspaceBlockSelectOptionsAsync`, and
|
|
15
|
+
* `seedWorkspaceMigrationProject` for
|
|
15
16
|
* explicit `wp-typia add` flows,
|
|
16
17
|
* `runAddAiFeatureCommand` for server-owned WordPress AI feature scaffolds,
|
|
17
18
|
* `runAddRestResourceCommand` for plugin-level REST resource scaffolds,
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
*/
|
|
31
32
|
export { getDoctorChecks, runDoctor } from "./cli-doctor.js";
|
|
32
33
|
export { createCliCommandError, createCliDiagnosticCodeError, CliDiagnosticError, CLI_DIAGNOSTIC_CODE_METADATA, CLI_DIAGNOSTIC_CODES, formatCliDiagnosticError, formatDoctorCheckLine, formatDoctorSummaryLine, getCliDiagnosticCodeMetadata, getDoctorFailureDetailLines, getFailingDoctorChecks, isCliDiagnosticError, } from "./cli-diagnostics.js";
|
|
33
|
-
export { EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, getWorkspaceBlockSelectOptions, runAddAdminViewCommand, runAddAbilityCommand, runAddBindingSourceCommand, runAddAiFeatureCommand, runAddBlockCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, seedWorkspaceMigrationProject, } from "./cli-add.js";
|
|
34
|
+
export { EDITOR_PLUGIN_SLOT_IDS, formatAddHelpText, getWorkspaceBlockSelectOptions, getWorkspaceBlockSelectOptionsAsync, runAddAdminViewCommand, runAddAbilityCommand, runAddBindingSourceCommand, runAddAiFeatureCommand, runAddBlockCommand, runAddBlockStyleCommand, runAddBlockTransformCommand, runAddEditorPluginCommand, runAddHookedBlockCommand, runAddPatternCommand, runAddRestResourceCommand, runAddVariationCommand, seedWorkspaceMigrationProject, } from "./cli-add.js";
|
|
34
35
|
export { COMPOUND_INNER_BLOCKS_PRESET_IDS, getCompoundInnerBlocksPresetDefinition, } from "./compound-inner-blocks.js";
|
|
35
36
|
export { HOOKED_BLOCK_POSITION_IDS } from "./hooked-blocks.js";
|
|
36
37
|
export { formatHelpText } from "./cli-help.js";
|
|
@@ -70,6 +70,8 @@ export function getWorkspaceDoctorChecks(cwd) {
|
|
|
70
70
|
}
|
|
71
71
|
checks.push(getWorkspacePackageMetadataCheck(workspace, workspacePackageJson));
|
|
72
72
|
try {
|
|
73
|
+
// Doctor checks expose a synchronous API so callers can collect a stable
|
|
74
|
+
// snapshot without mixing async inventory reads into check aggregation.
|
|
73
75
|
const inventory = readWorkspaceInventory(workspace.projectDir);
|
|
74
76
|
checks.push(createDoctorCheck("Workspace inventory", "pass", formatWorkspaceInventorySummary(inventory)));
|
|
75
77
|
checks.push(...getWorkspaceBlockDoctorChecks(workspace, inventory));
|
|
@@ -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 { collectScaffoldAnswers, DATA_STORAGE_MODES, PERSISTENCE_POLICIES, isDataStorageMode, isPersistencePolicy, resolvePackageManagerId, resolveTemplateId, scaffoldProject, } from "./scaffold.js";
|
|
@@ -10,6 +9,7 @@ import { getPrimaryDevelopmentScript } from "./local-dev-presets.js";
|
|
|
10
9
|
import { createManagedTempRoot } from "./temp-roots.js";
|
|
11
10
|
import { getOptionalOnboardingNote, getOptionalOnboardingShortNote, getOptionalOnboardingSteps, } from "./scaffold-onboarding.js";
|
|
12
11
|
import { formatNonEmptyTargetDirectoryError } from "./scaffold-bootstrap.js";
|
|
12
|
+
import { pathExists } from "./fs-async.js";
|
|
13
13
|
import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, isBuiltInTemplateId, } from "./template-registry.js";
|
|
14
14
|
import { resolveOptionalInteractiveExternalLayerId, } from "./external-layer-selection.js";
|
|
15
15
|
import { assertBuiltInTemplateVariantAllowed, resolveLocalCliPathOption, normalizeOptionalCliString, } from "./cli-validation.js";
|
|
@@ -32,7 +32,7 @@ async function listRelativeProjectFiles(rootDir) {
|
|
|
32
32
|
return relativeFiles.sort((left, right) => left.localeCompare(right));
|
|
33
33
|
}
|
|
34
34
|
async function assertDryRunTargetDirectoryReady(projectDir, allowExistingDir) {
|
|
35
|
-
if (!
|
|
35
|
+
if (!(await pathExists(projectDir)) || allowExistingDir) {
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
const entries = await fsp.readdir(projectDir);
|
|
@@ -425,7 +425,7 @@ export async function runScaffoldFlow({ projectInput, cwd = process.cwd(), templ
|
|
|
425
425
|
let availableScripts;
|
|
426
426
|
if (!dryRun) {
|
|
427
427
|
try {
|
|
428
|
-
const parsedPackageJson = JSON.parse(
|
|
428
|
+
const parsedPackageJson = JSON.parse(await fsp.readFile(path.join(projectDir, "package.json"), "utf8"));
|
|
429
429
|
const scripts = parsedPackageJson.scripts &&
|
|
430
430
|
typeof parsedPackageJson.scripts === "object" &&
|
|
431
431
|
!Array.isArray(parsedPackageJson.scripts)
|
|
@@ -3,6 +3,7 @@ import { CLI_DIAGNOSTIC_CODES, createCliDiagnosticCodeError, } from "./cli-diagn
|
|
|
3
3
|
import { OFFICIAL_WORKSPACE_TEMPLATE_ALIAS, OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, TEMPLATE_IDS, getTemplateById, isBuiltInTemplateId, } from "./template-registry.js";
|
|
4
4
|
import { getRemovedBuiltInTemplateMessage, isRemovedBuiltInTemplateId, } from "./template-defaults.js";
|
|
5
5
|
import { parseNpmTemplateLocator } from "./template-source-locators.js";
|
|
6
|
+
import { suggestCloseId } from "./id-suggestions.js";
|
|
6
7
|
export const CREATE_TEMPLATE_SELECTION_HINT = `--template <${[
|
|
7
8
|
...TEMPLATE_IDS,
|
|
8
9
|
OFFICIAL_WORKSPACE_TEMPLATE_ALIAS,
|
|
@@ -39,40 +40,13 @@ function looksLikeExplicitCreateExternalTemplateLocator(templateId) {
|
|
|
39
40
|
return (looksLikeExplicitNonNpmExternalTemplateLocator(templateId) ||
|
|
40
41
|
parseNpmTemplateLocator(templateId) !== null);
|
|
41
42
|
}
|
|
42
|
-
function getEditDistance(left, right) {
|
|
43
|
-
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
44
|
-
const current = new Array(right.length + 1);
|
|
45
|
-
for (let leftIndex = 0; leftIndex < left.length; leftIndex += 1) {
|
|
46
|
-
current[0] = leftIndex + 1;
|
|
47
|
-
for (let rightIndex = 0; rightIndex < right.length; rightIndex += 1) {
|
|
48
|
-
const substitutionCost = left[leftIndex] === right[rightIndex] ? 0 : 1;
|
|
49
|
-
current[rightIndex + 1] = Math.min(current[rightIndex] + 1, previous[rightIndex + 1] + 1, previous[rightIndex] + substitutionCost);
|
|
50
|
-
}
|
|
51
|
-
for (let index = 0; index < current.length; index += 1) {
|
|
52
|
-
previous[index] = current[index];
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return previous[right.length];
|
|
56
|
-
}
|
|
57
43
|
function findMistypedBuiltInTemplateSuggestion(templateId) {
|
|
58
44
|
const normalizedTemplateId = templateId.trim().toLowerCase();
|
|
59
45
|
if (normalizedTemplateId.length === 0 ||
|
|
60
46
|
looksLikeExplicitNonNpmExternalTemplateLocator(normalizedTemplateId)) {
|
|
61
47
|
return null;
|
|
62
48
|
}
|
|
63
|
-
|
|
64
|
-
for (const candidateId of TEMPLATE_SUGGESTION_IDS) {
|
|
65
|
-
const distance = getEditDistance(normalizedTemplateId, candidateId);
|
|
66
|
-
if (bestCandidate === null || distance < bestCandidate.distance) {
|
|
67
|
-
bestCandidate = {
|
|
68
|
-
distance,
|
|
69
|
-
id: candidateId,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return bestCandidate && bestCandidate.distance <= 2
|
|
74
|
-
? bestCandidate.id
|
|
75
|
-
: null;
|
|
49
|
+
return suggestCloseId(normalizedTemplateId, TEMPLATE_SUGGESTION_IDS);
|
|
76
50
|
}
|
|
77
51
|
function getMistypedBuiltInTemplateMessage(templateId) {
|
|
78
52
|
const suggestion = findMistypedBuiltInTemplateSuggestion(templateId);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface SuggestCloseIdOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Maximum edit distance accepted for a suggestion.
|
|
4
|
+
*
|
|
5
|
+
* Defaults to `2`, matching the create-template typo guard.
|
|
6
|
+
*/
|
|
7
|
+
maxDistance?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Normalizes user input and candidates before comparing them.
|
|
10
|
+
*
|
|
11
|
+
* Defaults to trimming and lowercasing for CLI id-like values.
|
|
12
|
+
*/
|
|
13
|
+
normalize?: (value: string) => string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Suggest the closest known id for a user-provided CLI value.
|
|
17
|
+
*
|
|
18
|
+
* This helper is intentionally generic so command-specific validation can keep
|
|
19
|
+
* its own wording and special-case handling while sharing typo thresholds.
|
|
20
|
+
*/
|
|
21
|
+
export declare function suggestCloseId<const TCandidate extends string>(input: string, candidates: readonly TCandidate[], options?: SuggestCloseIdOptions): TCandidate | null;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
function normalizeCloseId(value) {
|
|
2
|
+
return value.trim().toLowerCase();
|
|
3
|
+
}
|
|
4
|
+
function getEditDistance(left, right) {
|
|
5
|
+
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
6
|
+
const current = new Array(right.length + 1);
|
|
7
|
+
for (let leftIndex = 0; leftIndex < left.length; leftIndex += 1) {
|
|
8
|
+
current[0] = leftIndex + 1;
|
|
9
|
+
for (let rightIndex = 0; rightIndex < right.length; rightIndex += 1) {
|
|
10
|
+
const substitutionCost = left[leftIndex] === right[rightIndex] ? 0 : 1;
|
|
11
|
+
current[rightIndex + 1] = Math.min(current[rightIndex] + 1, previous[rightIndex + 1] + 1, previous[rightIndex] + substitutionCost);
|
|
12
|
+
}
|
|
13
|
+
for (let index = 0; index < current.length; index += 1) {
|
|
14
|
+
previous[index] = current[index];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return previous[right.length];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Suggest the closest known id for a user-provided CLI value.
|
|
21
|
+
*
|
|
22
|
+
* This helper is intentionally generic so command-specific validation can keep
|
|
23
|
+
* its own wording and special-case handling while sharing typo thresholds.
|
|
24
|
+
*/
|
|
25
|
+
export function suggestCloseId(input, candidates, options = {}) {
|
|
26
|
+
const normalize = options.normalize ?? normalizeCloseId;
|
|
27
|
+
const normalizedInput = normalize(input);
|
|
28
|
+
if (normalizedInput.length === 0) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const maxDistance = options.maxDistance ?? 2;
|
|
32
|
+
if (maxDistance < 0) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
let bestCandidate = null;
|
|
36
|
+
for (const candidateId of candidates) {
|
|
37
|
+
const distance = getEditDistance(normalizedInput, normalize(candidateId));
|
|
38
|
+
if (bestCandidate === null || distance < bestCandidate.distance) {
|
|
39
|
+
bestCandidate = {
|
|
40
|
+
distance,
|
|
41
|
+
id: candidateId,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return bestCandidate && bestCandidate.distance <= maxDistance
|
|
46
|
+
? bestCandidate.id
|
|
47
|
+
: null;
|
|
48
|
+
}
|
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);
|
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,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"));
|
|
@@ -7,6 +7,9 @@ export declare const EXTERNAL_TEMPLATE_TRUST_WARNING = "External template config
|
|
|
7
7
|
/**
|
|
8
8
|
* Search a source directory for the first supported external template entry.
|
|
9
9
|
*
|
|
10
|
+
* @deprecated Use `findExternalTemplateEntry()` from async template-source
|
|
11
|
+
* paths. This synchronous helper remains only for compatibility callers.
|
|
12
|
+
*
|
|
10
13
|
* @param sourceDir Directory that may contain an external template config entry.
|
|
11
14
|
* @returns The first matching entry path, or null when no supported entry exists.
|
|
12
15
|
*/
|
|
@@ -33,6 +33,9 @@ function resolveSourceSubpath(sourceDir, relativePath) {
|
|
|
33
33
|
/**
|
|
34
34
|
* Search a source directory for the first supported external template entry.
|
|
35
35
|
*
|
|
36
|
+
* @deprecated Use `findExternalTemplateEntry()` from async template-source
|
|
37
|
+
* paths. This synchronous helper remains only for compatibility callers.
|
|
38
|
+
*
|
|
36
39
|
* @param sourceDir Directory that may contain an external template config entry.
|
|
37
40
|
* @returns The first matching entry path, or null when no supported entry exists.
|
|
38
41
|
*/
|
|
@@ -205,11 +208,11 @@ export async function renderCreateBlockExternalTemplate(sourceDir, context, requ
|
|
|
205
208
|
const view = await buildExternalTemplateView(context, config, selectedVariant, variantConfig);
|
|
206
209
|
const blockTemplateDir = resolveSourceSubpath(sourceDir, templatePath);
|
|
207
210
|
await copyRenderedDirectory(blockTemplateDir, blockDir, view, {
|
|
208
|
-
filter: (sourcePath, _destinationPath, entry) => {
|
|
211
|
+
filter: async (sourcePath, _destinationPath, entry) => {
|
|
209
212
|
const mustacheVariantPath = path.join(path.dirname(sourcePath), `${entry.name}.mustache`);
|
|
210
213
|
return !(entry.isFile() &&
|
|
211
214
|
(entry.name === 'package.json' || entry.name === 'README.md') &&
|
|
212
|
-
|
|
215
|
+
(await pathExists(mustacheVariantPath)));
|
|
213
216
|
},
|
|
214
217
|
});
|
|
215
218
|
const assetsPath = typeof variantConfig.assetsPath === 'string'
|
|
@@ -2,6 +2,9 @@ import type { ResolvedTemplateSource, SeedSource, TemplateVariableContext } from
|
|
|
2
2
|
/**
|
|
3
3
|
* Read a remote block source and return its default block category.
|
|
4
4
|
*
|
|
5
|
+
* @deprecated Use `getDefaultCategoryAsync()` from async template-source paths.
|
|
6
|
+
* This synchronous helper remains only for compatibility callers.
|
|
7
|
+
*
|
|
5
8
|
* @param sourceDir Block source directory that may contain a block.json file.
|
|
6
9
|
* @returns The declared block category, or "widgets" when detection fails.
|
|
7
10
|
*/
|
|
@@ -16,6 +19,9 @@ export declare function getDefaultCategoryAsync(sourceDir: string): Promise<stri
|
|
|
16
19
|
/**
|
|
17
20
|
* Read `wpTypia.projectType` from a rendered or source template package
|
|
18
21
|
* manifest and return it when present.
|
|
22
|
+
*
|
|
23
|
+
* @deprecated Use `getTemplateProjectTypeAsync()` from async template-source
|
|
24
|
+
* paths. This synchronous helper remains only for compatibility callers.
|
|
19
25
|
*/
|
|
20
26
|
export declare function getTemplateProjectType(sourceDir: string): string | null;
|
|
21
27
|
/**
|
|
@@ -59,6 +59,9 @@ async function readRemoteBlockJsonAsync(blockDir) {
|
|
|
59
59
|
/**
|
|
60
60
|
* Read a remote block source and return its default block category.
|
|
61
61
|
*
|
|
62
|
+
* @deprecated Use `getDefaultCategoryAsync()` from async template-source paths.
|
|
63
|
+
* This synchronous helper remains only for compatibility callers.
|
|
64
|
+
*
|
|
62
65
|
* @param sourceDir Block source directory that may contain a block.json file.
|
|
63
66
|
* @returns The declared block category, or "widgets" when detection fails.
|
|
64
67
|
*/
|
|
@@ -139,6 +142,9 @@ async function readTemplatePackageJsonAsync(sourceDir) {
|
|
|
139
142
|
/**
|
|
140
143
|
* Read `wpTypia.projectType` from a rendered or source template package
|
|
141
144
|
* manifest and return it when present.
|
|
145
|
+
*
|
|
146
|
+
* @deprecated Use `getTemplateProjectTypeAsync()` from async template-source
|
|
147
|
+
* paths. This synchronous helper remains only for compatibility callers.
|
|
142
148
|
*/
|
|
143
149
|
export function getTemplateProjectType(sourceDir) {
|
|
144
150
|
const packageJsonEntry = readTemplatePackageJson(sourceDir);
|
|
@@ -185,11 +191,11 @@ export async function normalizeWpTypiaTemplateSeed(seed) {
|
|
|
185
191
|
const normalizedDir = path.join(tempRoot, 'template');
|
|
186
192
|
try {
|
|
187
193
|
await copyRawDirectory(seed.blockDir, normalizedDir, {
|
|
188
|
-
filter: (sourcePath, _targetPath, entry) => {
|
|
194
|
+
filter: async (sourcePath, _targetPath, entry) => {
|
|
189
195
|
const mustacheVariantPath = path.join(path.dirname(sourcePath), `${entry.name}.mustache`);
|
|
190
196
|
return !(entry.isFile() &&
|
|
191
197
|
(entry.name === 'package.json' || entry.name === 'README.md') &&
|
|
192
|
-
|
|
198
|
+
(await pathExists(mustacheVariantPath)));
|
|
193
199
|
},
|
|
194
200
|
});
|
|
195
201
|
if (seed.assetsDir && (await pathExists(seed.assetsDir))) {
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import type { RemoteTemplateLocator, SeedSource } from './template-source-contracts.js';
|
|
2
|
+
/**
|
|
3
|
+
* Synchronously identify the bundled workspace template seed.
|
|
4
|
+
*
|
|
5
|
+
* This remains sync-only for compatibility callers. Async template resolution
|
|
6
|
+
* paths should use `isOfficialWorkspaceTemplateSeedAsync()`.
|
|
7
|
+
*/
|
|
2
8
|
export declare function isOfficialWorkspaceTemplateSeed(seed: SeedSource): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Asynchronously identify the bundled workspace template seed.
|
|
11
|
+
*
|
|
12
|
+
* @param seed Seed source to inspect.
|
|
13
|
+
* @returns Whether the seed resolves to the official workspace template package.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isOfficialWorkspaceTemplateSeedAsync(seed: SeedSource): Promise<boolean>;
|
|
3
16
|
export declare function assertNoSymlinks(sourceDir: string): Promise<void>;
|
|
4
17
|
/**
|
|
5
18
|
* Resolves a template locator into a local seed source directory.
|
|
@@ -8,6 +8,7 @@ import { x as extractTarball } from 'tar';
|
|
|
8
8
|
import { createExternalTemplateTimeoutError, fetchWithExternalTemplateTimeout, getExternalTemplateMetadataMaxBytes, getExternalTemplateTarballMaxBytes, getExternalTemplateTimeoutMs, readBufferResponseWithLimit, readJsonResponseWithLimit, } from './external-template-guards.js';
|
|
9
9
|
import { findReusableExternalTemplateSourceCache, isExternalTemplateCacheEnabled, resolveExternalTemplateSourceCache, } from './template-source-cache.js';
|
|
10
10
|
import { CLI_DIAGNOSTIC_CODES, createCliDiagnosticCodeError, } from './cli-diagnostics.js';
|
|
11
|
+
import { pathExists } from './fs-async.js';
|
|
11
12
|
import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, OFFICIAL_WORKSPACE_TEMPLATE_ALIAS, PROJECT_TOOLS_PACKAGE_ROOT, TEMPLATE_IDS, } from './template-registry.js';
|
|
12
13
|
import { isPlainObject } from './object-utils.js';
|
|
13
14
|
import { createManagedTempRoot } from './temp-roots.js';
|
|
@@ -184,7 +185,9 @@ async function fetchNpmTemplateSource(locator) {
|
|
|
184
185
|
* Resolve a locally installed npm template package from the caller workspace.
|
|
185
186
|
*
|
|
186
187
|
* Bare package ids are preferred here so monorepo and offline workflows can
|
|
187
|
-
* use an already-installed template without forcing a registry fetch.
|
|
188
|
+
* use an already-installed template without forcing a registry fetch. This
|
|
189
|
+
* path intentionally keeps Node's synchronous `require.resolve` semantics
|
|
190
|
+
* because the module resolver itself has no async equivalent.
|
|
188
191
|
*/
|
|
189
192
|
function resolveInstalledNpmTemplateSource(locator, cwd) {
|
|
190
193
|
if (locator.rawSpec !== '' && locator.rawSpec !== '*') {
|
|
@@ -244,6 +247,12 @@ function resolveInstalledNpmTemplateSource(locator, cwd) {
|
|
|
244
247
|
throw error;
|
|
245
248
|
}
|
|
246
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* Synchronously identify the bundled workspace template seed.
|
|
252
|
+
*
|
|
253
|
+
* This remains sync-only for compatibility callers. Async template resolution
|
|
254
|
+
* paths should use `isOfficialWorkspaceTemplateSeedAsync()`.
|
|
255
|
+
*/
|
|
247
256
|
export function isOfficialWorkspaceTemplateSeed(seed) {
|
|
248
257
|
const packageJsonPath = path.join(seed.rootDir, 'package.json');
|
|
249
258
|
if (!fs.existsSync(packageJsonPath)) {
|
|
@@ -257,6 +266,25 @@ export function isOfficialWorkspaceTemplateSeed(seed) {
|
|
|
257
266
|
return false;
|
|
258
267
|
}
|
|
259
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Asynchronously identify the bundled workspace template seed.
|
|
271
|
+
*
|
|
272
|
+
* @param seed Seed source to inspect.
|
|
273
|
+
* @returns Whether the seed resolves to the official workspace template package.
|
|
274
|
+
*/
|
|
275
|
+
export async function isOfficialWorkspaceTemplateSeedAsync(seed) {
|
|
276
|
+
const packageJsonPath = path.join(seed.rootDir, 'package.json');
|
|
277
|
+
if (!(await pathExists(packageJsonPath))) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
try {
|
|
281
|
+
const packageJson = JSON.parse(await fsp.readFile(packageJsonPath, 'utf8'));
|
|
282
|
+
return packageJson.name === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE;
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
260
288
|
export async function assertNoSymlinks(sourceDir) {
|
|
261
289
|
const stats = await fsp.lstat(sourceDir);
|
|
262
290
|
if (stats.isSymbolicLink()) {
|
|
@@ -300,13 +328,13 @@ function runGitTemplateCommand(args, label, options = {}) {
|
|
|
300
328
|
function getGitHubTemplateRepositoryUrl(locator) {
|
|
301
329
|
return `https://github.com/${locator.owner}/${locator.repo}.git`;
|
|
302
330
|
}
|
|
303
|
-
function resolveGitHubTemplateDirectory(checkoutDir, locator) {
|
|
331
|
+
async function resolveGitHubTemplateDirectory(checkoutDir, locator) {
|
|
304
332
|
const sourceDir = path.resolve(checkoutDir, locator.sourcePath);
|
|
305
333
|
const relativeSourceDir = path.relative(checkoutDir, sourceDir);
|
|
306
334
|
if (relativeSourceDir.startsWith('..') || path.isAbsolute(relativeSourceDir)) {
|
|
307
335
|
throw new Error('GitHub template path must stay within the cloned repository.');
|
|
308
336
|
}
|
|
309
|
-
if (!
|
|
337
|
+
if (!(await pathExists(sourceDir))) {
|
|
310
338
|
throw new Error(`GitHub template path does not exist: ${locator.sourcePath}`);
|
|
311
339
|
}
|
|
312
340
|
return sourceDir;
|
|
@@ -438,7 +466,7 @@ async function reuseGitHubTemplateCacheByMetadata(locator) {
|
|
|
438
466
|
if (!cachedSource) {
|
|
439
467
|
return null;
|
|
440
468
|
}
|
|
441
|
-
const sourceDir = resolveGitHubTemplateDirectory(cachedSource.sourceDir, locator);
|
|
469
|
+
const sourceDir = await resolveGitHubTemplateDirectory(cachedSource.sourceDir, locator);
|
|
442
470
|
await assertNoSymlinks(sourceDir);
|
|
443
471
|
return {
|
|
444
472
|
blockDir: sourceDir,
|
|
@@ -482,12 +510,12 @@ async function resolveGitHubTemplateSource(locator) {
|
|
|
482
510
|
namespace: 'github',
|
|
483
511
|
}, async (checkoutDir) => {
|
|
484
512
|
cloneGitHubTemplateSource(locator, checkoutDir);
|
|
485
|
-
const sourceDir = resolveGitHubTemplateDirectory(checkoutDir, locator);
|
|
513
|
+
const sourceDir = await resolveGitHubTemplateDirectory(checkoutDir, locator);
|
|
486
514
|
await assertNoSymlinks(sourceDir);
|
|
487
515
|
pinGitHubTemplateCacheRevision(locator, checkoutDir, resolvedCacheRevision);
|
|
488
516
|
});
|
|
489
517
|
if (cachedSource) {
|
|
490
|
-
const sourceDir = resolveGitHubTemplateDirectory(cachedSource.sourceDir, locator);
|
|
518
|
+
const sourceDir = await resolveGitHubTemplateDirectory(cachedSource.sourceDir, locator);
|
|
491
519
|
await assertNoSymlinks(sourceDir);
|
|
492
520
|
return {
|
|
493
521
|
blockDir: sourceDir,
|
|
@@ -506,7 +534,7 @@ async function resolveGitHubTemplateSource(locator) {
|
|
|
506
534
|
const checkoutDir = path.join(remoteRoot, 'source');
|
|
507
535
|
try {
|
|
508
536
|
cloneGitHubTemplateSource(locator, checkoutDir);
|
|
509
|
-
const sourceDir = resolveGitHubTemplateDirectory(checkoutDir, locator);
|
|
537
|
+
const sourceDir = await resolveGitHubTemplateDirectory(checkoutDir, locator);
|
|
510
538
|
await assertNoSymlinks(sourceDir);
|
|
511
539
|
return {
|
|
512
540
|
blockDir: sourceDir,
|
|
@@ -530,7 +558,7 @@ async function resolveGitHubTemplateSource(locator) {
|
|
|
530
558
|
export async function resolveTemplateSeed(locator, cwd) {
|
|
531
559
|
if (locator.kind === 'path') {
|
|
532
560
|
const sourceDir = path.resolve(cwd, locator.templatePath);
|
|
533
|
-
if (!
|
|
561
|
+
if (!(await pathExists(sourceDir))) {
|
|
534
562
|
throw new Error(`Template path does not exist: ${sourceDir}`);
|
|
535
563
|
}
|
|
536
564
|
await assertNoSymlinks(sourceDir);
|
|
@@ -2,7 +2,7 @@ import { OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE, isBuiltInTemplateId, } from './tem
|
|
|
2
2
|
import { resolveBuiltInTemplateSource } from './template-builtins.js';
|
|
3
3
|
import { parseTemplateLocator, } from './template-source-locators.js';
|
|
4
4
|
import { detectTemplateSourceFormat, getTemplateProjectTypeAsync, getDefaultCategoryAsync, getTemplateVariableContext, normalizeCreateBlockSubset, normalizeWpTypiaTemplateSeed, renderCreateBlockExternalTemplate, } from './template-source-normalization.js';
|
|
5
|
-
import {
|
|
5
|
+
import { isOfficialWorkspaceTemplateSeedAsync, resolveTemplateSeed, } from './template-source-seeds.js';
|
|
6
6
|
import { assertBuiltInTemplateVariantAllowed, } from './cli-validation.js';
|
|
7
7
|
import { getScaffoldTemplateVariableGroups } from './scaffold-template-variable-groups.js';
|
|
8
8
|
export { parseGitHubTemplateLocator, parseNpmTemplateLocator, parseTemplateLocator, } from './template-source-locators.js';
|
|
@@ -27,7 +27,7 @@ export async function resolveTemplateSource(templateId, cwd, variables, variant)
|
|
|
27
27
|
const context = getTemplateVariableContext(variables);
|
|
28
28
|
const seed = await resolveTemplateSeed(locator, cwd);
|
|
29
29
|
const isOfficialWorkspaceTemplate = templateId === OFFICIAL_WORKSPACE_TEMPLATE_PACKAGE ||
|
|
30
|
-
|
|
30
|
+
(await isOfficialWorkspaceTemplateSeedAsync(seed));
|
|
31
31
|
let normalizedSeed = null;
|
|
32
32
|
try {
|
|
33
33
|
const format = await detectTemplateSourceFormat(seed.blockDir);
|
|
@@ -142,6 +142,11 @@ export interface WorkspaceInventory {
|
|
|
142
142
|
source: string;
|
|
143
143
|
variations: WorkspaceVariationInventoryEntry[];
|
|
144
144
|
}
|
|
145
|
+
export type WorkspaceBlockSelectOption = {
|
|
146
|
+
description: string;
|
|
147
|
+
name: string;
|
|
148
|
+
value: string;
|
|
149
|
+
};
|
|
145
150
|
export declare const BLOCK_CONFIG_ENTRY_MARKER = "\t// wp-typia add block entries";
|
|
146
151
|
export declare const VARIATION_CONFIG_ENTRY_MARKER = "\t// wp-typia add variation entries";
|
|
147
152
|
export declare const BLOCK_STYLE_CONFIG_ENTRY_MARKER = "\t// wp-typia add style entries";
|
|
@@ -199,19 +204,28 @@ export declare function readWorkspaceInventory(projectDir: string): WorkspaceInv
|
|
|
199
204
|
*/
|
|
200
205
|
export declare function readWorkspaceInventoryAsync(projectDir: string): Promise<WorkspaceInventory>;
|
|
201
206
|
/**
|
|
202
|
-
*
|
|
207
|
+
* Synchronously return select options for the current workspace block inventory.
|
|
203
208
|
*
|
|
204
209
|
* The `description` field mirrors `block.typesFile`, while `name` and `value`
|
|
205
210
|
* both map to the block slug for use in interactive add flows.
|
|
206
211
|
*
|
|
212
|
+
* @deprecated Use `getWorkspaceBlockSelectOptionsAsync()` from async command
|
|
213
|
+
* paths. This helper intentionally remains sync-only for compatibility callers.
|
|
214
|
+
*
|
|
207
215
|
* @param projectDir Workspace root directory.
|
|
208
216
|
* @returns Block options for variation-target selection.
|
|
209
217
|
*/
|
|
210
|
-
export declare function getWorkspaceBlockSelectOptions(projectDir: string):
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
218
|
+
export declare function getWorkspaceBlockSelectOptions(projectDir: string): WorkspaceBlockSelectOption[];
|
|
219
|
+
/**
|
|
220
|
+
* Asynchronously return select options for the current workspace block inventory.
|
|
221
|
+
*
|
|
222
|
+
* The returned option shape matches `getWorkspaceBlockSelectOptions()` while
|
|
223
|
+
* avoiding synchronous inventory reads in interactive or async command paths.
|
|
224
|
+
*
|
|
225
|
+
* @param projectDir Workspace root directory.
|
|
226
|
+
* @returns Block options for variation-target selection.
|
|
227
|
+
*/
|
|
228
|
+
export declare function getWorkspaceBlockSelectOptionsAsync(projectDir: string): Promise<WorkspaceBlockSelectOption[]>;
|
|
215
229
|
/**
|
|
216
230
|
* Update `scripts/block-config.ts` source text with additional inventory entries.
|
|
217
231
|
*
|
|
@@ -5,8 +5,8 @@ import ts from "typescript";
|
|
|
5
5
|
import { REST_RESOURCE_METHOD_IDS } from "./cli-add-shared.js";
|
|
6
6
|
import { escapeRegex } from "./php-utils.js";
|
|
7
7
|
import { getPropertyNameText } from "./ts-property-names.js";
|
|
8
|
-
function defineInventoryEntryParser(
|
|
9
|
-
return descriptor;
|
|
8
|
+
function defineInventoryEntryParser() {
|
|
9
|
+
return (descriptor) => descriptor;
|
|
10
10
|
}
|
|
11
11
|
export const BLOCK_CONFIG_ENTRY_MARKER = "\t// wp-typia add block entries";
|
|
12
12
|
export const VARIATION_CONFIG_ENTRY_MARKER = "\t// wp-typia add variation entries";
|
|
@@ -213,7 +213,7 @@ const BLOCK_INVENTORY_SECTION = {
|
|
|
213
213
|
},
|
|
214
214
|
parse: {
|
|
215
215
|
entriesKey: "blocks",
|
|
216
|
-
entry: defineInventoryEntryParser({
|
|
216
|
+
entry: defineInventoryEntryParser()({
|
|
217
217
|
entryName: "BLOCKS",
|
|
218
218
|
fields: [
|
|
219
219
|
{ key: "apiTypesFile" },
|
|
@@ -239,7 +239,7 @@ const INVENTORY_SECTIONS = [
|
|
|
239
239
|
},
|
|
240
240
|
parse: {
|
|
241
241
|
entriesKey: "variations",
|
|
242
|
-
entry: defineInventoryEntryParser({
|
|
242
|
+
entry: defineInventoryEntryParser()({
|
|
243
243
|
entryName: "VARIATIONS",
|
|
244
244
|
fields: [
|
|
245
245
|
{ key: "block", required: true },
|
|
@@ -265,7 +265,7 @@ const INVENTORY_SECTIONS = [
|
|
|
265
265
|
},
|
|
266
266
|
parse: {
|
|
267
267
|
entriesKey: "blockStyles",
|
|
268
|
-
entry: defineInventoryEntryParser({
|
|
268
|
+
entry: defineInventoryEntryParser()({
|
|
269
269
|
entryName: "BLOCK_STYLES",
|
|
270
270
|
fields: [
|
|
271
271
|
{ key: "block", required: true },
|
|
@@ -291,7 +291,7 @@ const INVENTORY_SECTIONS = [
|
|
|
291
291
|
},
|
|
292
292
|
parse: {
|
|
293
293
|
entriesKey: "blockTransforms",
|
|
294
|
-
entry: defineInventoryEntryParser({
|
|
294
|
+
entry: defineInventoryEntryParser()({
|
|
295
295
|
entryName: "BLOCK_TRANSFORMS",
|
|
296
296
|
fields: [
|
|
297
297
|
{ key: "block", required: true },
|
|
@@ -319,7 +319,7 @@ const INVENTORY_SECTIONS = [
|
|
|
319
319
|
},
|
|
320
320
|
parse: {
|
|
321
321
|
entriesKey: "patterns",
|
|
322
|
-
entry: defineInventoryEntryParser({
|
|
322
|
+
entry: defineInventoryEntryParser()({
|
|
323
323
|
entryName: "PATTERNS",
|
|
324
324
|
fields: [
|
|
325
325
|
{ key: "file", required: true },
|
|
@@ -344,7 +344,7 @@ const INVENTORY_SECTIONS = [
|
|
|
344
344
|
},
|
|
345
345
|
parse: {
|
|
346
346
|
entriesKey: "bindingSources",
|
|
347
|
-
entry: defineInventoryEntryParser({
|
|
347
|
+
entry: defineInventoryEntryParser()({
|
|
348
348
|
entryName: "BINDING_SOURCES",
|
|
349
349
|
fields: [
|
|
350
350
|
{ key: "attribute" },
|
|
@@ -372,7 +372,7 @@ const INVENTORY_SECTIONS = [
|
|
|
372
372
|
},
|
|
373
373
|
parse: {
|
|
374
374
|
entriesKey: "restResources",
|
|
375
|
-
entry: defineInventoryEntryParser({
|
|
375
|
+
entry: defineInventoryEntryParser()({
|
|
376
376
|
entryName: "REST_RESOURCES",
|
|
377
377
|
fields: [
|
|
378
378
|
{ key: "apiFile", required: true },
|
|
@@ -416,7 +416,7 @@ const INVENTORY_SECTIONS = [
|
|
|
416
416
|
},
|
|
417
417
|
parse: {
|
|
418
418
|
entriesKey: "abilities",
|
|
419
|
-
entry: defineInventoryEntryParser({
|
|
419
|
+
entry: defineInventoryEntryParser()({
|
|
420
420
|
entryName: "ABILITIES",
|
|
421
421
|
fields: [
|
|
422
422
|
{ key: "clientFile", required: true },
|
|
@@ -449,7 +449,7 @@ const INVENTORY_SECTIONS = [
|
|
|
449
449
|
},
|
|
450
450
|
parse: {
|
|
451
451
|
entriesKey: "aiFeatures",
|
|
452
|
-
entry: defineInventoryEntryParser({
|
|
452
|
+
entry: defineInventoryEntryParser()({
|
|
453
453
|
entryName: "AI_FEATURES",
|
|
454
454
|
fields: [
|
|
455
455
|
{ key: "aiSchemaFile", required: true },
|
|
@@ -482,7 +482,7 @@ const INVENTORY_SECTIONS = [
|
|
|
482
482
|
},
|
|
483
483
|
parse: {
|
|
484
484
|
entriesKey: "adminViews",
|
|
485
|
-
entry: defineInventoryEntryParser({
|
|
485
|
+
entry: defineInventoryEntryParser()({
|
|
486
486
|
entryName: "ADMIN_VIEWS",
|
|
487
487
|
fields: [
|
|
488
488
|
{ key: "file", required: true },
|
|
@@ -509,7 +509,7 @@ const INVENTORY_SECTIONS = [
|
|
|
509
509
|
},
|
|
510
510
|
parse: {
|
|
511
511
|
entriesKey: "editorPlugins",
|
|
512
|
-
entry: defineInventoryEntryParser({
|
|
512
|
+
entry: defineInventoryEntryParser()({
|
|
513
513
|
entryName: "EDITOR_PLUGINS",
|
|
514
514
|
fields: [
|
|
515
515
|
{ key: "file", required: true },
|
|
@@ -570,14 +570,7 @@ function getOptionalStringProperty(entryName, elementIndex, objectLiteral, key)
|
|
|
570
570
|
}
|
|
571
571
|
return undefined;
|
|
572
572
|
}
|
|
573
|
-
function
|
|
574
|
-
const value = getOptionalStringProperty(entryName, elementIndex, objectLiteral, key);
|
|
575
|
-
if (!value) {
|
|
576
|
-
throw new Error(`${entryName}[${elementIndex}] is missing required "${key}" in scripts/block-config.ts.`);
|
|
577
|
-
}
|
|
578
|
-
return value;
|
|
579
|
-
}
|
|
580
|
-
function getRequiredStringArrayProperty(entryName, elementIndex, objectLiteral, key) {
|
|
573
|
+
function getOptionalStringArrayProperty(entryName, elementIndex, objectLiteral, key) {
|
|
581
574
|
for (const property of objectLiteral.properties) {
|
|
582
575
|
if (!ts.isPropertyAssignment(property)) {
|
|
583
576
|
continue;
|
|
@@ -596,7 +589,24 @@ function getRequiredStringArrayProperty(entryName, elementIndex, objectLiteral,
|
|
|
596
589
|
return element.text;
|
|
597
590
|
});
|
|
598
591
|
}
|
|
599
|
-
|
|
592
|
+
return undefined;
|
|
593
|
+
}
|
|
594
|
+
function isMissingRequiredInventoryValue(value) {
|
|
595
|
+
return value === undefined || (typeof value === "string" && value.length === 0);
|
|
596
|
+
}
|
|
597
|
+
function formatMissingRequiredInventoryFields(keys) {
|
|
598
|
+
return keys.length === 1
|
|
599
|
+
? `required "${keys[0]}"`
|
|
600
|
+
: `required fields ${keys.map((key) => `"${key}"`).join(", ")}`;
|
|
601
|
+
}
|
|
602
|
+
function assertParsedInventoryEntry(entry, descriptor, elementIndex) {
|
|
603
|
+
const missingRequiredKeys = descriptor.fields
|
|
604
|
+
.filter((field) => field.required === true &&
|
|
605
|
+
isMissingRequiredInventoryValue(entry[field.key]))
|
|
606
|
+
.map((field) => field.key);
|
|
607
|
+
if (missingRequiredKeys.length > 0) {
|
|
608
|
+
throw new Error(`${descriptor.entryName}[${elementIndex}] is missing ${formatMissingRequiredInventoryFields(missingRequiredKeys)} in scripts/block-config.ts.`);
|
|
609
|
+
}
|
|
600
610
|
}
|
|
601
611
|
function parseInventoryEntries(arrayLiteral, descriptor) {
|
|
602
612
|
return arrayLiteral.elements.map((element, elementIndex) => {
|
|
@@ -607,10 +617,8 @@ function parseInventoryEntries(arrayLiteral, descriptor) {
|
|
|
607
617
|
for (const field of descriptor.fields) {
|
|
608
618
|
const kind = field.kind ?? "string";
|
|
609
619
|
const value = kind === "stringArray"
|
|
610
|
-
?
|
|
611
|
-
: field.
|
|
612
|
-
? getRequiredStringProperty(descriptor.entryName, elementIndex, element, field.key)
|
|
613
|
-
: getOptionalStringProperty(descriptor.entryName, elementIndex, element, field.key);
|
|
620
|
+
? getOptionalStringArrayProperty(descriptor.entryName, elementIndex, element, field.key)
|
|
621
|
+
: getOptionalStringProperty(descriptor.entryName, elementIndex, element, field.key);
|
|
614
622
|
field.validate?.(value, {
|
|
615
623
|
elementIndex,
|
|
616
624
|
entryName: descriptor.entryName,
|
|
@@ -618,6 +626,7 @@ function parseInventoryEntries(arrayLiteral, descriptor) {
|
|
|
618
626
|
});
|
|
619
627
|
entry[field.key] = value;
|
|
620
628
|
}
|
|
629
|
+
assertParsedInventoryEntry(entry, descriptor, elementIndex);
|
|
621
630
|
return entry;
|
|
622
631
|
});
|
|
623
632
|
}
|
|
@@ -757,21 +766,39 @@ export async function readWorkspaceInventoryAsync(projectDir) {
|
|
|
757
766
|
...parseWorkspaceInventorySource(source),
|
|
758
767
|
};
|
|
759
768
|
}
|
|
769
|
+
function toWorkspaceBlockSelectOptions(blocks) {
|
|
770
|
+
return blocks.map((block) => ({
|
|
771
|
+
description: block.typesFile,
|
|
772
|
+
name: block.slug,
|
|
773
|
+
value: block.slug,
|
|
774
|
+
}));
|
|
775
|
+
}
|
|
760
776
|
/**
|
|
761
|
-
*
|
|
777
|
+
* Synchronously return select options for the current workspace block inventory.
|
|
762
778
|
*
|
|
763
779
|
* The `description` field mirrors `block.typesFile`, while `name` and `value`
|
|
764
780
|
* both map to the block slug for use in interactive add flows.
|
|
765
781
|
*
|
|
782
|
+
* @deprecated Use `getWorkspaceBlockSelectOptionsAsync()` from async command
|
|
783
|
+
* paths. This helper intentionally remains sync-only for compatibility callers.
|
|
784
|
+
*
|
|
766
785
|
* @param projectDir Workspace root directory.
|
|
767
786
|
* @returns Block options for variation-target selection.
|
|
768
787
|
*/
|
|
769
788
|
export function getWorkspaceBlockSelectOptions(projectDir) {
|
|
770
|
-
return readWorkspaceInventory(projectDir).blocks
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
789
|
+
return toWorkspaceBlockSelectOptions(readWorkspaceInventory(projectDir).blocks);
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Asynchronously return select options for the current workspace block inventory.
|
|
793
|
+
*
|
|
794
|
+
* The returned option shape matches `getWorkspaceBlockSelectOptions()` while
|
|
795
|
+
* avoiding synchronous inventory reads in interactive or async command paths.
|
|
796
|
+
*
|
|
797
|
+
* @param projectDir Workspace root directory.
|
|
798
|
+
* @returns Block options for variation-target selection.
|
|
799
|
+
*/
|
|
800
|
+
export async function getWorkspaceBlockSelectOptionsAsync(projectDir) {
|
|
801
|
+
return toWorkspaceBlockSelectOptions((await readWorkspaceInventoryAsync(projectDir)).blocks);
|
|
775
802
|
}
|
|
776
803
|
function ensureWorkspaceInventorySections(source) {
|
|
777
804
|
let nextSource = source.trimEnd();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wp-typia/project-tools",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.9",
|
|
4
4
|
"description": "Project orchestration and programmatic tooling for wp-typia",
|
|
5
5
|
"packageManager": "bun@1.3.11",
|
|
6
6
|
"type": "module",
|
|
@@ -52,6 +52,11 @@
|
|
|
52
52
|
"import": "./dist/runtime/create-template-validation.js",
|
|
53
53
|
"default": "./dist/runtime/create-template-validation.js"
|
|
54
54
|
},
|
|
55
|
+
"./id-suggestions": {
|
|
56
|
+
"types": "./dist/runtime/id-suggestions.d.ts",
|
|
57
|
+
"import": "./dist/runtime/id-suggestions.js",
|
|
58
|
+
"default": "./dist/runtime/id-suggestions.js"
|
|
59
|
+
},
|
|
55
60
|
"./ai-artifacts": {
|
|
56
61
|
"types": "./dist/runtime/ai-artifacts.d.ts",
|
|
57
62
|
"import": "./dist/runtime/ai-artifacts.js",
|