@wp-typia/project-tools 0.11.1
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/README.md +32 -0
- package/dist/runtime/cli-add.d.ts +38 -0
- package/dist/runtime/cli-add.js +561 -0
- package/dist/runtime/cli-core.d.ts +25 -0
- package/dist/runtime/cli-core.js +25 -0
- package/dist/runtime/cli-doctor.d.ts +34 -0
- package/dist/runtime/cli-doctor.js +131 -0
- package/dist/runtime/cli-help.d.ts +9 -0
- package/dist/runtime/cli-help.js +37 -0
- package/dist/runtime/cli-prompt.d.ts +21 -0
- package/dist/runtime/cli-prompt.js +53 -0
- package/dist/runtime/cli-scaffold.d.ts +79 -0
- package/dist/runtime/cli-scaffold.js +206 -0
- package/dist/runtime/cli-templates.d.ts +30 -0
- package/dist/runtime/cli-templates.js +61 -0
- package/dist/runtime/index.d.ts +9 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/json-utils.d.ts +10 -0
- package/dist/runtime/json-utils.js +12 -0
- package/dist/runtime/local-dev-presets.d.ts +26 -0
- package/dist/runtime/local-dev-presets.js +132 -0
- package/dist/runtime/metadata-analysis.d.ts +11 -0
- package/dist/runtime/metadata-analysis.js +285 -0
- package/dist/runtime/metadata-model.d.ts +84 -0
- package/dist/runtime/metadata-model.js +59 -0
- package/dist/runtime/metadata-parser.d.ts +53 -0
- package/dist/runtime/metadata-parser.js +794 -0
- package/dist/runtime/metadata-php-render.d.ts +29 -0
- package/dist/runtime/metadata-php-render.js +549 -0
- package/dist/runtime/metadata-projection.d.ts +7 -0
- package/dist/runtime/metadata-projection.js +233 -0
- package/dist/runtime/migration-constants.d.ts +15 -0
- package/dist/runtime/migration-constants.js +16 -0
- package/dist/runtime/migration-diff.d.ts +2 -0
- package/dist/runtime/migration-diff.js +537 -0
- package/dist/runtime/migration-fixtures.d.ts +8 -0
- package/dist/runtime/migration-fixtures.js +94 -0
- package/dist/runtime/migration-fuzz-plan.d.ts +2 -0
- package/dist/runtime/migration-fuzz-plan.js +50 -0
- package/dist/runtime/migration-manifest.d.ts +19 -0
- package/dist/runtime/migration-manifest.js +129 -0
- package/dist/runtime/migration-project.d.ts +94 -0
- package/dist/runtime/migration-project.js +1101 -0
- package/dist/runtime/migration-render.d.ts +11 -0
- package/dist/runtime/migration-render.js +741 -0
- package/dist/runtime/migration-risk.d.ts +4 -0
- package/dist/runtime/migration-risk.js +52 -0
- package/dist/runtime/migration-types.d.ts +249 -0
- package/dist/runtime/migration-types.js +1 -0
- package/dist/runtime/migration-ui-capability.d.ts +17 -0
- package/dist/runtime/migration-ui-capability.js +190 -0
- package/dist/runtime/migration-utils.d.ts +69 -0
- package/dist/runtime/migration-utils.js +246 -0
- package/dist/runtime/migrations.d.ts +249 -0
- package/dist/runtime/migrations.js +1061 -0
- package/dist/runtime/object-utils.d.ts +12 -0
- package/dist/runtime/object-utils.js +14 -0
- package/dist/runtime/package-managers.d.ts +28 -0
- package/dist/runtime/package-managers.js +156 -0
- package/dist/runtime/package-versions.d.ts +10 -0
- package/dist/runtime/package-versions.js +68 -0
- package/dist/runtime/scaffold-onboarding.d.ts +32 -0
- package/dist/runtime/scaffold-onboarding.js +99 -0
- package/dist/runtime/scaffold.d.ts +146 -0
- package/dist/runtime/scaffold.js +612 -0
- package/dist/runtime/schema-core.d.ts +267 -0
- package/dist/runtime/schema-core.js +597 -0
- package/dist/runtime/starter-manifests.d.ts +25 -0
- package/dist/runtime/starter-manifests.js +383 -0
- package/dist/runtime/string-case.d.ts +36 -0
- package/dist/runtime/string-case.js +69 -0
- package/dist/runtime/template-builtins.d.ts +38 -0
- package/dist/runtime/template-builtins.js +72 -0
- package/dist/runtime/template-defaults.d.ts +75 -0
- package/dist/runtime/template-defaults.js +65 -0
- package/dist/runtime/template-registry.d.ts +36 -0
- package/dist/runtime/template-registry.js +94 -0
- package/dist/runtime/template-render.d.ts +24 -0
- package/dist/runtime/template-render.js +113 -0
- package/dist/runtime/template-source.d.ts +71 -0
- package/dist/runtime/template-source.js +821 -0
- package/dist/runtime/typia-tags.d.ts +1 -0
- package/dist/runtime/typia-tags.js +1 -0
- package/package.json +79 -0
- package/templates/_shared/base/languages/.gitkeep +1 -0
- package/templates/_shared/base/package.json.mustache +41 -0
- package/templates/_shared/base/scripts/sync-types-to-block-json.ts.mustache +118 -0
- package/templates/_shared/base/src/hooks.ts.mustache +19 -0
- package/templates/_shared/base/src/validator-toolkit.ts.mustache +31 -0
- package/templates/_shared/base/tsconfig.json.mustache +21 -0
- package/templates/_shared/base/webpack.config.js.mustache +99 -0
- package/templates/_shared/base/{{slugKebabCase}}.php.mustache +53 -0
- package/templates/_shared/compound/core/package.json.mustache +45 -0
- package/templates/_shared/compound/core/scripts/add-compound-child.ts.mustache +559 -0
- package/templates/_shared/compound/core/scripts/block-config.ts.mustache +13 -0
- package/templates/_shared/compound/core/scripts/sync-types-to-block-json.ts.mustache +53 -0
- package/templates/_shared/compound/core/webpack.config.js.mustache +141 -0
- package/templates/_shared/compound/core/{{slugKebabCase}}.php.mustache +51 -0
- package/templates/_shared/compound/persistence/package.json.mustache +50 -0
- package/templates/_shared/compound/persistence/scripts/block-config.ts.mustache +59 -0
- package/templates/_shared/compound/persistence/scripts/sync-rest-contracts.ts.mustache +101 -0
- package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api-types.ts.mustache +21 -0
- package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api-validators.ts.mustache +32 -0
- package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/api.ts.mustache +68 -0
- package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/block.json.mustache +52 -0
- package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/data.ts.mustache +192 -0
- package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/edit.tsx.mustache +123 -0
- package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/hooks.ts.mustache +11 -0
- package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/interactivity.ts.mustache +132 -0
- package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/render.php.mustache +158 -0
- package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/save.tsx.mustache +3 -0
- package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/types.ts.mustache +56 -0
- package/templates/_shared/compound/persistence/src/blocks/{{slugKebabCase}}/validators.ts.mustache +32 -0
- package/templates/_shared/compound/persistence-auth/{{slugKebabCase}}.php.mustache +294 -0
- package/templates/_shared/compound/persistence-public/{{slugKebabCase}}.php.mustache +312 -0
- package/templates/_shared/migration-ui/common/src/admin/migration-dashboard.tsx +394 -0
- package/templates/_shared/migration-ui/common/src/migration-detector.ts +9 -0
- package/templates/_shared/migration-ui/common/src/migrations/helpers.ts +490 -0
- package/templates/_shared/migration-ui/common/src/migrations/index.ts +886 -0
- package/templates/_shared/persistence/auth/{{slugKebabCase}}.php.mustache +290 -0
- package/templates/_shared/persistence/core/package.json.mustache +46 -0
- package/templates/_shared/persistence/core/scripts/sync-rest-contracts.ts.mustache +113 -0
- package/templates/_shared/persistence/core/scripts/sync-types-to-block-json.ts.mustache +125 -0
- package/templates/_shared/persistence/core/src/api-types.ts.mustache +21 -0
- package/templates/_shared/persistence/core/src/api-validators.ts.mustache +32 -0
- package/templates/_shared/persistence/core/src/api.ts.mustache +68 -0
- package/templates/_shared/persistence/core/src/data.ts.mustache +192 -0
- package/templates/_shared/persistence/core/src/index.tsx.mustache +25 -0
- package/templates/_shared/persistence/core/src/interactivity.ts.mustache +134 -0
- package/templates/_shared/persistence/core/src/save.tsx.mustache +5 -0
- package/templates/_shared/persistence/core/src/validators.ts.mustache +32 -0
- package/templates/_shared/persistence/core/{{slugKebabCase}}.php.mustache +336 -0
- package/templates/_shared/persistence/public/{{slugKebabCase}}.php.mustache +308 -0
- package/templates/_shared/presets/test-preset/.wp-env.test.json.mustache +16 -0
- package/templates/_shared/presets/test-preset/playwright.config.ts.mustache +22 -0
- package/templates/_shared/presets/test-preset/scripts/wait-for-wp-env.mjs.mustache +102 -0
- package/templates/_shared/presets/test-preset/scripts/wp-env-utils.cjs.mustache +32 -0
- package/templates/_shared/presets/test-preset/tests/e2e/smoke.spec.ts.mustache +34 -0
- package/templates/_shared/presets/wp-env/.wp-env.json.mustache +16 -0
- package/templates/_shared/rest-helpers/auth/inc/rest-auth.php.mustache +37 -0
- package/templates/_shared/rest-helpers/public/inc/rest-public.php.mustache +314 -0
- package/templates/_shared/rest-helpers/shared/inc/rest-shared.php.mustache +58 -0
- package/templates/_shared/workspace/persistence-auth/inc/rest-auth.php.mustache +36 -0
- package/templates/_shared/workspace/persistence-auth/inc/rest-shared.php.mustache +55 -0
- package/templates/_shared/workspace/persistence-auth/server.php.mustache +237 -0
- package/templates/_shared/workspace/persistence-public/inc/rest-public.php.mustache +273 -0
- package/templates/_shared/workspace/persistence-public/inc/rest-shared.php.mustache +55 -0
- package/templates/_shared/workspace/persistence-public/server.php.mustache +252 -0
- package/templates/basic/src/block.json.mustache +51 -0
- package/templates/basic/src/edit.tsx.mustache +128 -0
- package/templates/basic/src/editor.scss.mustache +8 -0
- package/templates/basic/src/hooks.ts.mustache +18 -0
- package/templates/basic/src/index.tsx.mustache +45 -0
- package/templates/basic/src/save.tsx.mustache +30 -0
- package/templates/basic/src/style.scss.mustache +40 -0
- package/templates/basic/src/types.ts.mustache +56 -0
- package/templates/basic/src/validators.ts.mustache +26 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}/block.json.mustache +37 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}/children.ts.mustache +25 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}/edit.tsx.mustache +93 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}/hooks.ts.mustache +11 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}/index.tsx.mustache +25 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}/save.tsx.mustache +32 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}/style.scss.mustache +31 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}/types.ts.mustache +13 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}/validators.ts.mustache +17 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}-item/block.json.mustache +35 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}-item/edit.tsx.mustache +50 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}-item/hooks.ts.mustache +11 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}-item/index.tsx.mustache +25 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}-item/save.tsx.mustache +24 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}-item/types.ts.mustache +12 -0
- package/templates/compound/src/blocks/{{slugKebabCase}}-item/validators.ts.mustache +17 -0
- package/templates/interactivity/package.json.mustache +42 -0
- package/templates/interactivity/src/block.json.mustache +73 -0
- package/templates/interactivity/src/edit.tsx.mustache +270 -0
- package/templates/interactivity/src/index.tsx.mustache +32 -0
- package/templates/interactivity/src/interactivity.ts.mustache +152 -0
- package/templates/interactivity/src/save.tsx.mustache +101 -0
- package/templates/interactivity/src/style.scss.mustache +60 -0
- package/templates/interactivity/src/types.ts.mustache +32 -0
- package/templates/interactivity/src/validators.ts.mustache +36 -0
- package/templates/persistence/src/block.json.mustache +52 -0
- package/templates/persistence/src/edit.tsx.mustache +165 -0
- package/templates/persistence/src/render.php.mustache +126 -0
- package/templates/persistence/src/style.scss.mustache +46 -0
- package/templates/persistence/src/types.ts.mustache +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# `@wp-typia/project-tools`
|
|
2
|
+
|
|
3
|
+
Programmatic project orchestration package for `wp-typia`.
|
|
4
|
+
|
|
5
|
+
Package roles:
|
|
6
|
+
|
|
7
|
+
- `wp-typia` owns the CLI, help, TUI, completions, skills, MCP, and bin entry.
|
|
8
|
+
- `@wp-typia/project-tools` owns scaffold, add-block, migrate, template, doctor, and schema project helpers.
|
|
9
|
+
- `@wp-typia/block-runtime/*` owns generated-project runtime helpers.
|
|
10
|
+
- `@wp-typia/create` is the deprecated legacy package shell.
|
|
11
|
+
|
|
12
|
+
Supported public imports:
|
|
13
|
+
|
|
14
|
+
- `@wp-typia/project-tools`
|
|
15
|
+
- `@wp-typia/project-tools/schema-core`
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import {
|
|
21
|
+
getTemplateById,
|
|
22
|
+
parseMigrationArgs,
|
|
23
|
+
projectJsonSchemaDocument,
|
|
24
|
+
resolvePackageManagerId,
|
|
25
|
+
} from "@wp-typia/project-tools";
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { normalizeEndpointAuthDefinition } from "@wp-typia/project-tools/schema-core";
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If you need metadata sync, editor helpers, validation helpers, or other generated-project runtime utilities, import them directly from `@wp-typia/block-runtime/*`.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported top-level `wp-typia add` kinds exposed by the canonical CLI.
|
|
3
|
+
*/
|
|
4
|
+
export declare const ADD_KIND_IDS: readonly ["block", "variation", "pattern"];
|
|
5
|
+
export type AddKindId = (typeof ADD_KIND_IDS)[number];
|
|
6
|
+
/**
|
|
7
|
+
* Supported built-in block families accepted by `wp-typia add block --template`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const ADD_BLOCK_TEMPLATE_IDS: readonly ["basic", "interactivity", "persistence", "compound"];
|
|
10
|
+
export type AddBlockTemplateId = (typeof ADD_BLOCK_TEMPLATE_IDS)[number];
|
|
11
|
+
interface RunAddBlockCommandOptions {
|
|
12
|
+
blockName: string;
|
|
13
|
+
cwd?: string;
|
|
14
|
+
dataStorageMode?: string;
|
|
15
|
+
persistencePolicy?: string;
|
|
16
|
+
templateId?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Returns help text for the canonical `wp-typia add` subcommands.
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatAddHelpText(): string;
|
|
22
|
+
/**
|
|
23
|
+
* Seeds an empty official workspace migration project before any blocks are added.
|
|
24
|
+
*/
|
|
25
|
+
export declare function seedWorkspaceMigrationProject(projectDir: string, currentMigrationVersion: string): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Adds one built-in block slice to an official workspace project.
|
|
28
|
+
*/
|
|
29
|
+
export declare function runAddBlockCommand({ blockName, cwd, dataStorageMode, persistencePolicy, templateId, }: RunAddBlockCommandOptions): Promise<{
|
|
30
|
+
blockSlugs: string[];
|
|
31
|
+
projectDir: string;
|
|
32
|
+
templateId: AddBlockTemplateId;
|
|
33
|
+
}>;
|
|
34
|
+
/**
|
|
35
|
+
* Returns the current placeholder guidance for unsupported `wp-typia add` kinds.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createAddPlaceholderMessage(kind: Exclude<AddKindId, "block">): string;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { promises as fsp } from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { defineEndpointManifest, syncBlockMetadata, syncEndpointClient, syncRestOpenApi, syncTypeSchemas, } from "@wp-typia/block-runtime/metadata-core";
|
|
6
|
+
import { ensureMigrationDirectories, parseMigrationConfig, writeInitialMigrationScaffold, writeMigrationConfig, } from "./migration-project.js";
|
|
7
|
+
import { snapshotProjectVersion } from "./migrations.js";
|
|
8
|
+
import { getDefaultAnswers, scaffoldProject } from "./scaffold.js";
|
|
9
|
+
import { SHARED_WORKSPACE_TEMPLATE_ROOT, } from "./template-registry.js";
|
|
10
|
+
import { copyInterpolatedDirectory } from "./template-render.js";
|
|
11
|
+
import { toKebabCase, toSnakeCase, } from "./string-case.js";
|
|
12
|
+
/**
|
|
13
|
+
* Supported top-level `wp-typia add` kinds exposed by the canonical CLI.
|
|
14
|
+
*/
|
|
15
|
+
export const ADD_KIND_IDS = ["block", "variation", "pattern"];
|
|
16
|
+
/**
|
|
17
|
+
* Supported built-in block families accepted by `wp-typia add block --template`.
|
|
18
|
+
*/
|
|
19
|
+
export const ADD_BLOCK_TEMPLATE_IDS = [
|
|
20
|
+
"basic",
|
|
21
|
+
"interactivity",
|
|
22
|
+
"persistence",
|
|
23
|
+
"compound",
|
|
24
|
+
];
|
|
25
|
+
const WORKSPACE_TEMPLATE_PACKAGE = "@wp-typia/create-workspace-template";
|
|
26
|
+
const BLOCK_CONFIG_ENTRY_MARKER = "\t// wp-typia add block entries";
|
|
27
|
+
const COLLECTION_IMPORT_LINE = "import '../../collection';";
|
|
28
|
+
const EMPTY_BLOCKS_ARRAY = `${BLOCK_CONFIG_ENTRY_MARKER}\n];`;
|
|
29
|
+
const REST_MANIFEST_IMPORT_PATTERN = /import\s*\{[^}]*\bdefineEndpointManifest\b[^}]*\}\s*from\s*["']@wp-typia\/block-runtime\/metadata-core["'];?/m;
|
|
30
|
+
function parsePackageManagerId(packageManagerField) {
|
|
31
|
+
const packageManagerId = packageManagerField?.split("@", 1)[0];
|
|
32
|
+
switch (packageManagerId) {
|
|
33
|
+
case "bun":
|
|
34
|
+
case "npm":
|
|
35
|
+
case "pnpm":
|
|
36
|
+
case "yarn":
|
|
37
|
+
return packageManagerId;
|
|
38
|
+
default:
|
|
39
|
+
return "bun";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function normalizeBlockSlug(input) {
|
|
43
|
+
return toKebabCase(input);
|
|
44
|
+
}
|
|
45
|
+
function buildWorkspacePhpPrefix(workspacePhpPrefix, slug) {
|
|
46
|
+
return toSnakeCase(`${workspacePhpPrefix}_${slug}`);
|
|
47
|
+
}
|
|
48
|
+
function isAddBlockTemplateId(value) {
|
|
49
|
+
return ADD_BLOCK_TEMPLATE_IDS.includes(value);
|
|
50
|
+
}
|
|
51
|
+
function quoteTsString(value) {
|
|
52
|
+
return JSON.stringify(value);
|
|
53
|
+
}
|
|
54
|
+
function buildServerTemplateRoot(persistencePolicy) {
|
|
55
|
+
return path.join(SHARED_WORKSPACE_TEMPLATE_ROOT, persistencePolicy === "public" ? "persistence-public" : "persistence-auth");
|
|
56
|
+
}
|
|
57
|
+
function buildSingleBlockConfigEntry(variables) {
|
|
58
|
+
return [
|
|
59
|
+
"\t{",
|
|
60
|
+
`\t\tslug: ${quoteTsString(variables.slugKebabCase)},`,
|
|
61
|
+
`\t\tattributeTypeName: ${quoteTsString(`${variables.pascalCase}Attributes`)},`,
|
|
62
|
+
`\t\ttypesFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}/types.ts`)},`,
|
|
63
|
+
"\t},",
|
|
64
|
+
].join("\n");
|
|
65
|
+
}
|
|
66
|
+
function buildPersistenceBlockConfigEntry(variables) {
|
|
67
|
+
return [
|
|
68
|
+
"\t{",
|
|
69
|
+
`\t\tapiTypesFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}/api-types.ts`)},`,
|
|
70
|
+
`\t\tattributeTypeName: ${quoteTsString(`${variables.pascalCase}Attributes`)},`,
|
|
71
|
+
"\t\trestManifest: defineEndpointManifest( {",
|
|
72
|
+
"\t\t\tcontracts: {",
|
|
73
|
+
"\t\t\t\t'state-query': {",
|
|
74
|
+
`\t\t\t\t\tsourceTypeName: ${quoteTsString(`${variables.pascalCase}StateQuery`)},`,
|
|
75
|
+
"\t\t\t\t},",
|
|
76
|
+
"\t\t\t\t'write-state-request': {",
|
|
77
|
+
`\t\t\t\t\tsourceTypeName: ${quoteTsString(`${variables.pascalCase}WriteStateRequest`)},`,
|
|
78
|
+
"\t\t\t\t},",
|
|
79
|
+
"\t\t\t\t'state-response': {",
|
|
80
|
+
`\t\t\t\t\tsourceTypeName: ${quoteTsString(`${variables.pascalCase}StateResponse`)},`,
|
|
81
|
+
"\t\t\t\t},",
|
|
82
|
+
"\t\t\t},",
|
|
83
|
+
"\t\t\tendpoints: [",
|
|
84
|
+
"\t\t\t\t{",
|
|
85
|
+
"\t\t\t\t\tauth: 'public',",
|
|
86
|
+
"\t\t\t\t\tmethod: 'GET',",
|
|
87
|
+
`\t\t\t\t\toperationId: ${quoteTsString(`get${variables.pascalCase}State`)},`,
|
|
88
|
+
`\t\t\t\t\tpath: ${quoteTsString(`/${variables.namespace}/v1/${variables.slugKebabCase}/state`)},`,
|
|
89
|
+
"\t\t\t\t\tqueryContract: 'state-query',",
|
|
90
|
+
"\t\t\t\t\tresponseContract: 'state-response',",
|
|
91
|
+
`\t\t\t\t\tsummary: 'Read the current persisted state.',`,
|
|
92
|
+
`\t\t\t\t\ttags: [ ${quoteTsString(variables.title)} ],`,
|
|
93
|
+
"\t\t\t\t},",
|
|
94
|
+
"\t\t\t\t{",
|
|
95
|
+
`\t\t\t\t\tauth: ${quoteTsString(variables.restWriteAuthIntent)},`,
|
|
96
|
+
"\t\t\t\t\tbodyContract: 'write-state-request',",
|
|
97
|
+
"\t\t\t\t\tmethod: 'POST',",
|
|
98
|
+
`\t\t\t\t\toperationId: ${quoteTsString(`write${variables.pascalCase}State`)},`,
|
|
99
|
+
`\t\t\t\t\tpath: ${quoteTsString(`/${variables.namespace}/v1/${variables.slugKebabCase}/state`)},`,
|
|
100
|
+
"\t\t\t\t\tresponseContract: 'state-response',",
|
|
101
|
+
`\t\t\t\t\tsummary: 'Write the current persisted state.',`,
|
|
102
|
+
`\t\t\t\t\ttags: [ ${quoteTsString(variables.title)} ],`,
|
|
103
|
+
"\t\t\t\t\twordpressAuth: {",
|
|
104
|
+
`\t\t\t\t\t\tmechanism: ${quoteTsString(variables.restWriteAuthMechanism)},`,
|
|
105
|
+
"\t\t\t\t\t},",
|
|
106
|
+
"\t\t\t\t},",
|
|
107
|
+
"\t\t\t],",
|
|
108
|
+
"\t\t\tinfo: {",
|
|
109
|
+
`\t\t\t\ttitle: ${quoteTsString(`${variables.title} REST API`)},`,
|
|
110
|
+
"\t\t\t\tversion: '1.0.0',",
|
|
111
|
+
"\t\t\t},",
|
|
112
|
+
"\t\t} ),",
|
|
113
|
+
`\t\topenApiFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}/api.openapi.json`)},`,
|
|
114
|
+
`\t\tslug: ${quoteTsString(variables.slugKebabCase)},`,
|
|
115
|
+
`\t\ttypesFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}/types.ts`)},`,
|
|
116
|
+
"\t},",
|
|
117
|
+
].join("\n");
|
|
118
|
+
}
|
|
119
|
+
function buildPersistenceEndpointManifest(variables) {
|
|
120
|
+
return defineEndpointManifest({
|
|
121
|
+
contracts: {
|
|
122
|
+
"state-query": {
|
|
123
|
+
sourceTypeName: `${variables.pascalCase}StateQuery`,
|
|
124
|
+
},
|
|
125
|
+
"write-state-request": {
|
|
126
|
+
sourceTypeName: `${variables.pascalCase}WriteStateRequest`,
|
|
127
|
+
},
|
|
128
|
+
"state-response": {
|
|
129
|
+
sourceTypeName: `${variables.pascalCase}StateResponse`,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
endpoints: [
|
|
133
|
+
{
|
|
134
|
+
auth: "public",
|
|
135
|
+
method: "GET",
|
|
136
|
+
operationId: `get${variables.pascalCase}State`,
|
|
137
|
+
path: `/${variables.namespace}/v1/${variables.slugKebabCase}/state`,
|
|
138
|
+
queryContract: "state-query",
|
|
139
|
+
responseContract: "state-response",
|
|
140
|
+
summary: "Read the current persisted state.",
|
|
141
|
+
tags: [variables.title],
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
auth: variables.restWriteAuthIntent,
|
|
145
|
+
bodyContract: "write-state-request",
|
|
146
|
+
method: "POST",
|
|
147
|
+
operationId: `write${variables.pascalCase}State`,
|
|
148
|
+
path: `/${variables.namespace}/v1/${variables.slugKebabCase}/state`,
|
|
149
|
+
responseContract: "state-response",
|
|
150
|
+
summary: "Write the current persisted state.",
|
|
151
|
+
tags: [variables.title],
|
|
152
|
+
wordpressAuth: {
|
|
153
|
+
mechanism: variables.restWriteAuthMechanism,
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
info: {
|
|
158
|
+
title: `${variables.title} REST API`,
|
|
159
|
+
version: "1.0.0",
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function buildCompoundChildConfigEntry(variables) {
|
|
164
|
+
return [
|
|
165
|
+
"\t{",
|
|
166
|
+
`\t\tslug: ${quoteTsString(`${variables.slugKebabCase}-item`)},`,
|
|
167
|
+
`\t\tattributeTypeName: ${quoteTsString(`${variables.pascalCase}ItemAttributes`)},`,
|
|
168
|
+
`\t\ttypesFile: ${quoteTsString(`src/blocks/${variables.slugKebabCase}-item/types.ts`)},`,
|
|
169
|
+
"\t},",
|
|
170
|
+
].join("\n");
|
|
171
|
+
}
|
|
172
|
+
function buildConfigEntries(templateId, variables) {
|
|
173
|
+
if (templateId === "basic" || templateId === "interactivity") {
|
|
174
|
+
return [buildSingleBlockConfigEntry(variables)];
|
|
175
|
+
}
|
|
176
|
+
if (templateId === "persistence") {
|
|
177
|
+
return [buildPersistenceBlockConfigEntry(variables)];
|
|
178
|
+
}
|
|
179
|
+
if (variables.compoundPersistenceEnabled === "true") {
|
|
180
|
+
return [
|
|
181
|
+
buildPersistenceBlockConfigEntry(variables),
|
|
182
|
+
buildCompoundChildConfigEntry(variables),
|
|
183
|
+
];
|
|
184
|
+
}
|
|
185
|
+
return [
|
|
186
|
+
buildSingleBlockConfigEntry(variables),
|
|
187
|
+
buildCompoundChildConfigEntry(variables),
|
|
188
|
+
];
|
|
189
|
+
}
|
|
190
|
+
function buildMigrationBlocks(templateId, variables) {
|
|
191
|
+
if (templateId === "compound") {
|
|
192
|
+
return [
|
|
193
|
+
{
|
|
194
|
+
blockJsonFile: `src/blocks/${variables.slugKebabCase}/block.json`,
|
|
195
|
+
blockName: `${variables.namespace}/${variables.slugKebabCase}`,
|
|
196
|
+
key: variables.slugKebabCase,
|
|
197
|
+
manifestFile: `src/blocks/${variables.slugKebabCase}/typia.manifest.json`,
|
|
198
|
+
saveFile: `src/blocks/${variables.slugKebabCase}/save.tsx`,
|
|
199
|
+
typesFile: `src/blocks/${variables.slugKebabCase}/types.ts`,
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
blockJsonFile: `src/blocks/${variables.slugKebabCase}-item/block.json`,
|
|
203
|
+
blockName: `${variables.namespace}/${variables.slugKebabCase}-item`,
|
|
204
|
+
key: `${variables.slugKebabCase}-item`,
|
|
205
|
+
manifestFile: `src/blocks/${variables.slugKebabCase}-item/typia.manifest.json`,
|
|
206
|
+
saveFile: `src/blocks/${variables.slugKebabCase}-item/save.tsx`,
|
|
207
|
+
typesFile: `src/blocks/${variables.slugKebabCase}-item/types.ts`,
|
|
208
|
+
},
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
return [
|
|
212
|
+
{
|
|
213
|
+
blockJsonFile: `src/blocks/${variables.slugKebabCase}/block.json`,
|
|
214
|
+
blockName: `${variables.namespace}/${variables.slugKebabCase}`,
|
|
215
|
+
key: variables.slugKebabCase,
|
|
216
|
+
manifestFile: `src/blocks/${variables.slugKebabCase}/typia.manifest.json`,
|
|
217
|
+
saveFile: `src/blocks/${variables.slugKebabCase}/save.tsx`,
|
|
218
|
+
typesFile: `src/blocks/${variables.slugKebabCase}/types.ts`,
|
|
219
|
+
},
|
|
220
|
+
];
|
|
221
|
+
}
|
|
222
|
+
async function patchFile(filePath, transform) {
|
|
223
|
+
const currentSource = await fsp.readFile(filePath, "utf8");
|
|
224
|
+
const nextSource = transform(currentSource);
|
|
225
|
+
if (nextSource !== currentSource) {
|
|
226
|
+
await fsp.writeFile(filePath, nextSource, "utf8");
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async function readOptionalFile(filePath) {
|
|
230
|
+
if (!fs.existsSync(filePath)) {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
return fsp.readFile(filePath, "utf8");
|
|
234
|
+
}
|
|
235
|
+
async function restoreOptionalFile(filePath, source) {
|
|
236
|
+
if (source === null) {
|
|
237
|
+
await fsp.rm(filePath, { force: true });
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
await fsp.mkdir(path.dirname(filePath), { recursive: true });
|
|
241
|
+
await fsp.writeFile(filePath, source, "utf8");
|
|
242
|
+
}
|
|
243
|
+
async function ensureCollectionImport(filePath) {
|
|
244
|
+
await patchFile(filePath, (source) => {
|
|
245
|
+
if (source.includes(COLLECTION_IMPORT_LINE)) {
|
|
246
|
+
return source;
|
|
247
|
+
}
|
|
248
|
+
if (source.includes("import metadata from './block.json';")) {
|
|
249
|
+
return source.replace("import metadata from './block.json';", `${COLLECTION_IMPORT_LINE}\nimport metadata from './block.json';`);
|
|
250
|
+
}
|
|
251
|
+
return `${COLLECTION_IMPORT_LINE}\n${source}`;
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
async function copyTempDirectory(sourceDir, targetDir) {
|
|
255
|
+
await fsp.mkdir(path.dirname(targetDir), { recursive: true });
|
|
256
|
+
await fsp.cp(sourceDir, targetDir, { recursive: true });
|
|
257
|
+
}
|
|
258
|
+
async function addCollectionImportsForTemplate(projectDir, templateId, variables) {
|
|
259
|
+
if (templateId === "compound") {
|
|
260
|
+
await ensureCollectionImport(path.join(projectDir, "src", "blocks", variables.slugKebabCase, "index.tsx"));
|
|
261
|
+
await ensureCollectionImport(path.join(projectDir, "src", "blocks", `${variables.slugKebabCase}-item`, "index.tsx"));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
await ensureCollectionImport(path.join(projectDir, "src", "blocks", variables.slugKebabCase, "index.tsx"));
|
|
265
|
+
}
|
|
266
|
+
function ensureBlockConfigCanAddRestManifests(source) {
|
|
267
|
+
const importLine = "import { defineEndpointManifest } from '@wp-typia/block-runtime/metadata-core';";
|
|
268
|
+
if (REST_MANIFEST_IMPORT_PATTERN.test(source)) {
|
|
269
|
+
return source;
|
|
270
|
+
}
|
|
271
|
+
return `${importLine}\n\n${source}`;
|
|
272
|
+
}
|
|
273
|
+
async function appendBlockConfigEntries(projectDir, entries, needsRestManifestImport) {
|
|
274
|
+
const blockConfigPath = path.join(projectDir, "scripts", "block-config.ts");
|
|
275
|
+
await patchFile(blockConfigPath, (source) => {
|
|
276
|
+
let nextSource = source;
|
|
277
|
+
if (needsRestManifestImport) {
|
|
278
|
+
nextSource = ensureBlockConfigCanAddRestManifests(nextSource);
|
|
279
|
+
}
|
|
280
|
+
if (nextSource.includes(BLOCK_CONFIG_ENTRY_MARKER)) {
|
|
281
|
+
return nextSource.replace(BLOCK_CONFIG_ENTRY_MARKER, `${entries.join("\n")}\n${BLOCK_CONFIG_ENTRY_MARKER}`);
|
|
282
|
+
}
|
|
283
|
+
if (nextSource.includes(EMPTY_BLOCKS_ARRAY)) {
|
|
284
|
+
return nextSource.replace(EMPTY_BLOCKS_ARRAY, `${entries.join("\n")}\n];`);
|
|
285
|
+
}
|
|
286
|
+
return nextSource.replace("];", `${entries.join("\n")}\n];`);
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
async function renderWorkspacePersistenceServerModule(projectDir, variables) {
|
|
290
|
+
const targetDir = path.join(projectDir, "src", "blocks", variables.slugKebabCase);
|
|
291
|
+
const templateDir = buildServerTemplateRoot(variables.persistencePolicy);
|
|
292
|
+
await copyInterpolatedDirectory(templateDir, targetDir, variables);
|
|
293
|
+
}
|
|
294
|
+
async function copyScaffoldedBlockSlice(projectDir, templateId, tempProjectDir, variables) {
|
|
295
|
+
if (templateId === "compound") {
|
|
296
|
+
await copyTempDirectory(path.join(tempProjectDir, "src", "blocks", variables.slugKebabCase), path.join(projectDir, "src", "blocks", variables.slugKebabCase));
|
|
297
|
+
await copyTempDirectory(path.join(tempProjectDir, "src", "blocks", `${variables.slugKebabCase}-item`), path.join(projectDir, "src", "blocks", `${variables.slugKebabCase}-item`));
|
|
298
|
+
if (variables.compoundPersistenceEnabled === "true") {
|
|
299
|
+
await renderWorkspacePersistenceServerModule(projectDir, variables);
|
|
300
|
+
}
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
await copyTempDirectory(path.join(tempProjectDir, "src"), path.join(projectDir, "src", "blocks", variables.slugKebabCase));
|
|
304
|
+
if (templateId === "persistence") {
|
|
305
|
+
await renderWorkspacePersistenceServerModule(projectDir, variables);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function collectWorkspaceBlockPaths(projectDir, templateId, variables) {
|
|
309
|
+
if (templateId === "compound") {
|
|
310
|
+
return [
|
|
311
|
+
path.join(projectDir, "src", "blocks", variables.slugKebabCase),
|
|
312
|
+
path.join(projectDir, "src", "blocks", `${variables.slugKebabCase}-item`),
|
|
313
|
+
];
|
|
314
|
+
}
|
|
315
|
+
return [path.join(projectDir, "src", "blocks", variables.slugKebabCase)];
|
|
316
|
+
}
|
|
317
|
+
function assertBlockTargetsDoNotExist(projectDir, templateId, variables) {
|
|
318
|
+
for (const targetPath of collectWorkspaceBlockPaths(projectDir, templateId, variables)) {
|
|
319
|
+
if (fs.existsSync(targetPath)) {
|
|
320
|
+
throw new Error(`A block already exists at ${path.relative(projectDir, targetPath)}. Choose a different name.`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
async function updateWorkspaceMigrationConfigIfPresent(projectDir, newBlocks) {
|
|
325
|
+
const configPath = path.join(projectDir, "src", "migrations", "config.ts");
|
|
326
|
+
if (!fs.existsSync(configPath)) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const configSource = await fsp.readFile(configPath, "utf8");
|
|
330
|
+
const config = parseMigrationConfig(configSource);
|
|
331
|
+
const existingBlocks = Array.isArray(config.blocks) ? config.blocks : [];
|
|
332
|
+
const nextBlocks = [
|
|
333
|
+
...existingBlocks,
|
|
334
|
+
...newBlocks.filter((block) => !existingBlocks.some((existing) => existing.key === block.key)),
|
|
335
|
+
];
|
|
336
|
+
writeMigrationConfig(projectDir, {
|
|
337
|
+
...config,
|
|
338
|
+
blocks: nextBlocks,
|
|
339
|
+
});
|
|
340
|
+
snapshotProjectVersion(projectDir, config.currentMigrationVersion, {
|
|
341
|
+
skipConfigUpdate: true,
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
async function syncWorkspaceBlockMetadata(projectDir, slug, sourceTypeName, typesFile) {
|
|
345
|
+
await syncBlockMetadata({
|
|
346
|
+
blockJsonFile: path.join("src", "blocks", slug, "block.json"),
|
|
347
|
+
jsonSchemaFile: path.join("src", "blocks", slug, "typia.schema.json"),
|
|
348
|
+
manifestFile: path.join("src", "blocks", slug, "typia.manifest.json"),
|
|
349
|
+
openApiFile: path.join("src", "blocks", slug, "typia.openapi.json"),
|
|
350
|
+
projectRoot: projectDir,
|
|
351
|
+
sourceTypeName,
|
|
352
|
+
typesFile,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
async function syncWorkspacePersistenceArtifacts(projectDir, variables) {
|
|
356
|
+
const manifest = buildPersistenceEndpointManifest(variables);
|
|
357
|
+
const apiTypesFile = path.join("src", "blocks", variables.slugKebabCase, "api-types.ts");
|
|
358
|
+
for (const [baseName, contract] of Object.entries(manifest.contracts)) {
|
|
359
|
+
await syncTypeSchemas({
|
|
360
|
+
jsonSchemaFile: path.join("src", "blocks", variables.slugKebabCase, "api-schemas", `${baseName}.schema.json`),
|
|
361
|
+
openApiFile: path.join("src", "blocks", variables.slugKebabCase, "api-schemas", `${baseName}.openapi.json`),
|
|
362
|
+
projectRoot: projectDir,
|
|
363
|
+
sourceTypeName: contract.sourceTypeName,
|
|
364
|
+
typesFile: apiTypesFile,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
await syncRestOpenApi({
|
|
368
|
+
manifest,
|
|
369
|
+
openApiFile: path.join("src", "blocks", variables.slugKebabCase, "api.openapi.json"),
|
|
370
|
+
projectRoot: projectDir,
|
|
371
|
+
typesFile: apiTypesFile,
|
|
372
|
+
});
|
|
373
|
+
await syncEndpointClient({
|
|
374
|
+
clientFile: path.join("src", "blocks", variables.slugKebabCase, "api-client.ts"),
|
|
375
|
+
manifest,
|
|
376
|
+
projectRoot: projectDir,
|
|
377
|
+
typesFile: apiTypesFile,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
async function syncWorkspaceAddedBlockArtifacts(projectDir, templateId, variables) {
|
|
381
|
+
await syncWorkspaceBlockMetadata(projectDir, variables.slugKebabCase, `${variables.pascalCase}Attributes`, path.join("src", "blocks", variables.slugKebabCase, "types.ts"));
|
|
382
|
+
if (templateId === "compound") {
|
|
383
|
+
await syncWorkspaceBlockMetadata(projectDir, `${variables.slugKebabCase}-item`, `${variables.pascalCase}ItemAttributes`, path.join("src", "blocks", `${variables.slugKebabCase}-item`, "types.ts"));
|
|
384
|
+
}
|
|
385
|
+
if (templateId === "persistence" ||
|
|
386
|
+
(templateId === "compound" && variables.compoundPersistenceEnabled === "true")) {
|
|
387
|
+
await syncWorkspacePersistenceArtifacts(projectDir, variables);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function resolveWorkspaceProject(startDir) {
|
|
391
|
+
let currentDir = path.resolve(startDir);
|
|
392
|
+
while (true) {
|
|
393
|
+
const packageJsonPath = path.join(currentDir, "package.json");
|
|
394
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
395
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
396
|
+
if (packageJson.wpTypia?.projectType === "workspace" &&
|
|
397
|
+
packageJson.wpTypia?.templatePackage === WORKSPACE_TEMPLATE_PACKAGE &&
|
|
398
|
+
typeof packageJson.wpTypia.namespace === "string" &&
|
|
399
|
+
typeof packageJson.wpTypia.textDomain === "string" &&
|
|
400
|
+
typeof packageJson.wpTypia.phpPrefix === "string") {
|
|
401
|
+
return {
|
|
402
|
+
author: typeof packageJson.author === "string" ? packageJson.author : "Your Name",
|
|
403
|
+
packageManager: parsePackageManagerId(packageJson.packageManager),
|
|
404
|
+
projectDir: currentDir,
|
|
405
|
+
workspace: {
|
|
406
|
+
namespace: packageJson.wpTypia.namespace,
|
|
407
|
+
phpPrefix: packageJson.wpTypia.phpPrefix,
|
|
408
|
+
projectType: "workspace",
|
|
409
|
+
templatePackage: WORKSPACE_TEMPLATE_PACKAGE,
|
|
410
|
+
textDomain: packageJson.wpTypia.textDomain,
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
const parentDir = path.dirname(currentDir);
|
|
416
|
+
if (parentDir === currentDir) {
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
currentDir = parentDir;
|
|
420
|
+
}
|
|
421
|
+
throw new Error(`This command must run inside a ${WORKSPACE_TEMPLATE_PACKAGE} project. Create one with \`wp-typia create my-plugin --template ${WORKSPACE_TEMPLATE_PACKAGE}\` first.`);
|
|
422
|
+
}
|
|
423
|
+
function assertPersistenceFlagsAllowed(templateId, options) {
|
|
424
|
+
const hasPersistenceFlags = typeof options.dataStorageMode === "string" ||
|
|
425
|
+
typeof options.persistencePolicy === "string";
|
|
426
|
+
if (!hasPersistenceFlags) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (templateId === "persistence" || templateId === "compound") {
|
|
430
|
+
if (typeof options.dataStorageMode === "string" &&
|
|
431
|
+
options.dataStorageMode !== "custom-table" &&
|
|
432
|
+
options.dataStorageMode !== "post-meta") {
|
|
433
|
+
throw new Error(`Unsupported data storage mode "${options.dataStorageMode}". Expected one of: post-meta, custom-table.`);
|
|
434
|
+
}
|
|
435
|
+
if (typeof options.persistencePolicy === "string" &&
|
|
436
|
+
options.persistencePolicy !== "authenticated" &&
|
|
437
|
+
options.persistencePolicy !== "public") {
|
|
438
|
+
throw new Error(`Unsupported persistence policy "${options.persistencePolicy}". Expected one of: authenticated, public.`);
|
|
439
|
+
}
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
throw new Error(`--data-storage and --persistence-policy are supported only for \`wp-typia add block --template persistence\` or \`--template compound\`.`);
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Returns help text for the canonical `wp-typia add` subcommands.
|
|
446
|
+
*/
|
|
447
|
+
export function formatAddHelpText() {
|
|
448
|
+
return `Usage:
|
|
449
|
+
wp-typia add block <name> --template <${ADD_BLOCK_TEMPLATE_IDS.join("|")}> [--data-storage <post-meta|custom-table>] [--persistence-policy <authenticated|public>]
|
|
450
|
+
wp-typia add variation
|
|
451
|
+
wp-typia add pattern
|
|
452
|
+
|
|
453
|
+
Notes:
|
|
454
|
+
\`wp-typia add block\` runs only inside official ${WORKSPACE_TEMPLATE_PACKAGE} workspaces.
|
|
455
|
+
\`wp-typia add variation\` and \`wp-typia add pattern\` are reserved placeholders for follow-up workflows.`;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Seeds an empty official workspace migration project before any blocks are added.
|
|
459
|
+
*/
|
|
460
|
+
export async function seedWorkspaceMigrationProject(projectDir, currentMigrationVersion) {
|
|
461
|
+
writeMigrationConfig(projectDir, {
|
|
462
|
+
blocks: [],
|
|
463
|
+
currentMigrationVersion,
|
|
464
|
+
snapshotDir: "src/migrations/versions",
|
|
465
|
+
supportedMigrationVersions: [currentMigrationVersion],
|
|
466
|
+
});
|
|
467
|
+
ensureMigrationDirectories(projectDir, []);
|
|
468
|
+
writeInitialMigrationScaffold(projectDir, currentMigrationVersion, []);
|
|
469
|
+
}
|
|
470
|
+
async function rollbackWorkspaceMutation(projectDir, snapshot) {
|
|
471
|
+
for (const targetPath of snapshot.targetPaths) {
|
|
472
|
+
await fsp.rm(targetPath, { force: true, recursive: true });
|
|
473
|
+
}
|
|
474
|
+
for (const snapshotDir of snapshot.snapshotDirs) {
|
|
475
|
+
await fsp.rm(snapshotDir, { force: true, recursive: true });
|
|
476
|
+
}
|
|
477
|
+
await restoreOptionalFile(path.join(projectDir, "scripts", "block-config.ts"), snapshot.blockConfigSource);
|
|
478
|
+
await restoreOptionalFile(path.join(projectDir, "src", "migrations", "config.ts"), snapshot.migrationConfigSource);
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Adds one built-in block slice to an official workspace project.
|
|
482
|
+
*/
|
|
483
|
+
export async function runAddBlockCommand({ blockName, cwd = process.cwd(), dataStorageMode, persistencePolicy, templateId = "basic", }) {
|
|
484
|
+
if (!isAddBlockTemplateId(templateId)) {
|
|
485
|
+
throw new Error(`Unknown add-block template "${templateId}". Expected one of: ${ADD_BLOCK_TEMPLATE_IDS.join(", ")}`);
|
|
486
|
+
}
|
|
487
|
+
const resolvedTemplateId = templateId;
|
|
488
|
+
assertPersistenceFlagsAllowed(resolvedTemplateId, { dataStorageMode, persistencePolicy });
|
|
489
|
+
const workspace = resolveWorkspaceProject(cwd);
|
|
490
|
+
const normalizedSlug = normalizeBlockSlug(blockName);
|
|
491
|
+
if (!normalizedSlug) {
|
|
492
|
+
throw new Error("Block name is required. Use `wp-typia add block <name> --template <family>`.");
|
|
493
|
+
}
|
|
494
|
+
const defaults = getDefaultAnswers(normalizedSlug, resolvedTemplateId);
|
|
495
|
+
let tempRoot = "";
|
|
496
|
+
try {
|
|
497
|
+
tempRoot = await fsp.mkdtemp(path.join(os.tmpdir(), "wp-typia-add-block-"));
|
|
498
|
+
const tempProjectDir = path.join(tempRoot, normalizedSlug);
|
|
499
|
+
const blockPhpPrefix = buildWorkspacePhpPrefix(workspace.workspace.phpPrefix, normalizedSlug);
|
|
500
|
+
const blockConfigSource = await readOptionalFile(path.join(workspace.projectDir, "scripts", "block-config.ts"));
|
|
501
|
+
const migrationConfigSource = await readOptionalFile(path.join(workspace.projectDir, "src", "migrations", "config.ts"));
|
|
502
|
+
const migrationConfig = migrationConfigSource === null ? null : parseMigrationConfig(migrationConfigSource);
|
|
503
|
+
const result = await scaffoldProject({
|
|
504
|
+
answers: {
|
|
505
|
+
...defaults,
|
|
506
|
+
author: workspace.author,
|
|
507
|
+
namespace: workspace.workspace.namespace,
|
|
508
|
+
phpPrefix: blockPhpPrefix,
|
|
509
|
+
slug: normalizedSlug,
|
|
510
|
+
textDomain: workspace.workspace.textDomain,
|
|
511
|
+
title: defaults.title,
|
|
512
|
+
},
|
|
513
|
+
cwd: workspace.projectDir,
|
|
514
|
+
dataStorageMode: dataStorageMode,
|
|
515
|
+
noInstall: true,
|
|
516
|
+
packageManager: workspace.packageManager,
|
|
517
|
+
persistencePolicy: persistencePolicy,
|
|
518
|
+
projectDir: tempProjectDir,
|
|
519
|
+
templateId: resolvedTemplateId,
|
|
520
|
+
});
|
|
521
|
+
assertBlockTargetsDoNotExist(workspace.projectDir, resolvedTemplateId, result.variables);
|
|
522
|
+
const mutationSnapshot = {
|
|
523
|
+
blockConfigSource,
|
|
524
|
+
migrationConfigSource,
|
|
525
|
+
snapshotDirs: migrationConfig === null
|
|
526
|
+
? []
|
|
527
|
+
: buildMigrationBlocks(resolvedTemplateId, result.variables).map((block) => path.join(workspace.projectDir, ...migrationConfig.snapshotDir.split("/"), migrationConfig.currentMigrationVersion, block.key)),
|
|
528
|
+
targetPaths: collectWorkspaceBlockPaths(workspace.projectDir, resolvedTemplateId, result.variables),
|
|
529
|
+
};
|
|
530
|
+
try {
|
|
531
|
+
await copyScaffoldedBlockSlice(workspace.projectDir, resolvedTemplateId, tempProjectDir, result.variables);
|
|
532
|
+
await addCollectionImportsForTemplate(workspace.projectDir, resolvedTemplateId, result.variables);
|
|
533
|
+
await appendBlockConfigEntries(workspace.projectDir, buildConfigEntries(resolvedTemplateId, result.variables), resolvedTemplateId === "persistence" ||
|
|
534
|
+
(resolvedTemplateId === "compound" &&
|
|
535
|
+
result.variables.compoundPersistenceEnabled === "true"));
|
|
536
|
+
await syncWorkspaceAddedBlockArtifacts(workspace.projectDir, resolvedTemplateId, result.variables);
|
|
537
|
+
await updateWorkspaceMigrationConfigIfPresent(workspace.projectDir, buildMigrationBlocks(resolvedTemplateId, result.variables));
|
|
538
|
+
return {
|
|
539
|
+
blockSlugs: collectWorkspaceBlockPaths(workspace.projectDir, resolvedTemplateId, result.variables).map((targetPath) => path.basename(targetPath)),
|
|
540
|
+
projectDir: workspace.projectDir,
|
|
541
|
+
templateId: resolvedTemplateId,
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
catch (error) {
|
|
545
|
+
await rollbackWorkspaceMutation(workspace.projectDir, mutationSnapshot);
|
|
546
|
+
throw error;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
finally {
|
|
550
|
+
if (tempRoot) {
|
|
551
|
+
await fsp.rm(tempRoot, { force: true, recursive: true });
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Returns the current placeholder guidance for unsupported `wp-typia add` kinds.
|
|
557
|
+
*/
|
|
558
|
+
export function createAddPlaceholderMessage(kind) {
|
|
559
|
+
const issueNumber = kind === "variation" ? "#157" : "#158";
|
|
560
|
+
return `\`wp-typia add ${kind}\` is not implemented yet. Track ${issueNumber} for the first supported ${kind} workflow.`;
|
|
561
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public runtime helper barrel consumed by the canonical `wp-typia` package.
|
|
3
|
+
*
|
|
4
|
+
* These exports do not make `@wp-typia/project-tools` the CLI owner again. Command
|
|
5
|
+
* taxonomy, help text ownership, and published bin responsibility stay in
|
|
6
|
+
* `packages/wp-typia`.
|
|
7
|
+
*
|
|
8
|
+
* Import `formatAddHelpText`, `runAddBlockCommand`,
|
|
9
|
+
* `createAddPlaceholderMessage`, and `seedWorkspaceMigrationProject` for
|
|
10
|
+
* explicit `wp-typia add` flows,
|
|
11
|
+
* `getDoctorChecks`, `runDoctor`, and `DoctorCheck` for diagnostics,
|
|
12
|
+
* `formatHelpText` for top-level CLI usage output, scaffold helpers such as
|
|
13
|
+
* `createReadlinePrompt`, `getNextSteps`, `getOptionalOnboarding`,
|
|
14
|
+
* `runScaffoldFlow`, and `ReadlinePrompt` for interactive project creation,
|
|
15
|
+
* and template helpers such as `formatTemplateDetails`,
|
|
16
|
+
* `formatTemplateFeatures`, `formatTemplateSummary`, `getTemplateById`,
|
|
17
|
+
* `getTemplateSelectOptions`, `listTemplates`, and `isBuiltInTemplateId` for
|
|
18
|
+
* template inspection flows.
|
|
19
|
+
*/
|
|
20
|
+
export { getDoctorChecks, runDoctor, type DoctorCheck } from "./cli-doctor.js";
|
|
21
|
+
export { createAddPlaceholderMessage, formatAddHelpText, runAddBlockCommand, seedWorkspaceMigrationProject } from "./cli-add.js";
|
|
22
|
+
export { formatHelpText } from "./cli-help.js";
|
|
23
|
+
export { getNextSteps, getOptionalOnboarding, runScaffoldFlow, } from "./cli-scaffold.js";
|
|
24
|
+
export { createReadlinePrompt, type ReadlinePrompt } from "./cli-prompt.js";
|
|
25
|
+
export { formatTemplateDetails, formatTemplateFeatures, formatTemplateSummary, getTemplateById, getTemplateSelectOptions, isBuiltInTemplateId, listTemplates, } from "./cli-templates.js";
|