Skip to content

Commit 37268da

Browse files
committed
Scope CLI options per command
1 parent fc63ce7 commit 37268da

File tree

1 file changed

+113
-89
lines changed

1 file changed

+113
-89
lines changed

packages/playground/cli/src/run-cli.ts

Lines changed: 113 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import { SupportedPHPVersions } from '@php-wasm/universal';
3939
import { cpus } from 'os';
4040
import { jspi } from 'wasm-feature-detect';
4141
import type { MessagePort as NodeMessagePort } from 'worker_threads';
42-
import yargs from 'yargs';
42+
import yargs, { type Argv, type Options as YargsOptions } from 'yargs';
4343
import { isValidWordPressSlug } from './is-valid-wordpress-slug';
4444
import { resolveBlueprint } from './resolve-blueprint';
4545
import { BlueprintsV2Handler } from './blueprints-v2/blueprints-v2-handler';
@@ -85,93 +85,71 @@ export async function parseOptionsAndRunCLI(argsToParse: string[]) {
8585
* @TODO This looks similar to Query API args https://wordpress.github.io/wordpress-playground/developers/apis/query-api/
8686
* Perhaps the two could be handled by the same code?
8787
*/
88-
const yargsObject = yargs(argsToParse)
89-
.usage('Usage: wp-playground <command> [options]')
90-
.command('server', 'Start a local WordPress server')
91-
.command(
92-
'run-blueprint',
93-
'Execute a Blueprint without starting a server'
94-
)
95-
.command(
96-
'build-snapshot',
97-
'Build a ZIP snapshot of a WordPress site based on a Blueprint'
98-
)
99-
.demandCommand(1, 'Please specify a command')
100-
.strictCommands()
101-
.option('outfile', {
102-
describe: 'When building, write to this output file.',
103-
type: 'string',
104-
default: 'wordpress.zip',
105-
})
106-
.option('port', {
107-
describe: 'Port to listen on when serving.',
108-
type: 'number',
109-
default: 9400,
110-
})
111-
.option('site-url', {
88+
const sharedOptions: Record<string, YargsOptions> = {
89+
'site-url': {
11290
describe:
11391
'Site URL to use for WordPress. Defaults to http://127.0.0.1:{port}',
11492
type: 'string',
115-
})
116-
.option('php', {
93+
},
94+
php: {
11795
describe: 'PHP version to use.',
11896
type: 'string',
11997
default: RecommendedPHPVersion,
12098
choices: SupportedPHPVersions,
121-
})
122-
.option('wp', {
99+
},
100+
wp: {
123101
describe: 'WordPress version to use.',
124102
type: 'string',
125103
default: 'latest',
126-
})
104+
},
127105
// @TODO: Support read-only mounts, e.g. via WORKERFS, a custom
128106
// ReadOnlyNODEFS, or by copying the files into MEMFS
129-
.option('mount', {
107+
mount: {
130108
describe:
131109
'Mount a directory to the PHP runtime (can be used multiple times). Format: /host/path:/vfs/path',
132110
type: 'array',
133111
string: true,
134112
coerce: parseMountWithDelimiterArguments,
135-
})
136-
.option('mount-before-install', {
113+
},
114+
'mount-before-install': {
137115
describe:
138116
'Mount a directory to the PHP runtime before WordPress installation (can be used multiple times). Format: /host/path:/vfs/path',
139117
type: 'array',
140118
string: true,
141119
coerce: parseMountWithDelimiterArguments,
142-
})
143-
.option('mount-dir', {
120+
},
121+
'mount-dir': {
144122
describe:
145123
'Mount a directory to the PHP runtime (can be used multiple times). Format: "/host/path" "/vfs/path"',
146124
type: 'array',
147125
nargs: 2,
148126
array: true,
149127
coerce: parseMountDirArguments,
150-
})
151-
.option('mount-dir-before-install', {
128+
},
129+
'mount-dir-before-install': {
152130
describe:
153131
'Mount a directory before WordPress installation (can be used multiple times). Format: "/host/path" "/vfs/path"',
154132
type: 'string',
155133
nargs: 2,
156134
array: true,
157135
coerce: parseMountDirArguments,
158-
})
159-
.option('login', {
136+
},
137+
login: {
160138
describe: 'Should log the user in',
161139
type: 'boolean',
162140
default: false,
163-
})
164-
.option('blueprint', {
141+
},
142+
blueprint: {
165143
describe: 'Blueprint to execute.',
166144
type: 'string',
167-
})
168-
.option('blueprint-may-read-adjacent-files', {
145+
},
146+
'blueprint-may-read-adjacent-files': {
169147
describe:
170148
'Consent flag: Allow "bundled" resources in a local blueprint to read files in the same directory as the blueprint file.',
171149
type: 'boolean',
172150
default: false,
173-
})
174-
.option('wordpress-install-mode', {
151+
},
152+
'wordpress-install-mode': {
175153
describe:
176154
'Control how Playground prepares WordPress before booting.',
177155
type: 'string',
@@ -182,71 +160,71 @@ export async function parseOptionsAndRunCLI(argsToParse: string[]) {
182160
'install-from-existing-files-if-needed',
183161
'do-not-attempt-installing',
184162
] as const,
185-
})
186-
.option('skip-wordpress-install', {
163+
},
164+
'skip-wordpress-install': {
187165
describe: '[Deprecated] Use --wordpress-install-mode instead.',
188166
type: 'boolean',
189167
hidden: true,
190-
})
191-
.option('skip-sqlite-setup', {
168+
},
169+
'skip-sqlite-setup': {
192170
describe:
193171
'Skip the SQLite integration plugin setup to allow the WordPress site to use MySQL.',
194172
type: 'boolean',
195173
default: false,
196-
})
174+
},
197175
// Hidden - Deprecated in favor of verbosity
198-
.option('quiet', {
176+
quiet: {
199177
describe: 'Do not output logs and progress messages.',
200178
type: 'boolean',
201179
default: false,
202180
hidden: true,
203-
})
204-
.option('verbosity', {
181+
},
182+
verbosity: {
205183
describe: 'Output logs and progress messages.',
206184
type: 'string',
207185
choices: Object.values(LogVerbosity).map(
208186
(verbosity) => verbosity.name
209187
),
210188
default: 'normal',
211-
})
212-
.option('debug', {
189+
},
190+
debug: {
213191
describe:
214192
'Print PHP error log content if an error occurs during Playground boot.',
215193
type: 'boolean',
216194
default: false,
217-
})
218-
.option('auto-mount', {
195+
},
196+
'auto-mount': {
219197
describe: `Automatically mount the specified directory. If no path is provided, mount the current working directory. You can mount a WordPress directory, a plugin directory, a theme directory, a wp-content directory, or any directory containing PHP and HTML files.`,
220198
type: 'string',
221-
})
222-
.option('follow-symlinks', {
199+
},
200+
'follow-symlinks': {
223201
describe:
224202
'Allow Playground to follow symlinks by automatically mounting symlinked directories and files encountered in mounted directories. \nWarning: Following symlinks will expose files outside mounted directories to Playground and could be a security risk.',
225203
type: 'boolean',
226204
default: false,
227-
})
228-
.option('experimental-trace', {
205+
},
206+
'experimental-trace': {
229207
describe:
230208
'Print detailed messages about system behavior to the console. Useful for troubleshooting.',
231209
type: 'boolean',
232210
default: false,
233211
// Hide this option because we want to replace with a more general log-level flag.
234212
hidden: true,
235-
})
236-
.option('internal-cookie-store', {
213+
},
214+
'internal-cookie-store': {
237215
describe:
238216
'Enable internal cookie handling. When enabled, Playground will manage cookies internally using ' +
239217
'an HttpCookieStore that persists cookies across requests. When disabled, cookies are handled ' +
240218
'externally (e.g., by a browser in Node.js environments).',
241219
type: 'boolean',
242220
default: false,
243-
})
244-
.option('xdebug', {
221+
},
222+
xdebug: {
245223
describe: 'Enable Xdebug.',
246224
type: 'boolean',
247225
default: false,
248-
})
249-
.option('experimental-unsafe-ide-integration', {
226+
},
227+
'experimental-unsafe-ide-integration': {
250228
describe:
251229
'Enable experimental IDE development tools. This option edits IDE config files ' +
252230
'to set Xdebug path mappings and web server details. CAUTION: If there are bugs, ' +
@@ -259,39 +237,85 @@ export async function parseOptionsAndRunCLI(argsToParse: string[]) {
259237
choices: ['', 'vscode', 'phpstorm'],
260238
coerce: (value?: string) =>
261239
value === '' ? ['vscode', 'phpstorm'] : [value],
262-
})
263-
.option('experimental-devtools', {
264-
describe: 'Enable experimental browser development tools.',
265-
type: 'boolean',
266-
})
267-
.conflicts(
268-
'experimental-unsafe-ide-integration',
269-
'experimental-devtools'
270-
)
271-
.option('experimental-multi-worker', {
272-
describe:
273-
'Enable experimental multi-worker support which requires ' +
274-
'a /wordpress directory backed by a real filesystem. ' +
275-
'Pass a positive number to specify the number of workers to use. ' +
276-
'Otherwise, default to the number of CPUs minus 1.',
277-
type: 'number',
278-
coerce: (value?: number) => value ?? cpus().length - 1,
279-
})
280-
.option('experimental-blueprints-v2-runner', {
240+
},
241+
'experimental-blueprints-v2-runner': {
281242
describe: 'Use the experimental Blueprint V2 runner.',
282243
type: 'boolean',
283244
default: false,
284245
// Remove the "hidden" flag once Blueprint V2 is fully supported
285246
hidden: true,
286-
})
287-
.option('mode', {
247+
},
248+
mode: {
288249
describe:
289250
'Blueprints v2 runner mode to use. This option is required when using the --experimental-blueprints-v2-runner flag with a blueprint.',
290251
type: 'string',
291252
choices: ['create-new-site', 'apply-to-existing-site'],
292253
// Remove the "hidden" flag once Blueprint V2 is fully supported
293254
hidden: true,
294-
})
255+
},
256+
};
257+
258+
const serverOnlyOptions: Record<string, YargsOptions> = {
259+
port: {
260+
describe: 'Port to listen on when serving.',
261+
type: 'number',
262+
default: 9400,
263+
},
264+
'experimental-multi-worker': {
265+
describe:
266+
'Enable experimental multi-worker support which requires ' +
267+
'a /wordpress directory backed by a real filesystem. ' +
268+
'Pass a positive number to specify the number of workers to use. ' +
269+
'Otherwise, default to the number of CPUs minus 1.',
270+
type: 'number',
271+
coerce: (value?: number) => value ?? cpus().length - 1,
272+
},
273+
'experimental-devtools': {
274+
describe: 'Enable experimental browser development tools.',
275+
type: 'boolean',
276+
},
277+
};
278+
279+
const buildSnapshotOnlyOptions: Record<string, YargsOptions> = {
280+
outfile: {
281+
describe: 'When building, write to this output file.',
282+
type: 'string',
283+
default: 'wordpress.zip',
284+
},
285+
};
286+
287+
const commandWithOptions =
288+
(commandOptions: Record<string, YargsOptions> = {}) =>
289+
(yargsInstance: Argv) =>
290+
yargsInstance.options({ ...sharedOptions, ...commandOptions });
291+
292+
const yargsObject = yargs(argsToParse)
293+
.usage('Usage: wp-playground <command> [options]')
294+
.command(
295+
'server',
296+
'Start a local WordPress server',
297+
commandWithOptions({
298+
...serverOnlyOptions,
299+
})
300+
)
301+
.command(
302+
'run-blueprint',
303+
'Execute a Blueprint without starting a server',
304+
commandWithOptions()
305+
)
306+
.command(
307+
'build-snapshot',
308+
'Build a ZIP snapshot of a WordPress site based on a Blueprint',
309+
commandWithOptions({
310+
...buildSnapshotOnlyOptions,
311+
})
312+
)
313+
.demandCommand(1, 'Please specify a command')
314+
.strictCommands()
315+
.conflicts(
316+
'experimental-unsafe-ide-integration',
317+
'experimental-devtools'
318+
)
295319
.showHelpOnFail(false)
296320
.fail((msg, err, yargsInstance) => {
297321
if (err) {

0 commit comments

Comments
 (0)