@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
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface WorkspaceBlockTargetName {
|
|
2
|
+
blockName: string;
|
|
3
|
+
blockSlug: string;
|
|
4
|
+
}
|
|
5
|
+
export interface WorkspaceBlockTargetDiagnostics {
|
|
6
|
+
empty: () => string;
|
|
7
|
+
emptySegment: (input: string) => string;
|
|
8
|
+
invalidFormat: (input: string) => string;
|
|
9
|
+
namespaceMismatch: (input: string, actualNamespace: string, expectedNamespace: string) => string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Validate a full `namespace/block-slug` block name.
|
|
13
|
+
*
|
|
14
|
+
* @param blockName Candidate block name.
|
|
15
|
+
* @param flagName CLI flag name used in diagnostics.
|
|
16
|
+
* @returns The trimmed full block name.
|
|
17
|
+
* @throws {Error} When the block name is empty or not a full block name.
|
|
18
|
+
*/
|
|
19
|
+
export declare function assertFullBlockName(blockName: string, flagName: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Resolve a workspace block target from either `block-slug` or
|
|
22
|
+
* `namespace/block-slug` input while preserving caller-owned diagnostics.
|
|
23
|
+
*
|
|
24
|
+
* @param blockName Candidate block target.
|
|
25
|
+
* @param namespace Expected workspace namespace.
|
|
26
|
+
* @param diagnostics Error message builders for the caller's UX context.
|
|
27
|
+
* @returns The normalized workspace block target.
|
|
28
|
+
* @throws {Error} When the target is empty, malformed, or references another namespace.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveWorkspaceBlockTargetName(blockName: string, namespace: string, diagnostics: WorkspaceBlockTargetDiagnostics): WorkspaceBlockTargetName;
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the standard `--to` style workspace block target used by add flows.
|
|
33
|
+
*
|
|
34
|
+
* @param blockName Candidate block target.
|
|
35
|
+
* @param namespace Expected workspace namespace.
|
|
36
|
+
* @param flagName CLI flag name used in diagnostics.
|
|
37
|
+
* @returns The normalized workspace block target.
|
|
38
|
+
* @throws {Error} When the target is empty, malformed, or references another namespace.
|
|
39
|
+
*/
|
|
40
|
+
export declare function resolveWorkspaceTargetBlockName(blockName: string, namespace: string, flagName: string): WorkspaceBlockTargetName;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { normalizeBlockSlug } from "./scaffold-identifiers.js";
|
|
2
|
+
const FULL_BLOCK_NAME_PATTERN = /^[a-z0-9-]+\/[a-z0-9-]+$/u;
|
|
3
|
+
/**
|
|
4
|
+
* Validate a full `namespace/block-slug` block name.
|
|
5
|
+
*
|
|
6
|
+
* @param blockName Candidate block name.
|
|
7
|
+
* @param flagName CLI flag name used in diagnostics.
|
|
8
|
+
* @returns The trimmed full block name.
|
|
9
|
+
* @throws {Error} When the block name is empty or not a full block name.
|
|
10
|
+
*/
|
|
11
|
+
export function assertFullBlockName(blockName, flagName) {
|
|
12
|
+
const trimmed = blockName.trim();
|
|
13
|
+
if (!trimmed) {
|
|
14
|
+
throw new Error(`\`${flagName}\` requires a block name.`);
|
|
15
|
+
}
|
|
16
|
+
if (!FULL_BLOCK_NAME_PATTERN.test(trimmed)) {
|
|
17
|
+
throw new Error(`\`${flagName}\` must use <namespace/block-slug> format.`);
|
|
18
|
+
}
|
|
19
|
+
return trimmed;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolve a workspace block target from either `block-slug` or
|
|
23
|
+
* `namespace/block-slug` input while preserving caller-owned diagnostics.
|
|
24
|
+
*
|
|
25
|
+
* @param blockName Candidate block target.
|
|
26
|
+
* @param namespace Expected workspace namespace.
|
|
27
|
+
* @param diagnostics Error message builders for the caller's UX context.
|
|
28
|
+
* @returns The normalized workspace block target.
|
|
29
|
+
* @throws {Error} When the target is empty, malformed, or references another namespace.
|
|
30
|
+
*/
|
|
31
|
+
export function resolveWorkspaceBlockTargetName(blockName, namespace, diagnostics) {
|
|
32
|
+
const trimmed = blockName.trim();
|
|
33
|
+
if (!trimmed) {
|
|
34
|
+
throw new Error(diagnostics.empty());
|
|
35
|
+
}
|
|
36
|
+
const blockNameSegments = trimmed.split("/");
|
|
37
|
+
if (blockNameSegments.length > 2) {
|
|
38
|
+
throw new Error(diagnostics.invalidFormat(trimmed));
|
|
39
|
+
}
|
|
40
|
+
if (blockNameSegments.some((segment) => segment.trim() === "")) {
|
|
41
|
+
throw new Error(diagnostics.emptySegment(trimmed));
|
|
42
|
+
}
|
|
43
|
+
const [maybeNamespace, maybeSlug] = blockNameSegments.length === 2
|
|
44
|
+
? blockNameSegments
|
|
45
|
+
: [undefined, blockNameSegments[0]];
|
|
46
|
+
if (maybeNamespace && maybeNamespace !== namespace) {
|
|
47
|
+
throw new Error(diagnostics.namespaceMismatch(trimmed, maybeNamespace, namespace));
|
|
48
|
+
}
|
|
49
|
+
const blockSlug = normalizeBlockSlug(maybeSlug ?? "");
|
|
50
|
+
return {
|
|
51
|
+
blockName: `${namespace}/${blockSlug}`,
|
|
52
|
+
blockSlug,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Resolve the standard `--to` style workspace block target used by add flows.
|
|
57
|
+
*
|
|
58
|
+
* @param blockName Candidate block target.
|
|
59
|
+
* @param namespace Expected workspace namespace.
|
|
60
|
+
* @param flagName CLI flag name used in diagnostics.
|
|
61
|
+
* @returns The normalized workspace block target.
|
|
62
|
+
* @throws {Error} When the target is empty, malformed, or references another namespace.
|
|
63
|
+
*/
|
|
64
|
+
export function resolveWorkspaceTargetBlockName(blockName, namespace, flagName) {
|
|
65
|
+
return resolveWorkspaceBlockTargetName(blockName, namespace, {
|
|
66
|
+
empty: () => `\`${flagName}\` requires <block-slug|namespace/block-slug>.`,
|
|
67
|
+
emptySegment: () => `\`${flagName}\` must use <block-slug|namespace/block-slug> format.`,
|
|
68
|
+
invalidFormat: () => `\`${flagName}\` must use <block-slug|namespace/block-slug> format.`,
|
|
69
|
+
namespaceMismatch: (_input, actualNamespace, expectedNamespace) => `\`${flagName}\` references namespace "${actualNamespace}". Expected "${expectedNamespace}".`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isCompoundPersistenceEnabled } from "./scaffold-template-variable-groups.js";
|
|
1
2
|
const STANDARD_PREAMBLE_LINES = [
|
|
2
3
|
'import type { TextAlignment } from "@wp-typia/block-types/block-editor/alignment";',
|
|
3
4
|
"import type {",
|
|
@@ -216,7 +217,7 @@ export function buildPersistenceTypesSource(variables, attributes) {
|
|
|
216
217
|
* @returns TypeScript source for the compound parent runtime types.
|
|
217
218
|
*/
|
|
218
219
|
export function buildCompoundTypesSource(variables, attributes) {
|
|
219
|
-
const persistenceEnabled = variables
|
|
220
|
+
const persistenceEnabled = isCompoundPersistenceEnabled(variables);
|
|
220
221
|
return emitTypesModule({
|
|
221
222
|
preambleLines: persistenceEnabled
|
|
222
223
|
? [...STANDARD_PREAMBLE_LINES]
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isCompoundPersistenceEnabled } from "./scaffold-template-variable-groups.js";
|
|
1
2
|
import { DEFAULT_COMPOUND_CHILD_BODY_PLACEHOLDER, buildAttributesFromSpecs, describe, } from "./built-in-block-attribute-emitters.js";
|
|
2
3
|
const ALIGNMENT_VALUES = ["left", "center", "right"];
|
|
3
4
|
const BASIC_ALIGNMENT_VALUES = ["left", "center", "right", "justify"];
|
|
@@ -334,7 +335,7 @@ export function buildPersistenceAttributes(variables) {
|
|
|
334
335
|
* @returns Emitted attribute definitions for the compound parent artifact.
|
|
335
336
|
*/
|
|
336
337
|
export function buildCompoundParentAttributes(variables) {
|
|
337
|
-
return buildAttributesFromSpecs(variables
|
|
338
|
+
return buildAttributesFromSpecs(isCompoundPersistenceEnabled(variables)
|
|
338
339
|
? [
|
|
339
340
|
...COMPOUND_PARENT_BASE_ATTRIBUTE_SPECS,
|
|
340
341
|
...COMPOUND_PARENT_PERSISTENCE_ATTRIBUTE_SPECS,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { buildBuiltInNonTsArtifacts } from "./built-in-block-non-ts-artifacts.js";
|
|
2
2
|
import { BASIC_EDIT_TEMPLATE, BASIC_INDEX_TEMPLATE, BASIC_SAVE_TEMPLATE, BASIC_VALIDATORS_TEMPLATE, BLOCK_METADATA_WRAPPER_TEMPLATE, COMPOUND_CHILD_EDIT_TEMPLATE, COMPOUND_CHILD_INDEX_TEMPLATE, COMPOUND_CHILD_SAVE_TEMPLATE, COMPOUND_CHILD_VALIDATORS_TEMPLATE, COMPOUND_CHILDREN_TEMPLATE, COMPOUND_LOCAL_HOOKS_TEMPLATE, COMPOUND_PARENT_EDIT_TEMPLATE, COMPOUND_PARENT_INDEX_TEMPLATE, COMPOUND_PARENT_SAVE_TEMPLATE, COMPOUND_PARENT_VALIDATORS_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_EDIT_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_INTERACTIVITY_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_SAVE_TEMPLATE, COMPOUND_PERSISTENCE_PARENT_VALIDATORS_TEMPLATE, INTERACTIVITY_EDIT_TEMPLATE, INTERACTIVITY_INDEX_TEMPLATE, INTERACTIVITY_SAVE_TEMPLATE, INTERACTIVITY_SCRIPT_TEMPLATE, INTERACTIVITY_STORE_TEMPLATE, INTERACTIVITY_VALIDATORS_TEMPLATE, MANIFEST_DEFAULTS_DOCUMENT_WRAPPER_TEMPLATE, MANIFEST_DOCUMENT_WRAPPER_TEMPLATE, PERSISTENCE_EDIT_TEMPLATE, PERSISTENCE_INDEX_TEMPLATE, PERSISTENCE_INTERACTIVITY_TEMPLATE, PERSISTENCE_SAVE_TEMPLATE, PERSISTENCE_VALIDATORS_TEMPLATE, QUERY_LOOP_INDEX_TEMPLATE, SHARED_HOOKS_TEMPLATE, } from "./built-in-block-code-templates.js";
|
|
3
3
|
import { getScaffoldTemplateVariableGroups } from "./scaffold-template-variable-groups.js";
|
|
4
|
+
import { assertScaffoldTemplateCodeIdentifiers } from "./scaffold-template-assertions.js";
|
|
4
5
|
import { renderMustacheTemplateString } from "./template-render.js";
|
|
5
6
|
function renderCodeTemplate(template, variables) {
|
|
7
|
+
assertScaffoldTemplateCodeIdentifiers(variables);
|
|
6
8
|
const rendered = renderMustacheTemplateString(template, variables);
|
|
7
9
|
return rendered.endsWith("\n") ? rendered : `${rendered}\n`;
|
|
8
10
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { buildAlternateRenderEntryArtifact, renderArtifact, toPhpSingleQuotedString, } from "./built-in-block-non-ts-render-utils.js";
|
|
2
|
+
import { getScaffoldAlternateRenderTargets, isCompoundPersistenceEnabled, } from "./scaffold-template-variable-groups.js";
|
|
2
3
|
const BASIC_STYLE_TEMPLATE = `/**
|
|
3
4
|
* {{title}} Block Styles
|
|
4
5
|
*/
|
|
@@ -826,7 +827,8 @@ export function buildInteractivityArtifacts(variables) {
|
|
|
826
827
|
* @returns The persistence SCSS and PHP artifacts for the selected render targets.
|
|
827
828
|
*/
|
|
828
829
|
export function buildPersistenceArtifacts(variables) {
|
|
829
|
-
|
|
830
|
+
const alternateRenderTargets = getScaffoldAlternateRenderTargets(variables);
|
|
831
|
+
if (!alternateRenderTargets.enabled) {
|
|
830
832
|
return [
|
|
831
833
|
renderArtifact("src/style.scss", PERSISTENCE_STYLE_TEMPLATE, variables),
|
|
832
834
|
renderArtifact("src/render.php", PERSISTENCE_RENDER_TEMPLATE, variables),
|
|
@@ -837,13 +839,13 @@ export function buildPersistenceArtifacts(variables) {
|
|
|
837
839
|
renderArtifact("src/render-targets.php", PERSISTENCE_RENDER_TARGETS_TEMPLATE, variables),
|
|
838
840
|
buildAlternateRenderEntryArtifact("src/render.php", "web", variables),
|
|
839
841
|
];
|
|
840
|
-
if (
|
|
842
|
+
if (alternateRenderTargets.hasEmail) {
|
|
841
843
|
artifacts.push(buildAlternateRenderEntryArtifact("src/render-email.php", "email", variables));
|
|
842
844
|
}
|
|
843
|
-
if (
|
|
845
|
+
if (alternateRenderTargets.hasMjml) {
|
|
844
846
|
artifacts.push(buildAlternateRenderEntryArtifact("src/render-mjml.php", "mjml", variables));
|
|
845
847
|
}
|
|
846
|
-
if (
|
|
848
|
+
if (alternateRenderTargets.hasPlainText) {
|
|
847
849
|
artifacts.push(buildAlternateRenderEntryArtifact("src/render-text.php", "plain-text", variables));
|
|
848
850
|
}
|
|
849
851
|
return artifacts;
|
|
@@ -855,23 +857,24 @@ export function buildPersistenceArtifacts(variables) {
|
|
|
855
857
|
* @returns The compound SCSS and PHP artifacts, including persistence variants when enabled.
|
|
856
858
|
*/
|
|
857
859
|
export function buildCompoundArtifacts(variables) {
|
|
860
|
+
const alternateRenderTargets = getScaffoldAlternateRenderTargets(variables);
|
|
858
861
|
const artifacts = [
|
|
859
862
|
renderArtifact(`src/blocks/${variables.slugKebabCase}/style.scss`, COMPOUND_STYLE_TEMPLATE, variables),
|
|
860
863
|
];
|
|
861
|
-
if (variables
|
|
864
|
+
if (isCompoundPersistenceEnabled(variables)) {
|
|
862
865
|
const renderView = {
|
|
863
866
|
...variables,
|
|
864
867
|
titlePhpLiteral: toPhpSingleQuotedString(variables.title),
|
|
865
868
|
};
|
|
866
|
-
if (
|
|
869
|
+
if (alternateRenderTargets.enabled) {
|
|
867
870
|
artifacts.push(renderArtifact(`src/blocks/${variables.slugKebabCase}/render-targets.php`, COMPOUND_PERSISTENCE_RENDER_TARGETS_TEMPLATE, renderView), buildAlternateRenderEntryArtifact(`src/blocks/${variables.slugKebabCase}/render.php`, "web", variables));
|
|
868
|
-
if (
|
|
871
|
+
if (alternateRenderTargets.hasEmail) {
|
|
869
872
|
artifacts.push(buildAlternateRenderEntryArtifact(`src/blocks/${variables.slugKebabCase}/render-email.php`, "email", variables));
|
|
870
873
|
}
|
|
871
|
-
if (
|
|
874
|
+
if (alternateRenderTargets.hasMjml) {
|
|
872
875
|
artifacts.push(buildAlternateRenderEntryArtifact(`src/blocks/${variables.slugKebabCase}/render-mjml.php`, "mjml", variables));
|
|
873
876
|
}
|
|
874
|
-
if (
|
|
877
|
+
if (alternateRenderTargets.hasPlainText) {
|
|
875
878
|
artifacts.push(buildAlternateRenderEntryArtifact(`src/blocks/${variables.slugKebabCase}/render-text.php`, "plain-text", variables));
|
|
876
879
|
}
|
|
877
880
|
return artifacts;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assertScaffoldTemplateCodeIdentifiers } from "./scaffold-template-assertions.js";
|
|
1
2
|
import { renderMustacheTemplateString } from "./template-render.js";
|
|
2
3
|
/**
|
|
3
4
|
* Renders a non-TypeScript artifact and normalizes the output to a trailing newline.
|
|
@@ -8,6 +9,7 @@ import { renderMustacheTemplateString } from "./template-render.js";
|
|
|
8
9
|
* @returns A built-in code artifact with normalized source text.
|
|
9
10
|
*/
|
|
10
11
|
export function renderArtifact(relativePath, template, view) {
|
|
12
|
+
assertScaffoldTemplateCodeIdentifiers(view);
|
|
11
13
|
const source = renderMustacheTemplateString(template, view);
|
|
12
14
|
return {
|
|
13
15
|
relativePath,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { SHARED_WORKSPACE_TEMPLATE_ROOT, } from "./template-registry.js";
|
|
3
|
+
import { isCompoundPersistenceEnabled } from "./scaffold-template-variable-groups.js";
|
|
3
4
|
import { quoteTsString, } from "./cli-add-shared.js";
|
|
4
5
|
export function buildServerTemplateRoot(persistencePolicy) {
|
|
5
6
|
return path.join(SHARED_WORKSPACE_TEMPLATE_ROOT, persistencePolicy === "public" ? "persistence-public" : "persistence-auth");
|
|
@@ -98,7 +99,7 @@ export function buildConfigEntries(templateId, variables) {
|
|
|
98
99
|
if (templateId === "persistence") {
|
|
99
100
|
return [buildPersistenceBlockConfigEntry(variables)];
|
|
100
101
|
}
|
|
101
|
-
if (variables
|
|
102
|
+
if (isCompoundPersistenceEnabled(variables)) {
|
|
102
103
|
return [
|
|
103
104
|
buildPersistenceBlockConfigEntry(variables),
|
|
104
105
|
buildCompoundChildConfigEntry(variables),
|
|
@@ -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";
|
|
@@ -17,6 +17,7 @@ import { parseTemplateLocator, resolveTemplateSeed, } from "./template-source.js
|
|
|
17
17
|
import { resolveExternalTemplateLayers, } from "./template-layers.js";
|
|
18
18
|
import { formatInstallCommand, } from "./package-managers.js";
|
|
19
19
|
import { parseCompoundInnerBlocksPreset } from "./compound-inner-blocks.js";
|
|
20
|
+
import { isCompoundPersistenceEnabled } from "./scaffold-template-variable-groups.js";
|
|
20
21
|
import { resolveOptionalInteractiveExternalLayerId, } from "./external-layer-selection.js";
|
|
21
22
|
import { parseAlternateRenderTargets } from "./alternate-render-targets.js";
|
|
22
23
|
import { assertExternalLayerCompositionOptions, normalizeOptionalCliString, resolveLocalCliPathOption, } from "./cli-validation.js";
|
|
@@ -79,12 +80,19 @@ async function assertWorkspaceDependenciesInstalled(workspace) {
|
|
|
79
80
|
}
|
|
80
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 ...\`.`);
|
|
81
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
|
+
}
|
|
82
90
|
async function copyScaffoldedBlockSlice(projectDir, templateId, tempProjectDir, variables, legacyValidatorPaths = []) {
|
|
83
91
|
if (templateId === "compound") {
|
|
84
92
|
await ensureCompoundWorkspaceSupportFiles(projectDir, tempProjectDir, legacyValidatorPaths);
|
|
85
93
|
await copyTempDirectory(path.join(tempProjectDir, "src", "blocks", variables.slugKebabCase), path.join(projectDir, "src", "blocks", variables.slugKebabCase));
|
|
86
94
|
await copyTempDirectory(path.join(tempProjectDir, "src", "blocks", `${variables.slugKebabCase}-item`), path.join(projectDir, "src", "blocks", `${variables.slugKebabCase}-item`));
|
|
87
|
-
if (variables
|
|
95
|
+
if (isCompoundPersistenceEnabled(variables)) {
|
|
88
96
|
await renderWorkspacePersistenceServerModule(projectDir, variables);
|
|
89
97
|
}
|
|
90
98
|
return;
|
|
@@ -196,7 +204,7 @@ async function syncWorkspaceAddedBlockArtifacts(projectDir, templateId, variable
|
|
|
196
204
|
await syncWorkspaceBlockMetadata(projectDir, `${variables.slugKebabCase}-item`, `${variables.pascalCase}ItemAttributes`, path.join("src", "blocks", `${variables.slugKebabCase}-item`, "types.ts"));
|
|
197
205
|
}
|
|
198
206
|
if (templateId === "persistence" ||
|
|
199
|
-
(templateId === "compound" && variables
|
|
207
|
+
(templateId === "compound" && isCompoundPersistenceEnabled(variables))) {
|
|
200
208
|
await syncWorkspacePersistenceArtifacts(projectDir, variables);
|
|
201
209
|
}
|
|
202
210
|
}
|
|
@@ -286,6 +294,10 @@ export async function runAddBlockCommand({ alternateRenderTargets, blockName, cw
|
|
|
286
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.");
|
|
287
295
|
}
|
|
288
296
|
if (!isAddBlockTemplateId(templateId)) {
|
|
297
|
+
const mistypedAddBlockTemplateMessage = getMistypedAddBlockTemplateMessage(templateId);
|
|
298
|
+
if (mistypedAddBlockTemplateMessage) {
|
|
299
|
+
throw new Error(mistypedAddBlockTemplateMessage);
|
|
300
|
+
}
|
|
289
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.`);
|
|
290
302
|
}
|
|
291
303
|
const resolvedTemplateId = templateId;
|
|
@@ -390,7 +402,7 @@ export async function runAddBlockCommand({ alternateRenderTargets, blockName, cw
|
|
|
390
402
|
await addCollectionImportsForTemplate(workspace.projectDir, resolvedTemplateId, result.variables);
|
|
391
403
|
await appendBlockConfigEntries(workspace.projectDir, buildConfigEntries(resolvedTemplateId, result.variables), resolvedTemplateId === "persistence" ||
|
|
392
404
|
(resolvedTemplateId === "compound" &&
|
|
393
|
-
result.variables
|
|
405
|
+
isCompoundPersistenceEnabled(result.variables)));
|
|
394
406
|
await syncWorkspaceAddedBlockArtifacts(workspace.projectDir, resolvedTemplateId, result.variables);
|
|
395
407
|
await updateWorkspaceMigrationConfigIfPresent(workspace.projectDir, buildMigrationBlocks(resolvedTemplateId, result.variables));
|
|
396
408
|
return {
|
|
@@ -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
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
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 { syncTypeSchemas } from "@wp-typia/block-runtime/metadata-core";
|
|
5
4
|
import semver from "semver";
|
|
6
|
-
import { appendWorkspaceInventoryEntries,
|
|
5
|
+
import { appendWorkspaceInventoryEntries, readWorkspaceInventoryAsync, } from "./workspace-inventory.js";
|
|
6
|
+
import { pathExists, readOptionalUtf8File } from "./fs-async.js";
|
|
7
7
|
import { buildAbilityClientSource, buildAbilityConfigEntry, buildAbilityConfigSource, buildAbilityDataSource, buildAbilityPhpSource, buildAbilityRegistrySource, buildAbilitySyncScriptSource, buildAbilityTypesSource, } from "./cli-add-workspace-ability-templates.js";
|
|
8
8
|
import { ABILITY_EDITOR_ASSET, ABILITY_EDITOR_SCRIPT, ABILITY_REGISTRY_END_MARKER, ABILITY_REGISTRY_START_MARKER, ABILITY_SERVER_GLOB, WP_ABILITIES_SCRIPT_MODULE_ID, WP_CORE_ABILITIES_SCRIPT_MODULE_ID, } from "./cli-add-workspace-ability-types.js";
|
|
9
9
|
import { getWorkspaceBootstrapPath, patchFile, } from "./cli-add-shared.js";
|
|
@@ -25,28 +25,34 @@ function resolveManagedDependencyVersion(existingVersion, requiredVersion) {
|
|
|
25
25
|
? existingVersion
|
|
26
26
|
: requiredVersion;
|
|
27
27
|
}
|
|
28
|
-
function resolveAbilityRegistryPath(projectDir) {
|
|
28
|
+
async function resolveAbilityRegistryPath(projectDir) {
|
|
29
29
|
const abilitiesDir = path.join(projectDir, "src", "abilities");
|
|
30
|
-
|
|
30
|
+
for (const candidatePath of [
|
|
31
|
+
path.join(abilitiesDir, "index.ts"),
|
|
32
|
+
path.join(abilitiesDir, "index.js"),
|
|
33
|
+
]) {
|
|
34
|
+
if (await pathExists(candidatePath)) {
|
|
35
|
+
return candidatePath;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return path.join(abilitiesDir, "index.ts");
|
|
31
39
|
}
|
|
32
|
-
function readAbilityRegistrySlugs(registryPath) {
|
|
33
|
-
|
|
40
|
+
async function readAbilityRegistrySlugs(registryPath) {
|
|
41
|
+
const source = await readOptionalUtf8File(registryPath);
|
|
42
|
+
if (source === null) {
|
|
34
43
|
return [];
|
|
35
44
|
}
|
|
36
|
-
|
|
37
|
-
return Array.from(source.matchAll(/^\s*export\s+\*\s+from\s+['"]\.\/([^/'"]+)\/client['"];?\s*$/gmu)).map((match) => match[1]);
|
|
45
|
+
return Array.from(source.matchAll(/^\s*export\s+\*\s+from\s+['"]\.\/([^/'"]+)\/client(?:\.[cm]?[jt]sx?)?['"];?\s*$/gmu)).map((match) => match[1]);
|
|
38
46
|
}
|
|
39
47
|
async function writeAbilityRegistry(projectDir, abilitySlug) {
|
|
40
48
|
const abilitiesDir = path.join(projectDir, "src", "abilities");
|
|
41
|
-
const registryPath = resolveAbilityRegistryPath(projectDir);
|
|
49
|
+
const registryPath = await resolveAbilityRegistryPath(projectDir);
|
|
42
50
|
await fsp.mkdir(abilitiesDir, { recursive: true });
|
|
43
|
-
const existingAbilitySlugs =
|
|
44
|
-
const existingRegistrySlugs = readAbilityRegistrySlugs(registryPath);
|
|
51
|
+
const existingAbilitySlugs = (await readWorkspaceInventoryAsync(projectDir)).abilities.map((entry) => entry.slug);
|
|
52
|
+
const existingRegistrySlugs = await readAbilityRegistrySlugs(registryPath);
|
|
45
53
|
const nextAbilitySlugs = Array.from(new Set([...existingAbilitySlugs, ...existingRegistrySlugs, abilitySlug])).sort();
|
|
46
54
|
const generatedSection = buildAbilityRegistrySource(nextAbilitySlugs);
|
|
47
|
-
const existingSource =
|
|
48
|
-
? fs.readFileSync(registryPath, "utf8")
|
|
49
|
-
: "";
|
|
55
|
+
const existingSource = (await readOptionalUtf8File(registryPath)) ?? "";
|
|
50
56
|
const generatedSectionPattern = new RegExp(`${escapeRegex(ABILITY_REGISTRY_START_MARKER)}[\\s\\S]*?${escapeRegex(ABILITY_REGISTRY_END_MARKER)}\\n?`, "u");
|
|
51
57
|
const nextSource = existingSource
|
|
52
58
|
? generatedSectionPattern.test(existingSource)
|
|
@@ -318,7 +324,7 @@ export async function scaffoldAbilityWorkspace({ abilitySlug, compatibilityPolic
|
|
|
318
324
|
const syncAbilitiesScriptPath = path.join(workspace.projectDir, "scripts", "sync-abilities.ts");
|
|
319
325
|
const syncProjectScriptPath = path.join(workspace.projectDir, "scripts", "sync-project.ts");
|
|
320
326
|
const webpackConfigPath = path.join(workspace.projectDir, "webpack.config.js");
|
|
321
|
-
const abilitiesIndexPath = resolveAbilityRegistryPath(workspace.projectDir);
|
|
327
|
+
const abilitiesIndexPath = await resolveAbilityRegistryPath(workspace.projectDir);
|
|
322
328
|
const abilityDir = path.join(workspace.projectDir, "src", "abilities", abilitySlug);
|
|
323
329
|
const configFilePath = path.join(abilityDir, "ability.config.json");
|
|
324
330
|
const typesFilePath = path.join(abilityDir, "types.ts");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assertAbilityDoesNotExist, assertValidGeneratedSlug, normalizeBlockSlug, } from "./cli-add-shared.js";
|
|
2
2
|
import { scaffoldAbilityWorkspace } from "./cli-add-workspace-ability-scaffold.js";
|
|
3
|
-
import {
|
|
3
|
+
import { readWorkspaceInventoryAsync } from "./workspace-inventory.js";
|
|
4
4
|
import { resolveWorkspaceProject } from "./workspace-project.js";
|
|
5
5
|
import { REQUIRED_WORKSPACE_ABILITY_COMPATIBILITY, resolveScaffoldCompatibilityPolicy, } from "./scaffold-compatibility.js";
|
|
6
6
|
/**
|
|
@@ -12,7 +12,7 @@ import { REQUIRED_WORKSPACE_ABILITY_COMPATIBILITY, resolveScaffoldCompatibilityP
|
|
|
12
12
|
export async function runAddAbilityCommand({ abilityName, cwd = process.cwd(), }) {
|
|
13
13
|
const workspace = resolveWorkspaceProject(cwd);
|
|
14
14
|
const abilitySlug = assertValidGeneratedSlug("Ability name", normalizeBlockSlug(abilityName), "wp-typia add ability <name>");
|
|
15
|
-
const inventory =
|
|
15
|
+
const inventory = await readWorkspaceInventoryAsync(workspace.projectDir);
|
|
16
16
|
assertAbilityDoesNotExist(workspace.projectDir, abilitySlug, inventory);
|
|
17
17
|
const compatibilityPolicy = resolveScaffoldCompatibilityPolicy(REQUIRED_WORKSPACE_ABILITY_COMPATIBILITY);
|
|
18
18
|
const scaffoldResult = await scaffoldAbilityWorkspace({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
1
|
import { promises as fsp } from 'node:fs';
|
|
3
2
|
import path from 'node:path';
|
|
4
|
-
import {
|
|
3
|
+
import { pathExists, readOptionalUtf8File } from './fs-async.js';
|
|
4
|
+
import { appendWorkspaceInventoryEntries, readWorkspaceInventoryAsync, } from './workspace-inventory.js';
|
|
5
5
|
import { buildAdminViewConfigEntry, buildAdminViewConfigSource, buildAdminViewEntrySource, buildAdminViewPhpSource, buildAdminViewRegistrySource, buildAdminViewScreenSource, buildAdminViewStyleSource, buildAdminViewTypesSource, buildCoreDataAdminViewDataSource, buildCoreDataAdminViewScreenSource, buildDefaultAdminViewDataSource, buildRestAdminViewDataSource, } from './cli-add-workspace-admin-view-templates.js';
|
|
6
6
|
import { ADMIN_VIEWS_PHP_GLOB, isAdminViewCoreDataSource, } from './cli-add-workspace-admin-view-types.js';
|
|
7
7
|
import { getWorkspaceBootstrapPath, patchFile, } from './cli-add-shared.js';
|
|
@@ -152,27 +152,31 @@ async function ensureAdminViewWebpackAnchors(workspace) {
|
|
|
152
152
|
return nextSource;
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
|
-
function resolveAdminViewRegistryPath(projectDir) {
|
|
155
|
+
async function resolveAdminViewRegistryPath(projectDir) {
|
|
156
156
|
const adminViewsDir = path.join(projectDir, 'src', 'admin-views');
|
|
157
|
-
|
|
157
|
+
for (const candidatePath of [
|
|
158
158
|
path.join(adminViewsDir, 'index.ts'),
|
|
159
159
|
path.join(adminViewsDir, 'index.js'),
|
|
160
|
-
]
|
|
161
|
-
|
|
160
|
+
]) {
|
|
161
|
+
if (await pathExists(candidatePath)) {
|
|
162
|
+
return candidatePath;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return path.join(adminViewsDir, 'index.ts');
|
|
162
166
|
}
|
|
163
|
-
function readAdminViewRegistrySlugs(registryPath) {
|
|
164
|
-
|
|
167
|
+
async function readAdminViewRegistrySlugs(registryPath) {
|
|
168
|
+
const source = await readOptionalUtf8File(registryPath);
|
|
169
|
+
if (source === null) {
|
|
165
170
|
return [];
|
|
166
171
|
}
|
|
167
|
-
const source = fs.readFileSync(registryPath, 'utf8');
|
|
168
172
|
return Array.from(source.matchAll(/^\s*import\s+['"]\.\/([^/'"]+)(?:\/index(?:\.[cm]?[jt]sx?)?)?['"];?\s*$/gmu)).map((match) => match[1]);
|
|
169
173
|
}
|
|
170
174
|
async function writeAdminViewRegistry(projectDir, adminViewSlug) {
|
|
171
175
|
const adminViewsDir = path.join(projectDir, 'src', 'admin-views');
|
|
172
|
-
const registryPath = resolveAdminViewRegistryPath(projectDir);
|
|
176
|
+
const registryPath = await resolveAdminViewRegistryPath(projectDir);
|
|
173
177
|
await fsp.mkdir(adminViewsDir, { recursive: true });
|
|
174
|
-
const existingAdminViewSlugs =
|
|
175
|
-
const existingRegistrySlugs = readAdminViewRegistrySlugs(registryPath);
|
|
178
|
+
const existingAdminViewSlugs = (await readWorkspaceInventoryAsync(projectDir)).adminViews.map((entry) => entry.slug);
|
|
179
|
+
const existingRegistrySlugs = await readAdminViewRegistrySlugs(registryPath);
|
|
176
180
|
const nextAdminViewSlugs = Array.from(new Set([
|
|
177
181
|
...existingAdminViewSlugs,
|
|
178
182
|
...existingRegistrySlugs,
|
|
@@ -187,7 +191,7 @@ export async function scaffoldAdminViewWorkspace(options) {
|
|
|
187
191
|
const buildScriptPath = path.join(workspace.projectDir, 'scripts', 'build-workspace.mjs');
|
|
188
192
|
const packageJsonPath = path.join(workspace.projectDir, 'package.json');
|
|
189
193
|
const webpackConfigPath = path.join(workspace.projectDir, 'webpack.config.js');
|
|
190
|
-
const adminViewsIndexPath = resolveAdminViewRegistryPath(workspace.projectDir);
|
|
194
|
+
const adminViewsIndexPath = await resolveAdminViewRegistryPath(workspace.projectDir);
|
|
191
195
|
const adminViewDir = path.join(workspace.projectDir, 'src', 'admin-views', adminViewSlug);
|
|
192
196
|
const adminViewPhpPath = path.join(workspace.projectDir, 'inc', 'admin-views', `${adminViewSlug}.php`);
|
|
193
197
|
await executeWorkspaceMutationPlan({
|
|
@@ -2,7 +2,7 @@ import { assertAdminViewDoesNotExist, assertValidGeneratedSlug, normalizeBlockSl
|
|
|
2
2
|
import { assertAdminViewPackageAvailability, parseAdminViewSource, resolveAdminViewCoreDataSource, resolveRestResourceSource, } from './cli-add-workspace-admin-view-source.js';
|
|
3
3
|
import { scaffoldAdminViewWorkspace } from './cli-add-workspace-admin-view-scaffold.js';
|
|
4
4
|
import { formatAdminViewSourceLocator } from './cli-add-workspace-admin-view-types.js';
|
|
5
|
-
import {
|
|
5
|
+
import { readWorkspaceInventoryAsync } from './workspace-inventory.js';
|
|
6
6
|
import { resolveWorkspaceProject } from './workspace-project.js';
|
|
7
7
|
const ADD_ADMIN_VIEW_USAGE = 'wp-typia add admin-view <name> [--source <rest-resource:slug|core-data:kind/name>]';
|
|
8
8
|
/**
|
|
@@ -14,7 +14,7 @@ export async function runAddAdminViewCommand({ adminViewName, cwd = process.cwd(
|
|
|
14
14
|
assertAdminViewPackageAvailability();
|
|
15
15
|
const adminViewSlug = assertValidGeneratedSlug('Admin view name', normalizeBlockSlug(adminViewName), ADD_ADMIN_VIEW_USAGE);
|
|
16
16
|
const parsedSource = parseAdminViewSource(source);
|
|
17
|
-
const inventory =
|
|
17
|
+
const inventory = await readWorkspaceInventoryAsync(workspace.projectDir);
|
|
18
18
|
const restResource = resolveRestResourceSource(inventory.restResources, parsedSource);
|
|
19
19
|
const coreDataSource = resolveAdminViewCoreDataSource(parsedSource);
|
|
20
20
|
assertAdminViewDoesNotExist(workspace.projectDir, adminViewSlug, inventory);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assertAiFeatureDoesNotExist, assertValidGeneratedSlug, normalizeBlockSlug, resolveRestResourceNamespace, } from "./cli-add-shared.js";
|
|
2
2
|
import { scaffoldAiFeatureWorkspace } from "./cli-add-workspace-ai-scaffold.js";
|
|
3
|
-
import {
|
|
3
|
+
import { readWorkspaceInventoryAsync } from "./workspace-inventory.js";
|
|
4
4
|
import { resolveWorkspaceProject } from "./workspace-project.js";
|
|
5
5
|
import { OPTIONAL_WORDPRESS_AI_CLIENT_COMPATIBILITY, resolveScaffoldCompatibilityPolicy, } from "./scaffold-compatibility.js";
|
|
6
6
|
/**
|
|
@@ -15,7 +15,7 @@ export async function runAddAiFeatureCommand({ aiFeatureName, cwd = process.cwd(
|
|
|
15
15
|
const aiFeatureSlug = assertValidGeneratedSlug("AI feature name", normalizeBlockSlug(aiFeatureName), "wp-typia add ai-feature <name> [--namespace <vendor/v1>]");
|
|
16
16
|
const resolvedNamespace = resolveRestResourceNamespace(workspace.workspace.namespace, namespace);
|
|
17
17
|
const compatibilityPolicy = resolveScaffoldCompatibilityPolicy(OPTIONAL_WORDPRESS_AI_CLIENT_COMPATIBILITY);
|
|
18
|
-
const inventory =
|
|
18
|
+
const inventory = await readWorkspaceInventoryAsync(workspace.projectDir);
|
|
19
19
|
assertAiFeatureDoesNotExist(workspace.projectDir, aiFeatureSlug, inventory);
|
|
20
20
|
const scaffoldResult = await scaffoldAiFeatureWorkspace({
|
|
21
21
|
aiFeatureSlug,
|