@wp-typia/project-tools 0.22.6 → 0.22.8
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-json.d.ts +2 -2
- package/dist/runtime/cli-add-block-json.js +5 -4
- package/dist/runtime/cli-add-block.js +4 -3
- 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 +14 -49
- package/dist/runtime/cli-diagnostics.js +6 -0
- package/dist/runtime/cli-init-plan-presentation.d.ts +16 -0
- package/dist/runtime/cli-init-plan-presentation.js +74 -0
- package/dist/runtime/cli-init-plan.js +5 -77
- package/dist/runtime/cli-scaffold.js +2 -1
- package/dist/runtime/create-template-validation.d.ts +10 -0
- package/dist/runtime/create-template-validation.js +121 -0
- package/dist/runtime/package-versions.d.ts +1 -1
- package/dist/runtime/package-versions.js +16 -3
- package/dist/runtime/php-utils.js +151 -148
- 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/string-case.d.ts +2 -4
- package/dist/runtime/string-case.js +15 -4
- 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/ts-property-names.d.ts +11 -0
- package/dist/runtime/ts-property-names.js +16 -0
- package/dist/runtime/workspace-inventory.d.ts +13 -1
- package/dist/runtime/workspace-inventory.js +35 -9
- package/package.json +6 -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),
|
|
@@ -16,10 +16,10 @@ export declare function resolveWorkspaceBlock(inventory: WorkspaceInventory, blo
|
|
|
16
16
|
* @returns Parsed block metadata and the absolute `block.json` path.
|
|
17
17
|
* @throws {Error} When the file is missing or cannot be parsed as scaffold metadata.
|
|
18
18
|
*/
|
|
19
|
-
export declare function readWorkspaceBlockJson(projectDir: string, blockSlug: string): {
|
|
19
|
+
export declare function readWorkspaceBlockJson(projectDir: string, blockSlug: string): Promise<{
|
|
20
20
|
blockJson: Record<string, unknown>;
|
|
21
21
|
blockJsonPath: string;
|
|
22
|
-
}
|
|
22
|
+
}>;
|
|
23
23
|
/**
|
|
24
24
|
* Return a mutable `blockHooks` object for a parsed block metadata document.
|
|
25
25
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import { parseScaffoldBlockMetadata } from "@wp-typia/block-runtime/blocks";
|
|
3
|
+
import { readOptionalUtf8File } from "./fs-async.js";
|
|
4
4
|
/**
|
|
5
5
|
* Resolve an existing workspace block inventory entry by slug.
|
|
6
6
|
*
|
|
@@ -24,14 +24,15 @@ export function resolveWorkspaceBlock(inventory, blockSlug) {
|
|
|
24
24
|
* @returns Parsed block metadata and the absolute `block.json` path.
|
|
25
25
|
* @throws {Error} When the file is missing or cannot be parsed as scaffold metadata.
|
|
26
26
|
*/
|
|
27
|
-
export function readWorkspaceBlockJson(projectDir, blockSlug) {
|
|
27
|
+
export async function readWorkspaceBlockJson(projectDir, blockSlug) {
|
|
28
28
|
const blockJsonPath = path.join(projectDir, "src", "blocks", blockSlug, "block.json");
|
|
29
|
-
|
|
29
|
+
const source = await readOptionalUtf8File(blockJsonPath);
|
|
30
|
+
if (source === null) {
|
|
30
31
|
throw new Error(`Missing ${path.relative(projectDir, blockJsonPath)} for workspace block "${blockSlug}".`);
|
|
31
32
|
}
|
|
32
33
|
let blockJson;
|
|
33
34
|
try {
|
|
34
|
-
blockJson = parseScaffoldBlockMetadata(JSON.parse(
|
|
35
|
+
blockJson = parseScaffoldBlockMetadata(JSON.parse(source));
|
|
35
36
|
}
|
|
36
37
|
catch (error) {
|
|
37
38
|
throw new Error(error instanceof Error
|
|
@@ -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";
|
|
@@ -84,7 +85,7 @@ async function copyScaffoldedBlockSlice(projectDir, templateId, tempProjectDir,
|
|
|
84
85
|
await ensureCompoundWorkspaceSupportFiles(projectDir, tempProjectDir, legacyValidatorPaths);
|
|
85
86
|
await copyTempDirectory(path.join(tempProjectDir, "src", "blocks", variables.slugKebabCase), path.join(projectDir, "src", "blocks", variables.slugKebabCase));
|
|
86
87
|
await copyTempDirectory(path.join(tempProjectDir, "src", "blocks", `${variables.slugKebabCase}-item`), path.join(projectDir, "src", "blocks", `${variables.slugKebabCase}-item`));
|
|
87
|
-
if (variables
|
|
88
|
+
if (isCompoundPersistenceEnabled(variables)) {
|
|
88
89
|
await renderWorkspacePersistenceServerModule(projectDir, variables);
|
|
89
90
|
}
|
|
90
91
|
return;
|
|
@@ -196,7 +197,7 @@ async function syncWorkspaceAddedBlockArtifacts(projectDir, templateId, variable
|
|
|
196
197
|
await syncWorkspaceBlockMetadata(projectDir, `${variables.slugKebabCase}-item`, `${variables.pascalCase}ItemAttributes`, path.join("src", "blocks", `${variables.slugKebabCase}-item`, "types.ts"));
|
|
197
198
|
}
|
|
198
199
|
if (templateId === "persistence" ||
|
|
199
|
-
(templateId === "compound" && variables
|
|
200
|
+
(templateId === "compound" && isCompoundPersistenceEnabled(variables))) {
|
|
200
201
|
await syncWorkspacePersistenceArtifacts(projectDir, variables);
|
|
201
202
|
}
|
|
202
203
|
}
|
|
@@ -390,7 +391,7 @@ export async function runAddBlockCommand({ alternateRenderTargets, blockName, cw
|
|
|
390
391
|
await addCollectionImportsForTemplate(workspace.projectDir, resolvedTemplateId, result.variables);
|
|
391
392
|
await appendBlockConfigEntries(workspace.projectDir, buildConfigEntries(resolvedTemplateId, result.variables), resolvedTemplateId === "persistence" ||
|
|
392
393
|
(resolvedTemplateId === "compound" &&
|
|
393
|
-
result.variables
|
|
394
|
+
isCompoundPersistenceEnabled(result.variables)));
|
|
394
395
|
await syncWorkspaceAddedBlockArtifacts(workspace.projectDir, resolvedTemplateId, result.variables);
|
|
395
396
|
await updateWorkspaceMigrationConfigIfPresent(workspace.projectDir, buildMigrationBlocks(resolvedTemplateId, result.variables));
|
|
396
397
|
return {
|
|
@@ -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,
|