wp-typia 0.11.1 → 0.12.0
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/.bunli/commands.gen.ts +105 -0
- package/README.md +11 -2
- package/bin/wp-typia.js +36 -1
- package/bunli.config.ts +31 -0
- package/package.json +46 -4
- package/src/cli.ts +126 -0
- package/src/command-contract.ts +232 -0
- package/src/command-list.ts +17 -0
- package/src/commands/add.ts +97 -0
- package/src/commands/create.ts +166 -0
- package/src/commands/doctor.ts +25 -0
- package/src/commands/mcp.ts +70 -0
- package/src/commands/migrate.ts +119 -0
- package/src/commands/templates.ts +45 -0
- package/src/config.ts +139 -0
- package/src/mcp.ts +112 -0
- package/src/plugins/wp-typia-skills.ts +15 -0
- package/src/plugins/wp-typia-user-config.ts +40 -0
- package/src/render-loader.ts +13 -0
- package/src/runtime-bridge.ts +387 -0
- package/src/ui/README.md +17 -0
- package/src/ui/add-flow.tsx +166 -0
- package/src/ui/create-flow.tsx +171 -0
- package/src/ui/lazy-flow.tsx +41 -0
- package/src/ui/migrate-flow.tsx +187 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// This file was automatically generated by Bunli.
|
|
2
|
+
// You should NOT make any changes in this file as it will be overwritten.
|
|
3
|
+
|
|
4
|
+
import type { Command, CLI, GeneratedOptionMeta, RegisteredCommands, CommandOptions, GeneratedCommandMeta } from '@bunli/core'
|
|
5
|
+
import { createGeneratedHelpers, registerGeneratedStore } from '@bunli/core'
|
|
6
|
+
|
|
7
|
+
import Add from '../src/commands/add.js'
|
|
8
|
+
import Create from '../src/commands/create.js'
|
|
9
|
+
import Doctor from '../src/commands/doctor.js'
|
|
10
|
+
import Mcp from '../src/commands/mcp.js'
|
|
11
|
+
import Migrate from '../src/commands/migrate.js'
|
|
12
|
+
import Templates from '../src/commands/templates.js'
|
|
13
|
+
|
|
14
|
+
// Narrow list of command names to avoid typeof-cycles in types
|
|
15
|
+
const names = ['add', 'create', 'doctor', 'mcp', 'migrate', 'templates'] as const
|
|
16
|
+
type GeneratedNames = typeof names[number]
|
|
17
|
+
|
|
18
|
+
const modules: Record<GeneratedNames, Command<any>> = {
|
|
19
|
+
'add': Add,
|
|
20
|
+
'create': Create,
|
|
21
|
+
'doctor': Doctor,
|
|
22
|
+
'mcp': Mcp,
|
|
23
|
+
'migrate': Migrate,
|
|
24
|
+
'templates': Templates
|
|
25
|
+
} as const
|
|
26
|
+
|
|
27
|
+
const metadata: Record<GeneratedNames, GeneratedCommandMeta> = {
|
|
28
|
+
'add': {
|
|
29
|
+
name: 'add',
|
|
30
|
+
description: 'Extend an official wp-typia workspace with blocks, variations, or patterns.',
|
|
31
|
+
path: './src/commands/add'
|
|
32
|
+
},
|
|
33
|
+
'create': {
|
|
34
|
+
name: 'create',
|
|
35
|
+
description: 'Scaffold a new wp-typia project.',
|
|
36
|
+
path: './src/commands/create'
|
|
37
|
+
},
|
|
38
|
+
'doctor': {
|
|
39
|
+
name: 'doctor',
|
|
40
|
+
description: 'Run repository and project diagnostics.',
|
|
41
|
+
path: './src/commands/doctor'
|
|
42
|
+
},
|
|
43
|
+
'mcp': {
|
|
44
|
+
name: 'mcp',
|
|
45
|
+
description: 'Inspect or sync schema-driven MCP metadata for wp-typia.',
|
|
46
|
+
path: './src/commands/mcp'
|
|
47
|
+
},
|
|
48
|
+
'migrate': {
|
|
49
|
+
name: 'migrate',
|
|
50
|
+
description: 'Run migration workflows for migration-capable wp-typia projects.',
|
|
51
|
+
path: './src/commands/migrate'
|
|
52
|
+
},
|
|
53
|
+
'templates': {
|
|
54
|
+
name: 'templates',
|
|
55
|
+
description: 'Inspect built-in and external scaffold templates.',
|
|
56
|
+
path: './src/commands/templates'
|
|
57
|
+
}
|
|
58
|
+
} as const
|
|
59
|
+
|
|
60
|
+
export const generated = registerGeneratedStore(createGeneratedHelpers(modules, metadata))
|
|
61
|
+
|
|
62
|
+
export const commands = generated.commands
|
|
63
|
+
export const commandMeta = generated.metadata
|
|
64
|
+
|
|
65
|
+
export interface GeneratedCLI {
|
|
66
|
+
register(cli?: CLI<any>): GeneratedCLI
|
|
67
|
+
list(): Array<{
|
|
68
|
+
name: GeneratedNames
|
|
69
|
+
command: (typeof modules)[GeneratedNames]
|
|
70
|
+
metadata: (typeof metadata)[GeneratedNames]
|
|
71
|
+
}>
|
|
72
|
+
get<Name extends GeneratedNames>(name: Name): (typeof modules)[Name]
|
|
73
|
+
getMetadata<Name extends GeneratedNames>(name: Name): (typeof metadata)[Name]
|
|
74
|
+
getFlags<Name extends keyof RegisteredCommands & string>(name: Name): CommandOptions<Name>
|
|
75
|
+
getFlagsMeta<Name extends GeneratedNames>(name: Name): Record<string, GeneratedOptionMeta>
|
|
76
|
+
withCLI(cli: CLI<any>): { execute(name: string, options: unknown): Promise<void> }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const cli: GeneratedCLI = {
|
|
80
|
+
register: (cliInstance?: CLI<any>) => { generated.register(cliInstance); return cli },
|
|
81
|
+
list: () => generated.list(),
|
|
82
|
+
get: <Name extends GeneratedNames>(name: Name) => generated.get(name),
|
|
83
|
+
getMetadata: <Name extends GeneratedNames>(name: Name) => generated.getMetadata(name),
|
|
84
|
+
getFlags: <Name extends keyof RegisteredCommands & string>(name: Name) => generated.getFlags(name) as CommandOptions<Name>,
|
|
85
|
+
getFlagsMeta: <Name extends GeneratedNames>(name: Name) => generated.getFlagsMeta(name),
|
|
86
|
+
withCLI: (cliInstance) => generated.withCLI(cliInstance)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Enhanced helper functions
|
|
90
|
+
export const listCommands = () => generated.list().map(c => c.name)
|
|
91
|
+
export const getCommandApi = <Name extends GeneratedNames>(name: Name) => generated.getMetadata(name)
|
|
92
|
+
export const getTypedFlags = <Name extends GeneratedNames>(name: Name) => generated.getFlags(name) as CommandOptions<Name>
|
|
93
|
+
export const validateCommand = <Name extends GeneratedNames>(name: Name, flags: Record<string, unknown>) => generated.validateCommand(name, flags)
|
|
94
|
+
export const findCommandByName = <Name extends GeneratedNames>(name: Name) => generated.findByName(name)
|
|
95
|
+
export const findCommandsByDescription = (searchTerm: string) => generated.findByDescription(searchTerm)
|
|
96
|
+
export const getCommandNames = () => generated.getCommandNames()
|
|
97
|
+
|
|
98
|
+
// Auto-register on import for zero-config usage
|
|
99
|
+
export default cli
|
|
100
|
+
|
|
101
|
+
// Ensure module augmentation happens on import
|
|
102
|
+
declare module '@bunli/core' {
|
|
103
|
+
// Precise key mapping without typeof cycles
|
|
104
|
+
interface RegisteredCommands extends Record<GeneratedNames, Command<any>> {}
|
|
105
|
+
}
|
package/README.md
CHANGED
|
@@ -6,8 +6,17 @@ Use this package for new installs:
|
|
|
6
6
|
|
|
7
7
|
- `npx wp-typia create my-block`
|
|
8
8
|
- `bunx wp-typia create my-block`
|
|
9
|
+
- `npx wp-typia create my-plugin --template @wp-typia/create-workspace-template`
|
|
10
|
+
- `wp-typia add block counter-card --template basic`
|
|
11
|
+
|
|
12
|
+
`wp-typia <project-dir>` remains available as a backward-compatible alias to
|
|
13
|
+
`wp-typia create <project-dir>`.
|
|
9
14
|
|
|
10
15
|
Compatibility notes:
|
|
11
16
|
|
|
12
|
-
- `@wp-typia/
|
|
13
|
-
-
|
|
17
|
+
- `@wp-typia/project-tools` is the canonical programmatic project orchestration package
|
|
18
|
+
- `@wp-typia/create` is the deprecated legacy package shell
|
|
19
|
+
- `create-wp-typia` is archived and should not be used for new installs
|
|
20
|
+
|
|
21
|
+
Maintainers: see [`docs/bunli-cli-migration.md`](../../docs/bunli-cli-migration.md)
|
|
22
|
+
for the active CLI ownership contract and the staged Bunli cutover plan.
|
package/bin/wp-typia.js
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import "
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
|
+
|
|
7
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
8
|
+
const cliEntrypoint = path.join(packageRoot, "src", "cli.ts");
|
|
9
|
+
const bunBinary = process.env.BUN_BIN || "bun";
|
|
10
|
+
|
|
11
|
+
const result = spawnSync(
|
|
12
|
+
bunBinary,
|
|
13
|
+
[
|
|
14
|
+
"--eval",
|
|
15
|
+
`
|
|
16
|
+
const [moduleUrl, ...argv] = process.argv.slice(1);
|
|
17
|
+
const mod = await import(moduleUrl);
|
|
18
|
+
await mod.main(argv);
|
|
19
|
+
`,
|
|
20
|
+
pathToFileURL(cliEntrypoint).href,
|
|
21
|
+
...process.argv.slice(2),
|
|
22
|
+
],
|
|
23
|
+
{
|
|
24
|
+
cwd: process.cwd(),
|
|
25
|
+
env: process.env,
|
|
26
|
+
stdio: "inherit",
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
if (result.error) {
|
|
31
|
+
console.error(
|
|
32
|
+
"❌ wp-typia requires Bun 1.3.11+ to run the Bunli-powered CLI. Install Bun or set BUN_BIN to a compatible runtime.",
|
|
33
|
+
);
|
|
34
|
+
console.error(result.error instanceof Error ? result.error.message : result.error);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
process.exit(result.status ?? 1);
|
package/bunli.config.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineConfig } from "@bunli/core";
|
|
2
|
+
|
|
3
|
+
import packageJson from "./package.json";
|
|
4
|
+
|
|
5
|
+
export const bunliConfig = defineConfig({
|
|
6
|
+
name: packageJson.name,
|
|
7
|
+
version: packageJson.version,
|
|
8
|
+
description: packageJson.description,
|
|
9
|
+
commands: {
|
|
10
|
+
entry: "./src/cli.ts",
|
|
11
|
+
directory: "./src/commands",
|
|
12
|
+
generateReport: true,
|
|
13
|
+
},
|
|
14
|
+
build: {
|
|
15
|
+
entry: "./src/cli.ts",
|
|
16
|
+
outdir: "./dist-bunli",
|
|
17
|
+
sourcemap: true,
|
|
18
|
+
},
|
|
19
|
+
test: {
|
|
20
|
+
pattern: ["tests/*.test.ts"],
|
|
21
|
+
coverage: false,
|
|
22
|
+
watch: false,
|
|
23
|
+
},
|
|
24
|
+
tui: {
|
|
25
|
+
renderer: {
|
|
26
|
+
bufferMode: "alternate",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export default bunliConfig;
|
package/package.json
CHANGED
|
@@ -1,17 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wp-typia",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Canonical CLI package for wp-typia scaffolding and project workflows",
|
|
5
|
-
"packageManager": "bun@1.3.
|
|
5
|
+
"packageManager": "bun@1.3.11",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"wp-typia": "bin/wp-typia.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
+
".bunli/",
|
|
11
12
|
"bin/",
|
|
13
|
+
"bunli.config.ts",
|
|
14
|
+
"src/",
|
|
12
15
|
"README.md",
|
|
13
16
|
"package.json"
|
|
14
17
|
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"generate": "bun scripts/generate-bunli-metadata.ts",
|
|
20
|
+
"build": "bun run generate",
|
|
21
|
+
"bunli:generate": "bun run generate",
|
|
22
|
+
"bunli:build": "bun run build",
|
|
23
|
+
"bunli:dev": "bun src/cli.ts",
|
|
24
|
+
"bunli:test": "bun test tests/*.test.ts",
|
|
25
|
+
"dev": "bun run generate && node bin/wp-typia.js",
|
|
26
|
+
"test": "cd ../wp-typia-project-tools && bun run build && cd ../wp-typia && bun run generate && bun test tests/*.test.ts",
|
|
27
|
+
"test:coverage": "cd ../wp-typia-project-tools && bun run build && cd ../wp-typia && bun run generate && bun test tests/*.test.ts --coverage --coverage-reporter=lcov --coverage-dir=coverage",
|
|
28
|
+
"clean": "rm -rf .bunli"
|
|
29
|
+
},
|
|
15
30
|
"keywords": [
|
|
16
31
|
"wordpress",
|
|
17
32
|
"gutenberg",
|
|
@@ -37,9 +52,36 @@
|
|
|
37
52
|
"engines": {
|
|
38
53
|
"node": ">=20.0.0",
|
|
39
54
|
"npm": ">=10.0.0",
|
|
40
|
-
"bun": ">=1.3.
|
|
55
|
+
"bun": ">=1.3.11"
|
|
41
56
|
},
|
|
42
57
|
"dependencies": {
|
|
43
|
-
"@
|
|
58
|
+
"@bunli/core": "0.9.0",
|
|
59
|
+
"@bunli/plugin-ai-detect": "0.6.4",
|
|
60
|
+
"@bunli/plugin-completions": "0.3.5",
|
|
61
|
+
"@bunli/plugin-config": "0.4.4",
|
|
62
|
+
"@bunli/plugin-mcp": "0.2.5",
|
|
63
|
+
"@bunli/plugin-skills": "0.1.0",
|
|
64
|
+
"@bunli/runtime": "0.3.1",
|
|
65
|
+
"@bunli/tui": "0.6.0",
|
|
66
|
+
"@bunli/utils": "0.6.0",
|
|
67
|
+
"@wp-typia/project-tools": "0.12.0",
|
|
68
|
+
"better-result": "^2.7.0",
|
|
69
|
+
"react": "19.2.0",
|
|
70
|
+
"react-dom": "19.2.0",
|
|
71
|
+
"zod": "4.3.6"
|
|
72
|
+
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"@bunli/generator": "0.6.5",
|
|
75
|
+
"@bunli/test": "0.6.0",
|
|
76
|
+
"bunli": "0.9.0",
|
|
77
|
+
"typescript": "^5.9.2"
|
|
78
|
+
},
|
|
79
|
+
"optionalDependencies": {
|
|
80
|
+
"@opentui/core-darwin-arm64": "0.1.97",
|
|
81
|
+
"@opentui/core-darwin-x64": "0.1.97",
|
|
82
|
+
"@opentui/core-linux-arm64": "0.1.97",
|
|
83
|
+
"@opentui/core-linux-x64": "0.1.97",
|
|
84
|
+
"@opentui/core-win32-arm64": "0.1.97",
|
|
85
|
+
"@opentui/core-win32-x64": "0.1.97"
|
|
44
86
|
}
|
|
45
87
|
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
|
|
4
|
+
import { createCLI, type CLI } from "@bunli/core";
|
|
5
|
+
import { aiAgentPlugin } from "@bunli/plugin-ai-detect";
|
|
6
|
+
import { completionsPlugin } from "@bunli/plugin-completions";
|
|
7
|
+
import { configMergerPlugin } from "@bunli/plugin-config";
|
|
8
|
+
import { skillsPlugin } from "@bunli/plugin-skills";
|
|
9
|
+
|
|
10
|
+
import packageJson from "../package.json";
|
|
11
|
+
import { bunliConfig } from "../bunli.config";
|
|
12
|
+
import { normalizeWpTypiaArgv } from "./command-contract";
|
|
13
|
+
import { wpTypiaCommands } from "./command-list";
|
|
14
|
+
import { WP_TYPIA_CONFIG_SOURCES } from "./config";
|
|
15
|
+
import { createWpTypiaSkillsMetadataPlugin } from "./plugins/wp-typia-skills";
|
|
16
|
+
import { wpTypiaUserConfigPlugin } from "./plugins/wp-typia-user-config";
|
|
17
|
+
|
|
18
|
+
function extractWpTypiaConfigOverride(argv: string[]): {
|
|
19
|
+
argv: string[];
|
|
20
|
+
configOverridePath?: string;
|
|
21
|
+
} {
|
|
22
|
+
const nextArgv: string[] = [];
|
|
23
|
+
let configOverridePath: string | undefined;
|
|
24
|
+
|
|
25
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
26
|
+
const arg = argv[index];
|
|
27
|
+
if (!arg) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (arg === "--config" || arg === "-c") {
|
|
32
|
+
const next = argv[index + 1];
|
|
33
|
+
if (!next || next.startsWith("-")) {
|
|
34
|
+
throw new Error(`\`${arg}\` requires a value.`);
|
|
35
|
+
}
|
|
36
|
+
configOverridePath = next;
|
|
37
|
+
index += 1;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (arg.startsWith("--config=")) {
|
|
42
|
+
const inlineValue = arg.slice("--config=".length);
|
|
43
|
+
if (!inlineValue) {
|
|
44
|
+
throw new Error("`--config` requires a value.");
|
|
45
|
+
}
|
|
46
|
+
configOverridePath = inlineValue;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
nextArgv.push(arg);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
argv: nextArgv,
|
|
55
|
+
configOverridePath,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function resolveGeneratedMetadataPath(moduleUrl: string): string {
|
|
60
|
+
const candidates = [
|
|
61
|
+
new URL("./.bunli/commands.gen.js", moduleUrl),
|
|
62
|
+
new URL("../lib/.bunli/commands.gen.js", moduleUrl),
|
|
63
|
+
new URL("../.bunli/commands.gen.ts", moduleUrl),
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
for (const candidate of candidates) {
|
|
67
|
+
const candidatePath = fileURLToPath(candidate);
|
|
68
|
+
if (fs.existsSync(candidatePath)) {
|
|
69
|
+
return candidatePath;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return fileURLToPath(new URL("../.bunli/commands.gen.ts", moduleUrl));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function createWpTypiaCli(options: {
|
|
77
|
+
configOverridePath?: string;
|
|
78
|
+
} = {}): Promise<CLI> {
|
|
79
|
+
const cli = await createCLI({
|
|
80
|
+
...bunliConfig,
|
|
81
|
+
description: packageJson.description,
|
|
82
|
+
name: packageJson.name,
|
|
83
|
+
plugins: [
|
|
84
|
+
configMergerPlugin({
|
|
85
|
+
mergeStrategy: "deep",
|
|
86
|
+
sources: [...WP_TYPIA_CONFIG_SOURCES],
|
|
87
|
+
}),
|
|
88
|
+
wpTypiaUserConfigPlugin({
|
|
89
|
+
overrideSource: options.configOverridePath,
|
|
90
|
+
}),
|
|
91
|
+
aiAgentPlugin({}),
|
|
92
|
+
createWpTypiaSkillsMetadataPlugin(wpTypiaCommands),
|
|
93
|
+
skillsPlugin({
|
|
94
|
+
description: packageJson.description,
|
|
95
|
+
}),
|
|
96
|
+
completionsPlugin({
|
|
97
|
+
commandName: "wp-typia",
|
|
98
|
+
executable: "wp-typia",
|
|
99
|
+
generatedPath: resolveGeneratedMetadataPath(import.meta.url),
|
|
100
|
+
}),
|
|
101
|
+
],
|
|
102
|
+
version: packageJson.version,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
for (const command of wpTypiaCommands) {
|
|
106
|
+
cli.command(command);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return cli;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function main(argv = process.argv.slice(2)): Promise<void> {
|
|
113
|
+
const normalizedArgv = normalizeWpTypiaArgv(argv);
|
|
114
|
+
const { argv: cliArgv, configOverridePath } = extractWpTypiaConfigOverride(normalizedArgv);
|
|
115
|
+
const cli = await createWpTypiaCli({ configOverridePath });
|
|
116
|
+
await cli.run(cliArgv);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (import.meta.main) {
|
|
120
|
+
void main().catch((error) => {
|
|
121
|
+
console.error(error instanceof Error ? error.message : error);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export default createWpTypiaCli;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
export const WP_TYPIA_CANONICAL_CREATE_USAGE = "wp-typia create <project-dir>";
|
|
2
|
+
export const WP_TYPIA_POSITIONAL_ALIAS_USAGE = "wp-typia <project-dir>";
|
|
3
|
+
export const WP_TYPIA_CANONICAL_MIGRATE_USAGE = "wp-typia migrate <subcommand>";
|
|
4
|
+
export const WP_TYPIA_DEPRECATED_MIGRATIONS_USAGE = "wp-typia migrations <subcommand>";
|
|
5
|
+
export const WP_TYPIA_BUNLI_MIGRATION_DOC = "docs/bunli-cli-migration.md";
|
|
6
|
+
|
|
7
|
+
export const WP_TYPIA_RESERVED_TOP_LEVEL_COMMAND_NAMES = [
|
|
8
|
+
"create",
|
|
9
|
+
"add",
|
|
10
|
+
"migrate",
|
|
11
|
+
"templates",
|
|
12
|
+
"doctor",
|
|
13
|
+
"mcp",
|
|
14
|
+
"skills",
|
|
15
|
+
"completions",
|
|
16
|
+
"complete",
|
|
17
|
+
] as const;
|
|
18
|
+
|
|
19
|
+
export const WP_TYPIA_TOP_LEVEL_COMMAND_NAMES = [
|
|
20
|
+
"create",
|
|
21
|
+
"add",
|
|
22
|
+
"migrate",
|
|
23
|
+
"templates",
|
|
24
|
+
"doctor",
|
|
25
|
+
"mcp",
|
|
26
|
+
] as const;
|
|
27
|
+
|
|
28
|
+
const STRING_OPTION_NAMES_BY_COMMAND = {
|
|
29
|
+
add: new Set([
|
|
30
|
+
"block",
|
|
31
|
+
"data-storage",
|
|
32
|
+
"persistence-policy",
|
|
33
|
+
"template",
|
|
34
|
+
]),
|
|
35
|
+
create: new Set([
|
|
36
|
+
"data-storage",
|
|
37
|
+
"namespace",
|
|
38
|
+
"package-manager",
|
|
39
|
+
"persistence-policy",
|
|
40
|
+
"php-prefix",
|
|
41
|
+
"template",
|
|
42
|
+
"text-domain",
|
|
43
|
+
"variant",
|
|
44
|
+
]),
|
|
45
|
+
migrate: new Set([
|
|
46
|
+
"current-migration-version",
|
|
47
|
+
"from-migration-version",
|
|
48
|
+
"iterations",
|
|
49
|
+
"migration-version",
|
|
50
|
+
"seed",
|
|
51
|
+
"to-migration-version",
|
|
52
|
+
]),
|
|
53
|
+
} as const;
|
|
54
|
+
|
|
55
|
+
const GLOBAL_STRING_OPTION_NAMES = new Set([
|
|
56
|
+
"config",
|
|
57
|
+
"format",
|
|
58
|
+
"id",
|
|
59
|
+
"output-dir",
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
const SHORT_OPTION_NAMES_WITH_VALUES = new Set([
|
|
63
|
+
"c",
|
|
64
|
+
"p",
|
|
65
|
+
"t",
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
function isLongOptionValueConsumer(optionName: string): boolean {
|
|
69
|
+
if (GLOBAL_STRING_OPTION_NAMES.has(optionName)) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return Object.values(STRING_OPTION_NAMES_BY_COMMAND).some((optionNames) =>
|
|
74
|
+
optionNames.has(optionName as never),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function findFirstPositionalIndex(argv: string[]): number {
|
|
79
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
80
|
+
const arg = argv[index];
|
|
81
|
+
if (arg === "--") {
|
|
82
|
+
return index + 1 < argv.length ? index + 1 : -1;
|
|
83
|
+
}
|
|
84
|
+
if (!arg.startsWith("-") || arg === "-") {
|
|
85
|
+
return index;
|
|
86
|
+
}
|
|
87
|
+
if (arg.startsWith("--")) {
|
|
88
|
+
if (arg.includes("=")) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (isLongOptionValueConsumer(arg.slice(2))) {
|
|
92
|
+
index += 1;
|
|
93
|
+
}
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (arg.length === 2 && SHORT_OPTION_NAMES_WITH_VALUES.has(arg.slice(1))) {
|
|
97
|
+
index += 1;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return -1;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const WP_TYPIA_FUTURE_COMMAND_TREE = [
|
|
105
|
+
{
|
|
106
|
+
description: "Scaffold a new wp-typia project.",
|
|
107
|
+
name: "create",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
description: "Extend an official wp-typia workspace.",
|
|
111
|
+
name: "add",
|
|
112
|
+
subcommands: ["block", "variation", "pattern"],
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
description: "Run migration workflows.",
|
|
116
|
+
name: "migrate",
|
|
117
|
+
subcommands: [
|
|
118
|
+
"init",
|
|
119
|
+
"snapshot",
|
|
120
|
+
"diff",
|
|
121
|
+
"scaffold",
|
|
122
|
+
"plan",
|
|
123
|
+
"wizard",
|
|
124
|
+
"verify",
|
|
125
|
+
"doctor",
|
|
126
|
+
"fixtures",
|
|
127
|
+
"fuzz",
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
description: "Inspect scaffold templates.",
|
|
132
|
+
name: "templates",
|
|
133
|
+
subcommands: ["list", "inspect"],
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
description: "Run repository and project diagnostics.",
|
|
137
|
+
name: "doctor",
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
description: "Inspect or sync schema-driven MCP metadata.",
|
|
141
|
+
name: "mcp",
|
|
142
|
+
subcommands: ["list", "sync"],
|
|
143
|
+
},
|
|
144
|
+
] as const;
|
|
145
|
+
|
|
146
|
+
export function isReservedTopLevelCommandName(value: string): boolean {
|
|
147
|
+
return WP_TYPIA_RESERVED_TOP_LEVEL_COMMAND_NAMES.includes(
|
|
148
|
+
value as (typeof WP_TYPIA_RESERVED_TOP_LEVEL_COMMAND_NAMES)[number],
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function assertStringOptionValues(argv: string[]): void {
|
|
153
|
+
const firstPositionalIndex = findFirstPositionalIndex(argv);
|
|
154
|
+
if (firstPositionalIndex === -1) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const commandName = argv[firstPositionalIndex] as keyof typeof STRING_OPTION_NAMES_BY_COMMAND;
|
|
159
|
+
const stringOptionNames = new Set<string>(GLOBAL_STRING_OPTION_NAMES);
|
|
160
|
+
for (const optionName of STRING_OPTION_NAMES_BY_COMMAND[commandName] ?? []) {
|
|
161
|
+
stringOptionNames.add(optionName);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (let index = firstPositionalIndex + 1; index < argv.length; index += 1) {
|
|
165
|
+
const arg = argv[index];
|
|
166
|
+
if (arg === "--") {
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
if (arg.length === 2 && arg.startsWith("-")) {
|
|
170
|
+
if (SHORT_OPTION_NAMES_WITH_VALUES.has(arg.slice(1))) {
|
|
171
|
+
const next = argv[index + 1];
|
|
172
|
+
if (!next || next.startsWith("-")) {
|
|
173
|
+
throw new Error(`\`${arg}\` requires a value.`);
|
|
174
|
+
}
|
|
175
|
+
index += 1;
|
|
176
|
+
}
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (!arg.startsWith("--")) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const [rawName, inlineValue] = arg.slice(2).split("=", 2);
|
|
184
|
+
if (!stringOptionNames.has(rawName)) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (arg.includes("=")) {
|
|
189
|
+
if (!inlineValue) {
|
|
190
|
+
throw new Error(`\`--${rawName}\` requires a value.`);
|
|
191
|
+
}
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const next = argv[index + 1];
|
|
196
|
+
if (!next || next.startsWith("-")) {
|
|
197
|
+
throw new Error(`\`--${rawName}\` requires a value.`);
|
|
198
|
+
}
|
|
199
|
+
index += 1;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function normalizeWpTypiaArgv(argv: string[]): string[] {
|
|
204
|
+
const firstPositionalIndex = findFirstPositionalIndex(argv);
|
|
205
|
+
if (firstPositionalIndex === -1) {
|
|
206
|
+
return argv;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const firstPositional = argv[firstPositionalIndex];
|
|
210
|
+
if (!firstPositional) {
|
|
211
|
+
return argv;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (firstPositional === "migrations") {
|
|
215
|
+
throw new Error(
|
|
216
|
+
"`wp-typia migrations` was removed in favor of `wp-typia migrate`. Use `wp-typia migrate <subcommand>` instead.",
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (isReservedTopLevelCommandName(firstPositional)) {
|
|
221
|
+
assertStringOptionValues(argv);
|
|
222
|
+
return argv;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const normalizedArgv = [
|
|
226
|
+
...argv.slice(0, firstPositionalIndex),
|
|
227
|
+
"create",
|
|
228
|
+
...argv.slice(firstPositionalIndex),
|
|
229
|
+
];
|
|
230
|
+
assertStringOptionValues(normalizedArgv);
|
|
231
|
+
return normalizedArgv;
|
|
232
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Command } from "@bunli/core";
|
|
2
|
+
|
|
3
|
+
import { addCommand } from "./commands/add";
|
|
4
|
+
import { createCommand } from "./commands/create";
|
|
5
|
+
import { doctorCommand } from "./commands/doctor";
|
|
6
|
+
import { mcpCommand } from "./commands/mcp";
|
|
7
|
+
import { migrateCommand } from "./commands/migrate";
|
|
8
|
+
import { templatesCommand } from "./commands/templates";
|
|
9
|
+
|
|
10
|
+
export const wpTypiaCommands: Command<any, any>[] = [
|
|
11
|
+
createCommand,
|
|
12
|
+
addCommand,
|
|
13
|
+
migrateCommand,
|
|
14
|
+
templatesCommand,
|
|
15
|
+
doctorCommand,
|
|
16
|
+
mcpCommand,
|
|
17
|
+
];
|