Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/playground/cli/src/mounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@ const ACTIVATE_FIRST_THEME_STEP = {
* Auto-mounts resolution logic:
*/
export function expandAutoMounts(args: RunCLIArgs): RunCLIArgs {
const path = args.autoMount!;
if (typeof args.autoMount !== 'string') {
return args;
}
const path = args.autoMount;

const mount = [...(args.mount || [])];
const mountBeforeInstall = [...(args['mount-before-install'] || [])];
Expand Down
40 changes: 32 additions & 8 deletions packages/playground/cli/src/run-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,11 +433,11 @@ export async function parseOptionsAndRunCLI(argsToParse: string[]) {
type: 'boolean',
default: false,
},
'no-auto-mount': {
'auto-mount': {
describe:
'Disable automatic project type detection. Use --mount to manually specify mounts instead.',
'Automatically detect project type (plugin, theme, wp-content, or WordPress) and mount accordingly. Use --no-auto-mount to disable and --mount to manually specify mounts instead.',
type: 'boolean',
default: false,
default: true,
},
// Define constants
define: sharedOptions['define'],
Expand Down Expand Up @@ -559,7 +559,14 @@ export async function parseOptionsAndRunCLI(argsToParse: string[]) {
}
}

if (args['auto-mount']) {
// For the `start` command, `--auto-mount` is a boolean
// toggle (path is taken from `--path`), so skip the
// directory validation here.
if (
args._[0] !== 'start' &&
typeof args['auto-mount'] === 'string' &&
args['auto-mount']
) {
Comment thread
pento marked this conversation as resolved.
let autoMountIsDir = false;
try {
const autoMountStats = fs.statSync(
Expand Down Expand Up @@ -827,7 +834,13 @@ export interface RunCLIArgs {
quiet?: boolean;
verbosity?: LogVerbosity;
wp?: string;
autoMount?: string;
/**
* For the `server` command (and other long-form commands), this is the
* host path to auto-detect and mount. For the `start` command, this is a
* boolean toggle: `true` (default) enables auto-detection on the
* `--path` directory; `false` (i.e. `--no-auto-mount`) disables it.
*/
autoMount?: string | boolean;
pathAliases?: PathAlias[];
experimentalTrace?: boolean;
internalCookieStore?: boolean;
Expand Down Expand Up @@ -878,7 +891,6 @@ export interface RunCLIArgs {
// --------- Start command args -----------
path?: string;
skipBrowser?: boolean;
noAutoMount?: boolean;
reset?: boolean;
}

Expand Down Expand Up @@ -1718,9 +1730,21 @@ function expandStartCommandArgs(
let newArgs = { ...args, command: 'server' };
Comment thread
pento marked this conversation as resolved.

/**
* Enable auto-mount unless explicitly disabled
* Enable auto-mount unless explicitly disabled via `--no-auto-mount`.
*
* yargs-parser's boolean-negation turns `--no-auto-mount` into
* `{ autoMount: false }`, so `args.autoMount === false` is how we detect
* the disabled case. The boolean form is start-command only — downstream
* code treats `autoMount` as a string path, so drop it either way and
* then re-populate it with a resolved path when enabled.
*/
if (!args.noAutoMount) {
const autoMountEnabled = args.autoMount !== false;
// Scrub both the camelCase and dashed forms yargs-parser emits so a
// stale boolean can't leak into downstream consumers that read either.
delete newArgs.autoMount;
Comment thread
pento marked this conversation as resolved.
delete (newArgs as Record<string, unknown>)['auto-mount'];

if (autoMountEnabled) {
newArgs.autoMount = path.resolve(process.cwd(), newArgs['path'] ?? '');
newArgs = expandAutoMounts(newArgs as RunCLIArgs);
// Delete the autoMount argument to avoid double expansion later on.
Expand Down
44 changes: 44 additions & 0 deletions packages/playground/cli/tests/run-cli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,50 @@ describe('start command', () => {
expect(existsSync(wpContentPath)).toBe(true);
expect(lstatSync(wpContentPath).isDirectory()).toBe(true);
}, 120000);

test('should accept --no-auto-mount and skip auto-detection', async () => {
// Regression test: yargs-parser's boolean-negation turns
// `--no-auto-mount` into `{ autoMount: false }`. When the start
// command declared a literal `no-auto-mount` option (with no
// matching `auto-mount`), strictOptions rejected the negated key
// with `Unknown arguments: auto-mount, autoMount`.
const tmpDir = await mkdtemp(
path.join(tmpdir(), 'playground-test-no-auto-mount-')
);
const pluginDirName = 'sample-plugin';
const pluginDir = path.join(tmpDir, pluginDirName);
mkdirSync(pluginDir, { recursive: true });
Comment thread
pento marked this conversation as resolved.
writeFileSync(
path.join(pluginDir, `${pluginDirName}.php`),
`<?php\n/*\nPlugin Name: Sample Plugin\n*/\n`
);

const exitSpy = vi
.spyOn(process, 'exit')
.mockImplementation((() => {}) as any);
Comment thread
pento marked this conversation as resolved.
Outdated

try {
await using cliResult = await parseOptionsAndRunCLI([
'start',
`--path=${pluginDir}`,
'--no-auto-mount',
'--skip-browser',
]);
const cliServer = cliResult[internalsKeyForTesting].cliServer;

// Server started → yargs accepted `--no-auto-mount`.
expect(cliServer.serverUrl).toMatch(/^http:\/\/127\.0\.0\.1:\d+$/);

// Auto-mount did not fire → the plugin is not present under
// /wordpress/wp-content/plugins/.
const autoMountedPluginExists = await cliServer.playground.isDir(
`/wordpress/wp-content/plugins/${pluginDirName}`
);
expect(autoMountedPluginExists).toBe(false);
} finally {
exitSpy.mockRestore();
}
}, 180000);
});

describe('php command', () => {
Expand Down
Loading