Skip to content

Commit d30905a

Browse files
committed
Scope CLI options per command
1 parent bc5e668 commit d30905a

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
@@ -40,7 +40,7 @@ import { SupportedPHPVersions } from '@php-wasm/universal';
4040
import { cpus } from 'os';
4141
import { jspi } from 'wasm-feature-detect';
4242
import type { MessagePort as NodeMessagePort } from 'worker_threads';
43-
import yargs from 'yargs';
43+
import yargs, { type Argv, type Options as YargsOptions } from 'yargs';
4444
import { isValidWordPressSlug } from './is-valid-wordpress-slug';
4545
import { resolveBlueprint } from './resolve-blueprint';
4646
import { BlueprintsV2Handler } from './blueprints-v2/blueprints-v2-handler';
@@ -86,93 +86,71 @@ export async function parseOptionsAndRunCLI(argsToParse: string[]) {
8686
* @TODO This looks similar to Query API args https://wordpress.github.io/wordpress-playground/developers/apis/query-api/
8787
* Perhaps the two could be handled by the same code?
8888
*/
89-
const yargsObject = yargs(argsToParse)
90-
.usage('Usage: wp-playground <command> [options]')
91-
.command('server', 'Start a local WordPress server')
92-
.command(
93-
'run-blueprint',
94-
'Execute a Blueprint without starting a server'
95-
)
96-
.command(
97-
'build-snapshot',
98-
'Build a ZIP snapshot of a WordPress site based on a Blueprint'
99-
)
100-
.demandCommand(1, 'Please specify a command')
101-
.strictCommands()
102-
.option('outfile', {
103-
describe: 'When building, write to this output file.',
104-
type: 'string',
105-
default: 'wordpress.zip',
106-
})
107-
.option('port', {
108-
describe: 'Port to listen on when serving.',
109-
type: 'number',
110-
default: 9400,
111-
})
112-
.option('site-url', {
89+
const sharedOptions: Record<string, YargsOptions> = {
90+
'site-url': {
11391
describe:
11492
'Site URL to use for WordPress. Defaults to http://127.0.0.1:{port}',
11593
type: 'string',
116-
})
117-
.option('php', {
94+
},
95+
php: {
11896
describe: 'PHP version to use.',
11997
type: 'string',
12098
default: RecommendedPHPVersion,
12199
choices: SupportedPHPVersions,
122-
})
123-
.option('wp', {
100+
},
101+
wp: {
124102
describe: 'WordPress version to use.',
125103
type: 'string',
126104
default: 'latest',
127-
})
105+
},
128106
// @TODO: Support read-only mounts, e.g. via WORKERFS, a custom
129107
// ReadOnlyNODEFS, or by copying the files into MEMFS
130-
.option('mount', {
108+
mount: {
131109
describe:
132110
'Mount a directory to the PHP runtime (can be used multiple times). Format: /host/path:/vfs/path',
133111
type: 'array',
134112
string: true,
135113
coerce: parseMountWithDelimiterArguments,
136-
})
137-
.option('mount-before-install', {
114+
},
115+
'mount-before-install': {
138116
describe:
139117
'Mount a directory to the PHP runtime before WordPress installation (can be used multiple times). Format: /host/path:/vfs/path',
140118
type: 'array',
141119
string: true,
142120
coerce: parseMountWithDelimiterArguments,
143-
})
144-
.option('mount-dir', {
121+
},
122+
'mount-dir': {
145123
describe:
146124
'Mount a directory to the PHP runtime (can be used multiple times). Format: "/host/path" "/vfs/path"',
147125
type: 'array',
148126
nargs: 2,
149127
array: true,
150128
coerce: parseMountDirArguments,
151-
})
152-
.option('mount-dir-before-install', {
129+
},
130+
'mount-dir-before-install': {
153131
describe:
154132
'Mount a directory before WordPress installation (can be used multiple times). Format: "/host/path" "/vfs/path"',
155133
type: 'string',
156134
nargs: 2,
157135
array: true,
158136
coerce: parseMountDirArguments,
159-
})
160-
.option('login', {
137+
},
138+
login: {
161139
describe: 'Should log the user in',
162140
type: 'boolean',
163141
default: false,
164-
})
165-
.option('blueprint', {
142+
},
143+
blueprint: {
166144
describe: 'Blueprint to execute.',
167145
type: 'string',
168-
})
169-
.option('blueprint-may-read-adjacent-files', {
146+
},
147+
'blueprint-may-read-adjacent-files': {
170148
describe:
171149
'Consent flag: Allow "bundled" resources in a local blueprint to read files in the same directory as the blueprint file.',
172150
type: 'boolean',
173151
default: false,
174-
})
175-
.option('wordpress-install-mode', {
152+
},
153+
'wordpress-install-mode': {
176154
describe:
177155
'Control how Playground prepares WordPress before booting.',
178156
type: 'string',
@@ -183,71 +161,71 @@ export async function parseOptionsAndRunCLI(argsToParse: string[]) {
183161
'install-from-existing-files-if-needed',
184162
'do-not-attempt-installing',
185163
] as const,
186-
})
187-
.option('skip-wordpress-install', {
164+
},
165+
'skip-wordpress-install': {
188166
describe: '[Deprecated] Use --wordpress-install-mode instead.',
189167
type: 'boolean',
190168
hidden: true,
191-
})
192-
.option('skip-sqlite-setup', {
169+
},
170+
'skip-sqlite-setup': {
193171
describe:
194172
'Skip the SQLite integration plugin setup to allow the WordPress site to use MySQL.',
195173
type: 'boolean',
196174
default: false,
197-
})
175+
},
198176
// Hidden - Deprecated in favor of verbosity
199-
.option('quiet', {
177+
quiet: {
200178
describe: 'Do not output logs and progress messages.',
201179
type: 'boolean',
202180
default: false,
203181
hidden: true,
204-
})
205-
.option('verbosity', {
182+
},
183+
verbosity: {
206184
describe: 'Output logs and progress messages.',
207185
type: 'string',
208186
choices: Object.values(LogVerbosity).map(
209187
(verbosity) => verbosity.name
210188
),
211189
default: 'normal',
212-
})
213-
.option('debug', {
190+
},
191+
debug: {
214192
describe:
215193
'Print PHP error log content if an error occurs during Playground boot.',
216194
type: 'boolean',
217195
default: false,
218-
})
219-
.option('auto-mount', {
196+
},
197+
'auto-mount': {
220198
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.`,
221199
type: 'string',
222-
})
223-
.option('follow-symlinks', {
200+
},
201+
'follow-symlinks': {
224202
describe:
225203
'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.',
226204
type: 'boolean',
227205
default: false,
228-
})
229-
.option('experimental-trace', {
206+
},
207+
'experimental-trace': {
230208
describe:
231209
'Print detailed messages about system behavior to the console. Useful for troubleshooting.',
232210
type: 'boolean',
233211
default: false,
234212
// Hide this option because we want to replace with a more general log-level flag.
235213
hidden: true,
236-
})
237-
.option('internal-cookie-store', {
214+
},
215+
'internal-cookie-store': {
238216
describe:
239217
'Enable internal cookie handling. When enabled, Playground will manage cookies internally using ' +
240218
'an HttpCookieStore that persists cookies across requests. When disabled, cookies are handled ' +
241219
'externally (e.g., by a browser in Node.js environments).',
242220
type: 'boolean',
243221
default: false,
244-
})
245-
.option('xdebug', {
222+
},
223+
xdebug: {
246224
describe: 'Enable Xdebug.',
247225
type: 'boolean',
248226
default: false,
249-
})
250-
.option('experimental-unsafe-ide-integration', {
227+
},
228+
'experimental-unsafe-ide-integration': {
251229
describe:
252230
'Enable experimental IDE development tools. This option edits IDE config files ' +
253231
'to set Xdebug path mappings and web server details. CAUTION: If there are bugs, ' +
@@ -260,39 +238,85 @@ export async function parseOptionsAndRunCLI(argsToParse: string[]) {
260238
choices: ['', 'vscode', 'phpstorm'],
261239
coerce: (value?: string) =>
262240
value === '' ? ['vscode', 'phpstorm'] : [value],
263-
})
264-
.option('experimental-devtools', {
265-
describe: 'Enable experimental browser development tools.',
266-
type: 'boolean',
267-
})
268-
.conflicts(
269-
'experimental-unsafe-ide-integration',
270-
'experimental-devtools'
271-
)
272-
.option('experimental-multi-worker', {
273-
describe:
274-
'Enable experimental multi-worker support which requires ' +
275-
'a /wordpress directory backed by a real filesystem. ' +
276-
'Pass a positive number to specify the number of workers to use. ' +
277-
'Otherwise, default to the number of CPUs minus 1.',
278-
type: 'number',
279-
coerce: (value?: number) => value ?? cpus().length - 1,
280-
})
281-
.option('experimental-blueprints-v2-runner', {
241+
},
242+
'experimental-blueprints-v2-runner': {
282243
describe: 'Use the experimental Blueprint V2 runner.',
283244
type: 'boolean',
284245
default: false,
285246
// Remove the "hidden" flag once Blueprint V2 is fully supported
286247
hidden: true,
287-
})
288-
.option('mode', {
248+
},
249+
mode: {
289250
describe:
290251
'Blueprints v2 runner mode to use. This option is required when using the --experimental-blueprints-v2-runner flag with a blueprint.',
291252
type: 'string',
292253
choices: ['create-new-site', 'apply-to-existing-site'],
293254
// Remove the "hidden" flag once Blueprint V2 is fully supported
294255
hidden: true,
295-
})
256+
},
257+
};
258+
259+
const serverOnlyOptions: Record<string, YargsOptions> = {
260+
port: {
261+
describe: 'Port to listen on when serving.',
262+
type: 'number',
263+
default: 9400,
264+
},
265+
'experimental-multi-worker': {
266+
describe:
267+
'Enable experimental multi-worker support which requires ' +
268+
'a /wordpress directory backed by a real filesystem. ' +
269+
'Pass a positive number to specify the number of workers to use. ' +
270+
'Otherwise, default to the number of CPUs minus 1.',
271+
type: 'number',
272+
coerce: (value?: number) => value ?? cpus().length - 1,
273+
},
274+
'experimental-devtools': {
275+
describe: 'Enable experimental browser development tools.',
276+
type: 'boolean',
277+
},
278+
};
279+
280+
const buildSnapshotOnlyOptions: Record<string, YargsOptions> = {
281+
outfile: {
282+
describe: 'When building, write to this output file.',
283+
type: 'string',
284+
default: 'wordpress.zip',
285+
},
286+
};
287+
288+
const commandWithOptions =
289+
(commandOptions: Record<string, YargsOptions> = {}) =>
290+
(yargsInstance: Argv) =>
291+
yargsInstance.options({ ...sharedOptions, ...commandOptions });
292+
293+
const yargsObject = yargs(argsToParse)
294+
.usage('Usage: wp-playground <command> [options]')
295+
.command(
296+
'server',
297+
'Start a local WordPress server',
298+
commandWithOptions({
299+
...serverOnlyOptions,
300+
})
301+
)
302+
.command(
303+
'run-blueprint',
304+
'Execute a Blueprint without starting a server',
305+
commandWithOptions()
306+
)
307+
.command(
308+
'build-snapshot',
309+
'Build a ZIP snapshot of a WordPress site based on a Blueprint',
310+
commandWithOptions({
311+
...buildSnapshotOnlyOptions,
312+
})
313+
)
314+
.demandCommand(1, 'Please specify a command')
315+
.strictCommands()
316+
.conflicts(
317+
'experimental-unsafe-ide-integration',
318+
'experimental-devtools'
319+
)
296320
.showHelpOnFail(false)
297321
.fail((msg, err, yargsInstance) => {
298322
if (err) {

0 commit comments

Comments
 (0)