wp-typia 0.16.11 → 0.16.13
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 +6 -2
- package/bin/wp-typia.js +0 -0
- package/package.json +3 -3
- package/src/command-contract.ts +244 -212
- package/src/runtime-bridge-output.ts +216 -0
- package/src/runtime-bridge-sync.ts +169 -0
- package/src/runtime-bridge.ts +14 -337
package/README.md
CHANGED
|
@@ -12,19 +12,23 @@ Use this package for new installs:
|
|
|
12
12
|
- `wp-typia add hooked-block counter-card --anchor core/post-content --position after`
|
|
13
13
|
|
|
14
14
|
`wp-typia <project-dir>` remains available as a backward-compatible alias to
|
|
15
|
-
`wp-typia create <project-dir
|
|
15
|
+
`wp-typia create <project-dir>` when `<project-dir>` is the only positional
|
|
16
|
+
argument.
|
|
16
17
|
|
|
17
18
|
Compatibility notes:
|
|
18
19
|
|
|
19
20
|
- `@wp-typia/project-tools` is the canonical programmatic project orchestration package
|
|
20
21
|
- `@wp-typia/create` is the deprecated legacy package shell
|
|
21
22
|
- `create-wp-typia` is archived and should not be used for new installs
|
|
23
|
+
- internal runtime-bridge helper modules are implementation details; integrations
|
|
24
|
+
should target the CLI or `@wp-typia/project-tools`, not CLI internals
|
|
22
25
|
|
|
23
|
-
Maintainers: see [`docs/bunli-cli-migration.md`](
|
|
26
|
+
Maintainers: see [`docs/bunli-cli-migration.md`](https://imjlk.github.io/wp-typia/maintainers/bunli-cli-migration/)
|
|
24
27
|
for the active CLI ownership contract and the staged Bunli cutover plan.
|
|
25
28
|
|
|
26
29
|
Project meta docs:
|
|
27
30
|
|
|
28
31
|
- [Upgrade Guide](https://github.com/imjlk/wp-typia/blob/main/UPGRADE.md)
|
|
32
|
+
- [License](https://github.com/imjlk/wp-typia/blob/main/LICENSE)
|
|
29
33
|
- [Security Policy](https://github.com/imjlk/wp-typia/blob/main/SECURITY.md)
|
|
30
34
|
- [Contributing Guide](https://github.com/imjlk/wp-typia/blob/main/CONTRIBUTING.md)
|
package/bin/wp-typia.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wp-typia",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.13",
|
|
4
4
|
"description": "Canonical CLI package for wp-typia scaffolding and project workflows",
|
|
5
5
|
"packageManager": "bun@1.3.11",
|
|
6
6
|
"type": "module",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"@bunli/runtime": "0.3.1",
|
|
65
65
|
"@bunli/tui": "0.6.0",
|
|
66
66
|
"@bunli/utils": "0.6.0",
|
|
67
|
-
"@wp-typia/api-client": "^0.4.
|
|
68
|
-
"@wp-typia/project-tools": "0.16.
|
|
67
|
+
"@wp-typia/api-client": "^0.4.5",
|
|
68
|
+
"@wp-typia/project-tools": "0.16.12",
|
|
69
69
|
"better-result": "^2.7.0",
|
|
70
70
|
"react": "^19.2.5",
|
|
71
71
|
"react-dom": "^19.2.5",
|
package/src/command-contract.ts
CHANGED
|
@@ -1,244 +1,276 @@
|
|
|
1
|
-
export const WP_TYPIA_CANONICAL_CREATE_USAGE =
|
|
2
|
-
export const WP_TYPIA_POSITIONAL_ALIAS_USAGE =
|
|
3
|
-
export const WP_TYPIA_CANONICAL_MIGRATE_USAGE =
|
|
4
|
-
export const WP_TYPIA_DEPRECATED_MIGRATIONS_USAGE =
|
|
5
|
-
|
|
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 =
|
|
5
|
+
'wp-typia migrations <subcommand>';
|
|
6
|
+
export const WP_TYPIA_BUNLI_MIGRATION_DOC =
|
|
7
|
+
'https://imjlk.github.io/wp-typia/maintainers/bunli-cli-migration/';
|
|
6
8
|
|
|
7
9
|
export const WP_TYPIA_RESERVED_TOP_LEVEL_COMMAND_NAMES = [
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
'create',
|
|
11
|
+
'sync',
|
|
12
|
+
'add',
|
|
13
|
+
'migrate',
|
|
14
|
+
'templates',
|
|
15
|
+
'doctor',
|
|
16
|
+
'mcp',
|
|
17
|
+
'skills',
|
|
18
|
+
'completions',
|
|
19
|
+
'complete',
|
|
18
20
|
] as const;
|
|
19
21
|
|
|
20
22
|
export const WP_TYPIA_TOP_LEVEL_COMMAND_NAMES = [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
'create',
|
|
24
|
+
'sync',
|
|
25
|
+
'add',
|
|
26
|
+
'migrate',
|
|
27
|
+
'templates',
|
|
28
|
+
'doctor',
|
|
29
|
+
'mcp',
|
|
28
30
|
] as const;
|
|
29
31
|
|
|
30
32
|
const STRING_OPTION_NAMES_BY_COMMAND = {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
33
|
+
add: new Set([
|
|
34
|
+
'anchor',
|
|
35
|
+
'block',
|
|
36
|
+
'data-storage',
|
|
37
|
+
'external-layer-id',
|
|
38
|
+
'external-layer-source',
|
|
39
|
+
'persistence-policy',
|
|
40
|
+
'position',
|
|
41
|
+
'template',
|
|
42
|
+
]),
|
|
43
|
+
create: new Set([
|
|
44
|
+
'data-storage',
|
|
45
|
+
'external-layer-id',
|
|
46
|
+
'external-layer-source',
|
|
47
|
+
'namespace',
|
|
48
|
+
'package-manager',
|
|
49
|
+
'persistence-policy',
|
|
50
|
+
'php-prefix',
|
|
51
|
+
'template',
|
|
52
|
+
'text-domain',
|
|
53
|
+
'variant',
|
|
54
|
+
]),
|
|
55
|
+
migrate: new Set([
|
|
56
|
+
'current-migration-version',
|
|
57
|
+
'from-migration-version',
|
|
58
|
+
'iterations',
|
|
59
|
+
'migration-version',
|
|
60
|
+
'seed',
|
|
61
|
+
'to-migration-version',
|
|
62
|
+
]),
|
|
61
63
|
} as const;
|
|
62
64
|
|
|
63
65
|
const GLOBAL_STRING_OPTION_NAMES = new Set([
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
'config',
|
|
67
|
+
'format',
|
|
68
|
+
'id',
|
|
69
|
+
'output-dir',
|
|
68
70
|
]);
|
|
69
71
|
|
|
70
|
-
const SHORT_OPTION_NAMES_WITH_VALUES = new Set([
|
|
71
|
-
"c",
|
|
72
|
-
"p",
|
|
73
|
-
"t",
|
|
74
|
-
]);
|
|
72
|
+
const SHORT_OPTION_NAMES_WITH_VALUES = new Set(['c', 'p', 't']);
|
|
75
73
|
|
|
76
74
|
function isLongOptionValueConsumer(optionName: string): boolean {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
if (GLOBAL_STRING_OPTION_NAMES.has(optionName)) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
80
78
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
return Object.values(STRING_OPTION_NAMES_BY_COMMAND).some((optionNames) =>
|
|
80
|
+
optionNames.has(optionName as never),
|
|
81
|
+
);
|
|
84
82
|
}
|
|
85
83
|
|
|
86
84
|
function findFirstPositionalIndex(argv: string[]): number {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
85
|
+
const positionalIndexes = collectPositionalIndexes(argv);
|
|
86
|
+
return positionalIndexes[0] ?? -1;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function collectPositionalIndexes(argv: string[]): number[] {
|
|
90
|
+
const positionalIndexes: number[] = [];
|
|
91
|
+
|
|
92
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
93
|
+
const arg = argv[index];
|
|
94
|
+
if (arg === '--') {
|
|
95
|
+
for (let restIndex = index + 1; restIndex < argv.length; restIndex += 1) {
|
|
96
|
+
positionalIndexes.push(restIndex);
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
if (!arg.startsWith('-') || arg === '-') {
|
|
101
|
+
positionalIndexes.push(index);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (arg.startsWith('--')) {
|
|
105
|
+
if (arg.includes('=')) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (isLongOptionValueConsumer(arg.slice(2))) {
|
|
109
|
+
index += 1;
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (arg.length === 2 && SHORT_OPTION_NAMES_WITH_VALUES.has(arg.slice(1))) {
|
|
114
|
+
index += 1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return positionalIndexes;
|
|
110
119
|
}
|
|
111
120
|
|
|
112
121
|
export const WP_TYPIA_FUTURE_COMMAND_TREE = [
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
122
|
+
{
|
|
123
|
+
description: 'Scaffold a new wp-typia project.',
|
|
124
|
+
name: 'create',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
description: 'Run the common generated-project sync workflow.',
|
|
128
|
+
name: 'sync',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
description: 'Extend an official wp-typia workspace.',
|
|
132
|
+
name: 'add',
|
|
133
|
+
subcommands: [
|
|
134
|
+
'block',
|
|
135
|
+
'variation',
|
|
136
|
+
'pattern',
|
|
137
|
+
'binding-source',
|
|
138
|
+
'hooked-block',
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
description: 'Run migration workflows.',
|
|
143
|
+
name: 'migrate',
|
|
144
|
+
subcommands: [
|
|
145
|
+
'init',
|
|
146
|
+
'snapshot',
|
|
147
|
+
'diff',
|
|
148
|
+
'scaffold',
|
|
149
|
+
'plan',
|
|
150
|
+
'wizard',
|
|
151
|
+
'verify',
|
|
152
|
+
'doctor',
|
|
153
|
+
'fixtures',
|
|
154
|
+
'fuzz',
|
|
155
|
+
],
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
description: 'Inspect scaffold templates.',
|
|
159
|
+
name: 'templates',
|
|
160
|
+
subcommands: ['list', 'inspect'],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
description: 'Run repository and project diagnostics.',
|
|
164
|
+
name: 'doctor',
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
description: 'Inspect or sync schema-driven MCP metadata.',
|
|
168
|
+
name: 'mcp',
|
|
169
|
+
subcommands: ['list', 'sync'],
|
|
170
|
+
},
|
|
156
171
|
] as const;
|
|
157
172
|
|
|
158
173
|
export function isReservedTopLevelCommandName(value: string): boolean {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
174
|
+
return WP_TYPIA_RESERVED_TOP_LEVEL_COMMAND_NAMES.includes(
|
|
175
|
+
value as (typeof WP_TYPIA_RESERVED_TOP_LEVEL_COMMAND_NAMES)[number],
|
|
176
|
+
);
|
|
162
177
|
}
|
|
163
178
|
|
|
164
179
|
function assertStringOptionValues(argv: string[]): void {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
180
|
+
const firstPositionalIndex = findFirstPositionalIndex(argv);
|
|
181
|
+
if (firstPositionalIndex === -1) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const commandName = argv[
|
|
186
|
+
firstPositionalIndex
|
|
187
|
+
] as keyof typeof STRING_OPTION_NAMES_BY_COMMAND;
|
|
188
|
+
const stringOptionNames = new Set<string>(GLOBAL_STRING_OPTION_NAMES);
|
|
189
|
+
for (const optionName of STRING_OPTION_NAMES_BY_COMMAND[commandName] ?? []) {
|
|
190
|
+
stringOptionNames.add(optionName);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
for (let index = firstPositionalIndex + 1; index < argv.length; index += 1) {
|
|
194
|
+
const arg = argv[index];
|
|
195
|
+
if (arg === '--') {
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
if (arg.length === 2 && arg.startsWith('-')) {
|
|
199
|
+
if (SHORT_OPTION_NAMES_WITH_VALUES.has(arg.slice(1))) {
|
|
200
|
+
const next = argv[index + 1];
|
|
201
|
+
if (!next || next.startsWith('-')) {
|
|
202
|
+
throw new Error(`\`${arg}\` requires a value.`);
|
|
203
|
+
}
|
|
204
|
+
index += 1;
|
|
205
|
+
}
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (!arg.startsWith('--')) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const [rawName, inlineValue] = arg.slice(2).split('=', 2);
|
|
213
|
+
if (!stringOptionNames.has(rawName)) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (arg.includes('=')) {
|
|
218
|
+
if (!inlineValue) {
|
|
219
|
+
throw new Error(`\`--${rawName}\` requires a value.`);
|
|
220
|
+
}
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const next = argv[index + 1];
|
|
225
|
+
if (!next || next.startsWith('-')) {
|
|
226
|
+
throw new Error(`\`--${rawName}\` requires a value.`);
|
|
227
|
+
}
|
|
228
|
+
index += 1;
|
|
229
|
+
}
|
|
213
230
|
}
|
|
214
231
|
|
|
215
232
|
export function normalizeWpTypiaArgv(argv: string[]): string[] {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
233
|
+
const positionalIndexes = collectPositionalIndexes(argv);
|
|
234
|
+
const firstPositionalIndex = positionalIndexes[0] ?? -1;
|
|
235
|
+
if (firstPositionalIndex === -1) {
|
|
236
|
+
return argv;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const firstPositional = argv[firstPositionalIndex];
|
|
240
|
+
if (!firstPositional) {
|
|
241
|
+
return argv;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (firstPositional === 'migrations') {
|
|
245
|
+
throw new Error(
|
|
246
|
+
'`wp-typia migrations` was removed in favor of `wp-typia migrate`. Use `wp-typia migrate <subcommand>` instead.',
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (isReservedTopLevelCommandName(firstPositional)) {
|
|
251
|
+
assertStringOptionValues(argv);
|
|
252
|
+
return argv;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (positionalIndexes.length > 1) {
|
|
256
|
+
const extraPositionals = positionalIndexes
|
|
257
|
+
.slice(1)
|
|
258
|
+
.map((index) => argv[index])
|
|
259
|
+
.filter(
|
|
260
|
+
(value): value is string =>
|
|
261
|
+
typeof value === 'string' && value.length > 0,
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
throw new Error(
|
|
265
|
+
`The positional alias only accepts a single project directory. Use \`${WP_TYPIA_CANONICAL_CREATE_USAGE}\` for scaffold invocations with additional positional arguments, or check the command spelling if you meant another top-level command. Extra positional arguments: ${extraPositionals.map((value) => `\`${value}\``).join(', ')}.`,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const normalizedArgv = [
|
|
270
|
+
...argv.slice(0, firstPositionalIndex),
|
|
271
|
+
'create',
|
|
272
|
+
...argv.slice(firstPositionalIndex),
|
|
273
|
+
];
|
|
274
|
+
assertStringOptionValues(normalizedArgv);
|
|
275
|
+
return normalizedArgv;
|
|
244
276
|
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import type { AlternateBufferCompletionPayload } from "./ui/alternate-buffer-lifecycle";
|
|
2
|
+
|
|
3
|
+
type PrintLine = (line: string) => void;
|
|
4
|
+
|
|
5
|
+
type ExternalLayerSelectOption = {
|
|
6
|
+
description?: string;
|
|
7
|
+
extends: string[];
|
|
8
|
+
id: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Prints a formatted alternate-buffer completion payload to the provided writers.
|
|
13
|
+
*
|
|
14
|
+
* @param payload Structured completion payload to render.
|
|
15
|
+
* @param options Optional line printers for normal output and warnings.
|
|
16
|
+
* @returns Nothing.
|
|
17
|
+
*/
|
|
18
|
+
export function printCompletionPayload(
|
|
19
|
+
payload: AlternateBufferCompletionPayload,
|
|
20
|
+
options: {
|
|
21
|
+
printLine?: PrintLine;
|
|
22
|
+
warnLine?: PrintLine;
|
|
23
|
+
} = {},
|
|
24
|
+
): void {
|
|
25
|
+
const printLine = options.printLine ?? (console.log as PrintLine);
|
|
26
|
+
const warnLine = options.warnLine ?? printLine;
|
|
27
|
+
|
|
28
|
+
for (const line of payload.preambleLines ?? []) {
|
|
29
|
+
printLine(line);
|
|
30
|
+
}
|
|
31
|
+
for (const warning of payload.warningLines ?? []) {
|
|
32
|
+
warnLine(`⚠️ ${warning}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const hasDetails =
|
|
36
|
+
(payload.summaryLines?.length ?? 0) > 0 ||
|
|
37
|
+
(payload.nextSteps?.length ?? 0) > 0 ||
|
|
38
|
+
(payload.optionalLines?.length ?? 0) > 0 ||
|
|
39
|
+
Boolean(payload.optionalNote);
|
|
40
|
+
const hasLeadingContext =
|
|
41
|
+
(payload.preambleLines?.length ?? 0) > 0 ||
|
|
42
|
+
(payload.warningLines?.length ?? 0) > 0;
|
|
43
|
+
|
|
44
|
+
printLine(hasLeadingContext && hasDetails ? `\n${payload.title}` : payload.title);
|
|
45
|
+
for (const line of payload.summaryLines ?? []) {
|
|
46
|
+
printLine(line);
|
|
47
|
+
}
|
|
48
|
+
if ((payload.nextSteps?.length ?? 0) > 0) {
|
|
49
|
+
printLine("Next steps:");
|
|
50
|
+
for (const step of payload.nextSteps ?? []) {
|
|
51
|
+
printLine(` ${step}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if ((payload.optionalLines?.length ?? 0) > 0) {
|
|
55
|
+
printLine(`\n${payload.optionalTitle ?? "Optional:"}`);
|
|
56
|
+
for (const step of payload.optionalLines ?? []) {
|
|
57
|
+
printLine(` ${step}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (payload.optionalNote) {
|
|
61
|
+
printLine(`Note: ${payload.optionalNote}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Builds the completion payload shown after a create flow succeeds.
|
|
67
|
+
*
|
|
68
|
+
* @param flow Resolved create-flow data including onboarding steps and warnings.
|
|
69
|
+
* @returns A structured alternate-buffer completion payload.
|
|
70
|
+
*/
|
|
71
|
+
export function buildCreateCompletionPayload(flow: {
|
|
72
|
+
nextSteps: string[];
|
|
73
|
+
optionalOnboarding: {
|
|
74
|
+
note: string;
|
|
75
|
+
steps: string[];
|
|
76
|
+
};
|
|
77
|
+
projectDir: string;
|
|
78
|
+
result: {
|
|
79
|
+
selectedVariant?: string | null;
|
|
80
|
+
variables: {
|
|
81
|
+
title: string;
|
|
82
|
+
};
|
|
83
|
+
warnings: string[];
|
|
84
|
+
};
|
|
85
|
+
}): AlternateBufferCompletionPayload {
|
|
86
|
+
return {
|
|
87
|
+
nextSteps: flow.nextSteps,
|
|
88
|
+
optionalLines:
|
|
89
|
+
flow.optionalOnboarding.steps.length > 0 ? flow.optionalOnboarding.steps : undefined,
|
|
90
|
+
optionalNote:
|
|
91
|
+
flow.optionalOnboarding.steps.length > 0 ? flow.optionalOnboarding.note : undefined,
|
|
92
|
+
optionalTitle:
|
|
93
|
+
flow.optionalOnboarding.steps.length > 0 ? "Optional before first commit:" : undefined,
|
|
94
|
+
preambleLines: flow.result.selectedVariant
|
|
95
|
+
? [`Template variant: ${flow.result.selectedVariant}`]
|
|
96
|
+
: undefined,
|
|
97
|
+
summaryLines: [`Project directory: ${flow.projectDir}`],
|
|
98
|
+
title: `✅ Created ${flow.result.variables.title} in ${flow.projectDir}`,
|
|
99
|
+
warningLines: flow.result.warnings,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Builds the completion payload shown after a migrate command succeeds.
|
|
105
|
+
*
|
|
106
|
+
* @param options Completed migrate command metadata plus rendered lines.
|
|
107
|
+
* @returns A structured alternate-buffer completion payload.
|
|
108
|
+
*/
|
|
109
|
+
export function buildMigrationCompletionPayload(options: {
|
|
110
|
+
command: string;
|
|
111
|
+
lines: string[];
|
|
112
|
+
}): AlternateBufferCompletionPayload {
|
|
113
|
+
const summaryLines = options.lines.filter((line) => line.trim().length > 0);
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
summaryLines,
|
|
117
|
+
title: `✅ Completed wp-typia migrate ${options.command}`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Builds the completion payload shown after an add flow succeeds.
|
|
123
|
+
*
|
|
124
|
+
* @param options Add-flow kind plus normalized values to summarize.
|
|
125
|
+
* @returns A structured alternate-buffer completion payload.
|
|
126
|
+
*/
|
|
127
|
+
export function buildAddCompletionPayload(options: {
|
|
128
|
+
kind: "binding-source" | "block" | "hooked-block" | "pattern" | "variation";
|
|
129
|
+
projectDir: string;
|
|
130
|
+
values: Record<string, string>;
|
|
131
|
+
warnings?: string[];
|
|
132
|
+
}): AlternateBufferCompletionPayload {
|
|
133
|
+
switch (options.kind) {
|
|
134
|
+
case "variation":
|
|
135
|
+
return {
|
|
136
|
+
summaryLines: [
|
|
137
|
+
`Variation: ${options.values.variationSlug}`,
|
|
138
|
+
`Target block: ${options.values.blockSlug}`,
|
|
139
|
+
`Project directory: ${options.projectDir}`,
|
|
140
|
+
],
|
|
141
|
+
title: "✅ Added workspace variation",
|
|
142
|
+
};
|
|
143
|
+
case "pattern":
|
|
144
|
+
return {
|
|
145
|
+
summaryLines: [
|
|
146
|
+
`Pattern: ${options.values.patternSlug}`,
|
|
147
|
+
`Project directory: ${options.projectDir}`,
|
|
148
|
+
],
|
|
149
|
+
title: "✅ Added workspace pattern",
|
|
150
|
+
};
|
|
151
|
+
case "binding-source":
|
|
152
|
+
return {
|
|
153
|
+
summaryLines: [
|
|
154
|
+
`Binding source: ${options.values.bindingSourceSlug}`,
|
|
155
|
+
`Project directory: ${options.projectDir}`,
|
|
156
|
+
],
|
|
157
|
+
title: "✅ Added binding source",
|
|
158
|
+
};
|
|
159
|
+
case "hooked-block":
|
|
160
|
+
return {
|
|
161
|
+
summaryLines: [
|
|
162
|
+
`Block: ${options.values.blockSlug}`,
|
|
163
|
+
`Anchor: ${options.values.anchorBlockName}`,
|
|
164
|
+
`Position: ${options.values.position}`,
|
|
165
|
+
`Project directory: ${options.projectDir}`,
|
|
166
|
+
],
|
|
167
|
+
title: "✅ Added blockHooks metadata",
|
|
168
|
+
};
|
|
169
|
+
default:
|
|
170
|
+
return {
|
|
171
|
+
summaryLines: [
|
|
172
|
+
`Blocks: ${options.values.blockSlugs}`,
|
|
173
|
+
`Template family: ${options.values.templateId}`,
|
|
174
|
+
`Project directory: ${options.projectDir}`,
|
|
175
|
+
],
|
|
176
|
+
title: "✅ Added workspace block",
|
|
177
|
+
warningLines: options.warnings,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Prints a block of text lines using a shared line printer.
|
|
184
|
+
*
|
|
185
|
+
* @param lines Lines to print in order.
|
|
186
|
+
* @param printLine Line printer to use.
|
|
187
|
+
* @returns Nothing.
|
|
188
|
+
*/
|
|
189
|
+
export function printBlock(lines: string[], printLine: PrintLine): void {
|
|
190
|
+
for (const line of lines) {
|
|
191
|
+
printLine(line);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function formatExternalLayerSelectHint(option: ExternalLayerSelectOption): string | undefined {
|
|
196
|
+
const details = [
|
|
197
|
+
option.description,
|
|
198
|
+
option.extends.length > 0 ? `extends ${option.extends.join(", ")}` : undefined,
|
|
199
|
+
].filter((value): value is string => typeof value === "string" && value.length > 0);
|
|
200
|
+
|
|
201
|
+
return details.length > 0 ? details.join(" · ") : undefined;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Converts external layer options into prompt-compatible select items.
|
|
206
|
+
*
|
|
207
|
+
* @param options External layer options returned by the block generator.
|
|
208
|
+
* @returns Prompt select options with labels and hints.
|
|
209
|
+
*/
|
|
210
|
+
export function toExternalLayerPromptOptions(options: ExternalLayerSelectOption[]) {
|
|
211
|
+
return options.map((option) => ({
|
|
212
|
+
hint: formatExternalLayerSelectHint(option),
|
|
213
|
+
label: option.id,
|
|
214
|
+
value: option.id,
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { formatRunScript } from "@wp-typia/project-tools/package-managers";
|
|
6
|
+
|
|
7
|
+
type PackageManagerId = "bun" | "npm" | "pnpm" | "yarn";
|
|
8
|
+
|
|
9
|
+
type SyncExecutionInput = {
|
|
10
|
+
check?: boolean;
|
|
11
|
+
cwd: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type SyncProjectContext = {
|
|
15
|
+
cwd: string;
|
|
16
|
+
packageJsonPath: string;
|
|
17
|
+
packageManager: PackageManagerId;
|
|
18
|
+
scripts: Partial<Record<"sync" | "sync-rest" | "sync-types", string>>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function getSyncRootError(cwd: string): Error {
|
|
22
|
+
return new Error(
|
|
23
|
+
`No generated wp-typia project root was found at ${cwd}. Run \`wp-typia sync\` from a scaffolded project or official workspace root.`,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function inferSyncPackageManager(cwd: string, packageManagerField?: string): PackageManagerId {
|
|
28
|
+
const field = String(packageManagerField ?? "");
|
|
29
|
+
if (field.startsWith("bun@")) return "bun";
|
|
30
|
+
if (field.startsWith("npm@")) return "npm";
|
|
31
|
+
if (field.startsWith("pnpm@")) return "pnpm";
|
|
32
|
+
if (field.startsWith("yarn@")) return "yarn";
|
|
33
|
+
|
|
34
|
+
if (
|
|
35
|
+
fs.existsSync(path.join(cwd, "bun.lock")) ||
|
|
36
|
+
fs.existsSync(path.join(cwd, "bun.lockb"))
|
|
37
|
+
) {
|
|
38
|
+
return "bun";
|
|
39
|
+
}
|
|
40
|
+
if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) {
|
|
41
|
+
return "pnpm";
|
|
42
|
+
}
|
|
43
|
+
if (
|
|
44
|
+
fs.existsSync(path.join(cwd, "yarn.lock")) ||
|
|
45
|
+
fs.existsSync(path.join(cwd, ".pnp.cjs")) ||
|
|
46
|
+
fs.existsSync(path.join(cwd, ".pnp.loader.mjs")) ||
|
|
47
|
+
fs.existsSync(path.join(cwd, ".yarnrc.yml"))
|
|
48
|
+
) {
|
|
49
|
+
return "yarn";
|
|
50
|
+
}
|
|
51
|
+
if (
|
|
52
|
+
fs.existsSync(path.join(cwd, "package-lock.json")) ||
|
|
53
|
+
fs.existsSync(path.join(cwd, "npm-shrinkwrap.json"))
|
|
54
|
+
) {
|
|
55
|
+
return "npm";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return "npm";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function resolveSyncProjectContext(cwd: string): SyncProjectContext {
|
|
62
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
63
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
64
|
+
throw getSyncRootError(cwd);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")) as {
|
|
68
|
+
packageManager?: string;
|
|
69
|
+
scripts?: Record<string, unknown>;
|
|
70
|
+
};
|
|
71
|
+
const scripts = packageJson.scripts ?? {};
|
|
72
|
+
const syncScripts = {
|
|
73
|
+
sync: typeof scripts.sync === "string" ? scripts.sync : undefined,
|
|
74
|
+
"sync-rest":
|
|
75
|
+
typeof scripts["sync-rest"] === "string" ? scripts["sync-rest"] : undefined,
|
|
76
|
+
"sync-types":
|
|
77
|
+
typeof scripts["sync-types"] === "string" ? scripts["sync-types"] : undefined,
|
|
78
|
+
} satisfies SyncProjectContext["scripts"];
|
|
79
|
+
|
|
80
|
+
if (!syncScripts.sync && !syncScripts["sync-types"]) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Expected ${packageJsonPath} to define either a \`sync\` or \`sync-types\` script.`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
cwd,
|
|
88
|
+
packageJsonPath,
|
|
89
|
+
packageManager: inferSyncPackageManager(cwd, packageJson.packageManager),
|
|
90
|
+
scripts: syncScripts,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function getPackageManagerRunInvocation(
|
|
95
|
+
packageManager: PackageManagerId,
|
|
96
|
+
scriptName: string,
|
|
97
|
+
extraArgs: string[],
|
|
98
|
+
): { args: string[]; command: string } {
|
|
99
|
+
switch (packageManager) {
|
|
100
|
+
case "bun":
|
|
101
|
+
return { args: ["run", scriptName, ...extraArgs], command: "bun" };
|
|
102
|
+
case "npm":
|
|
103
|
+
return {
|
|
104
|
+
args: ["run", scriptName, ...(extraArgs.length > 0 ? ["--", ...extraArgs] : [])],
|
|
105
|
+
command: "npm",
|
|
106
|
+
};
|
|
107
|
+
case "pnpm":
|
|
108
|
+
return { args: ["run", scriptName, ...extraArgs], command: "pnpm" };
|
|
109
|
+
case "yarn":
|
|
110
|
+
return { args: ["run", scriptName, ...extraArgs], command: "yarn" };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function runProjectScript(
|
|
115
|
+
project: SyncProjectContext,
|
|
116
|
+
scriptName: "sync" | "sync-rest" | "sync-types",
|
|
117
|
+
extraArgs: string[],
|
|
118
|
+
): void {
|
|
119
|
+
const script = project.scripts[scriptName];
|
|
120
|
+
if (!script) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const invocation = getPackageManagerRunInvocation(
|
|
125
|
+
project.packageManager,
|
|
126
|
+
scriptName,
|
|
127
|
+
extraArgs,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const result = spawnSync(invocation.command, invocation.args, {
|
|
131
|
+
cwd: project.cwd,
|
|
132
|
+
shell: process.platform === "win32",
|
|
133
|
+
stdio: "inherit",
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (result.error || result.status !== 0) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
`\`${formatRunScript(project.packageManager, scriptName, extraArgs.join(" "))}\` failed.`,
|
|
139
|
+
{
|
|
140
|
+
cause: result.error,
|
|
141
|
+
},
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Executes the generated-project sync flow through the local project scripts.
|
|
148
|
+
*
|
|
149
|
+
* @param options Sync execution options including cwd and optional `--check`.
|
|
150
|
+
* @returns A promise that resolves after the relevant sync scripts complete.
|
|
151
|
+
*/
|
|
152
|
+
export async function executeSyncCommand({
|
|
153
|
+
check = false,
|
|
154
|
+
cwd,
|
|
155
|
+
}: SyncExecutionInput): Promise<void> {
|
|
156
|
+
const project = resolveSyncProjectContext(cwd);
|
|
157
|
+
const extraArgs = check ? ["--check"] : [];
|
|
158
|
+
|
|
159
|
+
if (project.scripts.sync) {
|
|
160
|
+
runProjectScript(project, "sync", extraArgs);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
runProjectScript(project, "sync-types", extraArgs);
|
|
165
|
+
|
|
166
|
+
if (project.scripts["sync-rest"]) {
|
|
167
|
+
runProjectScript(project, "sync-rest", extraArgs);
|
|
168
|
+
}
|
|
169
|
+
}
|
package/src/runtime-bridge.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
|
|
5
1
|
import {
|
|
6
2
|
formatTemplateDetails,
|
|
7
3
|
formatTemplateFeatures,
|
|
@@ -9,10 +5,23 @@ import {
|
|
|
9
5
|
getTemplateById,
|
|
10
6
|
listTemplates,
|
|
11
7
|
} from "@wp-typia/project-tools/cli-templates";
|
|
12
|
-
import { formatRunScript } from "@wp-typia/project-tools/package-managers";
|
|
13
8
|
import { tryResolveWorkspaceProject } from "@wp-typia/project-tools/workspace-project";
|
|
14
9
|
import type { ReadlinePrompt } from "@wp-typia/project-tools/cli-prompt";
|
|
15
10
|
import type { AlternateBufferCompletionPayload } from "./ui/alternate-buffer-lifecycle";
|
|
11
|
+
import {
|
|
12
|
+
buildAddCompletionPayload,
|
|
13
|
+
buildCreateCompletionPayload,
|
|
14
|
+
buildMigrationCompletionPayload,
|
|
15
|
+
printBlock,
|
|
16
|
+
printCompletionPayload,
|
|
17
|
+
toExternalLayerPromptOptions,
|
|
18
|
+
} from "./runtime-bridge-output";
|
|
19
|
+
export {
|
|
20
|
+
buildCreateCompletionPayload,
|
|
21
|
+
buildMigrationCompletionPayload,
|
|
22
|
+
printCompletionPayload,
|
|
23
|
+
} from "./runtime-bridge-output";
|
|
24
|
+
export { executeSyncCommand } from "./runtime-bridge-sync";
|
|
16
25
|
|
|
17
26
|
type CreateExecutionInput = {
|
|
18
27
|
projectDir: string;
|
|
@@ -44,11 +53,6 @@ type TemplatesExecutionInput = {
|
|
|
44
53
|
};
|
|
45
54
|
};
|
|
46
55
|
|
|
47
|
-
type SyncExecutionInput = {
|
|
48
|
-
check?: boolean;
|
|
49
|
-
cwd: string;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
56
|
type MigrateExecutionInput = {
|
|
53
57
|
command?: string;
|
|
54
58
|
cwd: string;
|
|
@@ -58,20 +62,7 @@ type MigrateExecutionInput = {
|
|
|
58
62
|
};
|
|
59
63
|
|
|
60
64
|
type PrintLine = (line: string) => void;
|
|
61
|
-
type PackageManagerId = "bun" | "npm" | "pnpm" | "yarn";
|
|
62
65
|
type CliCommandId = "add" | "create" | "doctor" | "migrate";
|
|
63
|
-
type ExternalLayerSelectOption = {
|
|
64
|
-
description?: string;
|
|
65
|
-
extends: string[];
|
|
66
|
-
id: string;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
type SyncProjectContext = {
|
|
70
|
-
cwd: string;
|
|
71
|
-
packageJsonPath: string;
|
|
72
|
-
packageManager: PackageManagerId;
|
|
73
|
-
scripts: Partial<Record<"sync" | "sync-rest" | "sync-types", string>>;
|
|
74
|
-
};
|
|
75
66
|
|
|
76
67
|
const loadCliAddRuntime = () => import("@wp-typia/project-tools/cli-add");
|
|
77
68
|
const loadCliDiagnosticsRuntime = () => import("@wp-typia/project-tools/cli-diagnostics");
|
|
@@ -101,176 +92,6 @@ function shouldWrapCliCommandError(options: {
|
|
|
101
92
|
return true;
|
|
102
93
|
}
|
|
103
94
|
|
|
104
|
-
function printBlock(lines: string[], printLine: PrintLine): void {
|
|
105
|
-
for (const line of lines) {
|
|
106
|
-
printLine(line);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function formatExternalLayerSelectHint(option: ExternalLayerSelectOption): string | undefined {
|
|
111
|
-
const details = [
|
|
112
|
-
option.description,
|
|
113
|
-
option.extends.length > 0 ? `extends ${option.extends.join(", ")}` : undefined,
|
|
114
|
-
].filter((value): value is string => typeof value === "string" && value.length > 0);
|
|
115
|
-
|
|
116
|
-
return details.length > 0 ? details.join(" · ") : undefined;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function toExternalLayerPromptOptions(options: ExternalLayerSelectOption[]) {
|
|
120
|
-
return options.map((option) => ({
|
|
121
|
-
hint: formatExternalLayerSelectHint(option),
|
|
122
|
-
label: option.id,
|
|
123
|
-
value: option.id,
|
|
124
|
-
}));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function printCompletionPayload(
|
|
128
|
-
payload: AlternateBufferCompletionPayload,
|
|
129
|
-
options: {
|
|
130
|
-
printLine?: PrintLine;
|
|
131
|
-
warnLine?: PrintLine;
|
|
132
|
-
} = {},
|
|
133
|
-
): void {
|
|
134
|
-
const printLine = options.printLine ?? (console.log as PrintLine);
|
|
135
|
-
const warnLine = options.warnLine ?? printLine;
|
|
136
|
-
|
|
137
|
-
for (const line of payload.preambleLines ?? []) {
|
|
138
|
-
printLine(line);
|
|
139
|
-
}
|
|
140
|
-
for (const warning of payload.warningLines ?? []) {
|
|
141
|
-
warnLine(`⚠️ ${warning}`);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const hasDetails =
|
|
145
|
-
(payload.summaryLines?.length ?? 0) > 0 ||
|
|
146
|
-
(payload.nextSteps?.length ?? 0) > 0 ||
|
|
147
|
-
(payload.optionalLines?.length ?? 0) > 0 ||
|
|
148
|
-
Boolean(payload.optionalNote);
|
|
149
|
-
const hasLeadingContext =
|
|
150
|
-
(payload.preambleLines?.length ?? 0) > 0 ||
|
|
151
|
-
(payload.warningLines?.length ?? 0) > 0;
|
|
152
|
-
|
|
153
|
-
printLine(hasLeadingContext && hasDetails ? `\n${payload.title}` : payload.title);
|
|
154
|
-
for (const line of payload.summaryLines ?? []) {
|
|
155
|
-
printLine(line);
|
|
156
|
-
}
|
|
157
|
-
if ((payload.nextSteps?.length ?? 0) > 0) {
|
|
158
|
-
printLine("Next steps:");
|
|
159
|
-
for (const step of payload.nextSteps ?? []) {
|
|
160
|
-
printLine(` ${step}`);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
if ((payload.optionalLines?.length ?? 0) > 0) {
|
|
164
|
-
printLine(`\n${payload.optionalTitle ?? "Optional:"}`);
|
|
165
|
-
for (const step of payload.optionalLines ?? []) {
|
|
166
|
-
printLine(` ${step}`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
if (payload.optionalNote) {
|
|
170
|
-
printLine(`Note: ${payload.optionalNote}`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export function buildCreateCompletionPayload(flow: {
|
|
175
|
-
nextSteps: string[];
|
|
176
|
-
optionalOnboarding: {
|
|
177
|
-
note: string;
|
|
178
|
-
steps: string[];
|
|
179
|
-
};
|
|
180
|
-
projectDir: string;
|
|
181
|
-
result: {
|
|
182
|
-
selectedVariant?: string | null;
|
|
183
|
-
variables: {
|
|
184
|
-
title: string;
|
|
185
|
-
};
|
|
186
|
-
warnings: string[];
|
|
187
|
-
};
|
|
188
|
-
}): AlternateBufferCompletionPayload {
|
|
189
|
-
return {
|
|
190
|
-
nextSteps: flow.nextSteps,
|
|
191
|
-
optionalLines:
|
|
192
|
-
flow.optionalOnboarding.steps.length > 0 ? flow.optionalOnboarding.steps : undefined,
|
|
193
|
-
optionalNote:
|
|
194
|
-
flow.optionalOnboarding.steps.length > 0 ? flow.optionalOnboarding.note : undefined,
|
|
195
|
-
optionalTitle:
|
|
196
|
-
flow.optionalOnboarding.steps.length > 0 ? "Optional before first commit:" : undefined,
|
|
197
|
-
preambleLines: flow.result.selectedVariant
|
|
198
|
-
? [`Template variant: ${flow.result.selectedVariant}`]
|
|
199
|
-
: undefined,
|
|
200
|
-
summaryLines: [`Project directory: ${flow.projectDir}`],
|
|
201
|
-
title: `✅ Created ${flow.result.variables.title} in ${flow.projectDir}`,
|
|
202
|
-
warningLines: flow.result.warnings,
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export function buildMigrationCompletionPayload(options: {
|
|
207
|
-
command: string;
|
|
208
|
-
lines: string[];
|
|
209
|
-
}): AlternateBufferCompletionPayload {
|
|
210
|
-
const summaryLines = options.lines.filter((line) => line.trim().length > 0);
|
|
211
|
-
|
|
212
|
-
return {
|
|
213
|
-
summaryLines,
|
|
214
|
-
title: `✅ Completed wp-typia migrate ${options.command}`,
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function buildAddCompletionPayload(options: {
|
|
219
|
-
kind: "binding-source" | "block" | "hooked-block" | "pattern" | "variation";
|
|
220
|
-
projectDir: string;
|
|
221
|
-
values: Record<string, string>;
|
|
222
|
-
warnings?: string[];
|
|
223
|
-
}): AlternateBufferCompletionPayload {
|
|
224
|
-
switch (options.kind) {
|
|
225
|
-
case "variation":
|
|
226
|
-
return {
|
|
227
|
-
summaryLines: [
|
|
228
|
-
`Variation: ${options.values.variationSlug}`,
|
|
229
|
-
`Target block: ${options.values.blockSlug}`,
|
|
230
|
-
`Project directory: ${options.projectDir}`,
|
|
231
|
-
],
|
|
232
|
-
title: "✅ Added workspace variation",
|
|
233
|
-
};
|
|
234
|
-
case "pattern":
|
|
235
|
-
return {
|
|
236
|
-
summaryLines: [
|
|
237
|
-
`Pattern: ${options.values.patternSlug}`,
|
|
238
|
-
`Project directory: ${options.projectDir}`,
|
|
239
|
-
],
|
|
240
|
-
title: "✅ Added workspace pattern",
|
|
241
|
-
};
|
|
242
|
-
case "binding-source":
|
|
243
|
-
return {
|
|
244
|
-
summaryLines: [
|
|
245
|
-
`Binding source: ${options.values.bindingSourceSlug}`,
|
|
246
|
-
`Project directory: ${options.projectDir}`,
|
|
247
|
-
],
|
|
248
|
-
title: "✅ Added binding source",
|
|
249
|
-
};
|
|
250
|
-
case "hooked-block":
|
|
251
|
-
return {
|
|
252
|
-
summaryLines: [
|
|
253
|
-
`Block: ${options.values.blockSlug}`,
|
|
254
|
-
`Anchor: ${options.values.anchorBlockName}`,
|
|
255
|
-
`Position: ${options.values.position}`,
|
|
256
|
-
`Project directory: ${options.projectDir}`,
|
|
257
|
-
],
|
|
258
|
-
title: "✅ Added blockHooks metadata",
|
|
259
|
-
};
|
|
260
|
-
case "block":
|
|
261
|
-
default:
|
|
262
|
-
return {
|
|
263
|
-
summaryLines: [
|
|
264
|
-
`Blocks: ${options.values.blockSlugs}`,
|
|
265
|
-
`Template family: ${options.values.templateId}`,
|
|
266
|
-
`Project directory: ${options.projectDir}`,
|
|
267
|
-
],
|
|
268
|
-
title: "✅ Added workspace block",
|
|
269
|
-
warningLines: options.warnings,
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
95
|
function readOptionalStringFlag(
|
|
275
96
|
flags: Record<string, unknown>,
|
|
276
97
|
name: string,
|
|
@@ -311,131 +132,6 @@ function pushFlag(argv: string[], name: string, value: unknown): void {
|
|
|
311
132
|
argv.push(`--${name}`, String(value));
|
|
312
133
|
}
|
|
313
134
|
|
|
314
|
-
function getSyncRootError(cwd: string): Error {
|
|
315
|
-
return new Error(
|
|
316
|
-
`No generated wp-typia project root was found at ${cwd}. Run \`wp-typia sync\` from a scaffolded project or official workspace root.`,
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function inferSyncPackageManager(cwd: string, packageManagerField?: string): PackageManagerId {
|
|
321
|
-
const field = String(packageManagerField ?? "");
|
|
322
|
-
if (field.startsWith("bun@")) return "bun";
|
|
323
|
-
if (field.startsWith("npm@")) return "npm";
|
|
324
|
-
if (field.startsWith("pnpm@")) return "pnpm";
|
|
325
|
-
if (field.startsWith("yarn@")) return "yarn";
|
|
326
|
-
|
|
327
|
-
if (
|
|
328
|
-
fs.existsSync(path.join(cwd, "bun.lock")) ||
|
|
329
|
-
fs.existsSync(path.join(cwd, "bun.lockb"))
|
|
330
|
-
) {
|
|
331
|
-
return "bun";
|
|
332
|
-
}
|
|
333
|
-
if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) {
|
|
334
|
-
return "pnpm";
|
|
335
|
-
}
|
|
336
|
-
if (
|
|
337
|
-
fs.existsSync(path.join(cwd, "yarn.lock")) ||
|
|
338
|
-
fs.existsSync(path.join(cwd, ".pnp.cjs")) ||
|
|
339
|
-
fs.existsSync(path.join(cwd, ".pnp.loader.mjs")) ||
|
|
340
|
-
fs.existsSync(path.join(cwd, ".yarnrc.yml"))
|
|
341
|
-
) {
|
|
342
|
-
return "yarn";
|
|
343
|
-
}
|
|
344
|
-
if (
|
|
345
|
-
fs.existsSync(path.join(cwd, "package-lock.json")) ||
|
|
346
|
-
fs.existsSync(path.join(cwd, "npm-shrinkwrap.json"))
|
|
347
|
-
) {
|
|
348
|
-
return "npm";
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
return "npm";
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function resolveSyncProjectContext(cwd: string): SyncProjectContext {
|
|
355
|
-
const packageJsonPath = path.join(cwd, "package.json");
|
|
356
|
-
if (!fs.existsSync(packageJsonPath)) {
|
|
357
|
-
throw getSyncRootError(cwd);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")) as {
|
|
361
|
-
packageManager?: string;
|
|
362
|
-
scripts?: Record<string, unknown>;
|
|
363
|
-
};
|
|
364
|
-
const scripts = packageJson.scripts ?? {};
|
|
365
|
-
const syncScripts = {
|
|
366
|
-
sync: typeof scripts.sync === "string" ? scripts.sync : undefined,
|
|
367
|
-
"sync-rest":
|
|
368
|
-
typeof scripts["sync-rest"] === "string" ? scripts["sync-rest"] : undefined,
|
|
369
|
-
"sync-types":
|
|
370
|
-
typeof scripts["sync-types"] === "string" ? scripts["sync-types"] : undefined,
|
|
371
|
-
} satisfies SyncProjectContext["scripts"];
|
|
372
|
-
|
|
373
|
-
if (!syncScripts.sync && !syncScripts["sync-types"]) {
|
|
374
|
-
throw new Error(
|
|
375
|
-
`Expected ${packageJsonPath} to define either a \`sync\` or \`sync-types\` script.`,
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
return {
|
|
380
|
-
cwd,
|
|
381
|
-
packageJsonPath,
|
|
382
|
-
packageManager: inferSyncPackageManager(cwd, packageJson.packageManager),
|
|
383
|
-
scripts: syncScripts,
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
function getPackageManagerRunInvocation(
|
|
388
|
-
packageManager: PackageManagerId,
|
|
389
|
-
scriptName: string,
|
|
390
|
-
extraArgs: string[],
|
|
391
|
-
): { args: string[]; command: string } {
|
|
392
|
-
switch (packageManager) {
|
|
393
|
-
case "bun":
|
|
394
|
-
return { args: ["run", scriptName, ...extraArgs], command: "bun" };
|
|
395
|
-
case "npm":
|
|
396
|
-
return {
|
|
397
|
-
args: ["run", scriptName, ...(extraArgs.length > 0 ? ["--", ...extraArgs] : [])],
|
|
398
|
-
command: "npm",
|
|
399
|
-
};
|
|
400
|
-
case "pnpm":
|
|
401
|
-
return { args: ["run", scriptName, ...extraArgs], command: "pnpm" };
|
|
402
|
-
case "yarn":
|
|
403
|
-
return { args: ["run", scriptName, ...extraArgs], command: "yarn" };
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
function runProjectScript(
|
|
408
|
-
project: SyncProjectContext,
|
|
409
|
-
scriptName: "sync" | "sync-rest" | "sync-types",
|
|
410
|
-
extraArgs: string[],
|
|
411
|
-
): void {
|
|
412
|
-
const script = project.scripts[scriptName];
|
|
413
|
-
if (!script) {
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const invocation = getPackageManagerRunInvocation(
|
|
418
|
-
project.packageManager,
|
|
419
|
-
scriptName,
|
|
420
|
-
extraArgs,
|
|
421
|
-
);
|
|
422
|
-
|
|
423
|
-
const result = spawnSync(invocation.command, invocation.args, {
|
|
424
|
-
cwd: project.cwd,
|
|
425
|
-
shell: process.platform === "win32",
|
|
426
|
-
stdio: "inherit",
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
if (result.error || result.status !== 0) {
|
|
430
|
-
throw new Error(
|
|
431
|
-
`\`${formatRunScript(project.packageManager, scriptName, extraArgs.join(" "))}\` failed.`,
|
|
432
|
-
{
|
|
433
|
-
cause: result.error,
|
|
434
|
-
},
|
|
435
|
-
);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
135
|
const PACKAGE_MANAGER_PROMPT_OPTIONS = [
|
|
440
136
|
{ label: "npm", value: "npm", hint: "Use npm" },
|
|
441
137
|
{ label: "pnpm", value: "pnpm", hint: "Use pnpm" },
|
|
@@ -844,25 +540,6 @@ export async function loadAddWorkspaceBlockOptions(cwd: string) {
|
|
|
844
540
|
return getWorkspaceBlockSelectOptions(workspace.projectDir);
|
|
845
541
|
}
|
|
846
542
|
|
|
847
|
-
export async function executeSyncCommand({
|
|
848
|
-
check = false,
|
|
849
|
-
cwd,
|
|
850
|
-
}: SyncExecutionInput): Promise<void> {
|
|
851
|
-
const project = resolveSyncProjectContext(cwd);
|
|
852
|
-
const extraArgs = check ? ["--check"] : [];
|
|
853
|
-
|
|
854
|
-
if (project.scripts.sync) {
|
|
855
|
-
runProjectScript(project, "sync", extraArgs);
|
|
856
|
-
return;
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
runProjectScript(project, "sync-types", extraArgs);
|
|
860
|
-
|
|
861
|
-
if (project.scripts["sync-rest"]) {
|
|
862
|
-
runProjectScript(project, "sync-rest", extraArgs);
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
|
|
866
543
|
export async function executeMigrateCommand({
|
|
867
544
|
command,
|
|
868
545
|
cwd,
|