Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
13 changes: 4 additions & 9 deletions packages/playground/cli/src/blueprints-v1/worker-thread-v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ import { loadNodeRuntime } from '@php-wasm/node';
import { EmscriptenDownloadMonitor } from '@php-wasm/progress';
import type { RemoteAPI, SupportedPHPVersion } from '@php-wasm/universal';
import {
PHPWorker,
consumeAPI,
consumeAPISync,
exposeAPI,
sandboxedSpawnHandlerFactory,
} from '@php-wasm/universal';
import { sprintf } from '@php-wasm/util';
import { RecommendedPHPVersion } from '@wp-playground/common';
import { bootWordPress, bootRequestHandler } from '@wp-playground/wordpress';
import { bootRequestHandler, bootWordPress } from '@wp-playground/wordpress';
import { rootCertificates } from 'tls';
import { jspi } from 'wasm-feature-detect';
import { MessageChannel, type MessagePort, parentPort } from 'worker_threads';
import { MessageChannel, parentPort, type MessagePort } from 'worker_threads';
import { PlaygroundCliWorker } from '../playground-cli-worker';
import { mountResources } from '../mounts';

export interface Mount {
Expand Down Expand Up @@ -78,7 +78,7 @@ function tracePhpWasm(processId: number, format: string, ...args: any[]) {
);
}

export class PlaygroundCliBlueprintV1Worker extends PHPWorker {
export class PlaygroundCliBlueprintV1Worker extends PlaygroundCliWorker {
booted = false;
fileLockManager: RemoteAPI<FileLockManager> | FileLockManager | undefined;

Expand Down Expand Up @@ -293,11 +293,6 @@ export class PlaygroundCliBlueprintV1Worker extends PHPWorker {
throw e;
}
}

// Provide a named disposal method that can be invoked via comlink.
async dispose() {
await this[Symbol.asyncDispose]();
}
}

const phpChannel = new MessageChannel();
Expand Down
29 changes: 11 additions & 18 deletions packages/playground/cli/src/blueprints-v2/worker-thread-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,41 +3,39 @@ import type { FileLockManager } from '@php-wasm/node';
import { createNodeFsMountHandler, loadNodeRuntime } from '@php-wasm/node';
import { EmscriptenDownloadMonitor } from '@php-wasm/progress';
import type {
PHP,
FileTree,
PHP,
RemoteAPI,
SupportedPHPVersion,
} from '@php-wasm/universal';
import {
PHPExecutionFailureError,
PHPResponse,
PHPWorker,
consumeAPI,
consumeAPISync,
exposeAPI,
sandboxedSpawnHandlerFactory,
} from '@php-wasm/universal';
import { sprintf } from '@php-wasm/util';
import {
type BlueprintMessage,
runBlueprintV2,
} from '@wp-playground/blueprints';
import {
type ParsedBlueprintV2Declaration,
type BlueprintMessage,
type BlueprintV2Declaration,
type ParsedBlueprintV2Declaration,
} from '@wp-playground/blueprints';
import type {
PHPInstanceCreatedHook,
PhpIniOptions,
} from '@wp-playground/wordpress';
import { bootRequestHandler } from '@wp-playground/wordpress';
import { existsSync } from 'fs';
import path from 'path';
import { rootCertificates } from 'tls';
import { MessageChannel, type MessagePort, parentPort } from 'worker_threads';
import type { Mount } from '../mounts';
import { jspi } from 'wasm-feature-detect';
import { MessageChannel, parentPort, type MessagePort } from 'worker_threads';
import { PlaygroundCliWorker } from '../playground-cli-worker';
import type { Mount } from '../mounts';
import { type RunCLIArgs } from '../run-cli';
import type {
PhpIniOptions,
PHPInstanceCreatedHook,
} from '@wp-playground/wordpress';

async function mountResources(php: PHP, mounts: Mount[]) {
for (const mount of mounts) {
Expand Down Expand Up @@ -154,7 +152,7 @@ export type WorkerBootRequestHandlerOptions = Omit<
onPHPInstanceCreated: PHPInstanceCreatedHook;
};

export class PlaygroundCliBlueprintV2Worker extends PHPWorker {
export class PlaygroundCliBlueprintV2Worker extends PlaygroundCliWorker {
booted = false;
blueprintTargetResolved = false;
phpInstancesThatNeedMountsAfterTargetResolved = new Set<PHP>();
Expand Down Expand Up @@ -470,11 +468,6 @@ export class PlaygroundCliBlueprintV2Worker extends PHPWorker {
throw e;
}
}

// Provide a named disposal method that can be invoked via comlink.
async dispose() {
await this[Symbol.asyncDispose]();
}
}

const phpChannel = new MessageChannel();
Expand Down
5 changes: 1 addition & 4 deletions packages/playground/cli/src/load-balancer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import type { PHPRequest, PHPResponse, RemoteAPI } from '@php-wasm/universal';
import type { PlaygroundCliBlueprintV1Worker as PlaygroundCliWorkerV1 } from './blueprints-v1/worker-thread-v1';
import type { PlaygroundCliBlueprintV2Worker as PlaygroundCliWorkerV2 } from './blueprints-v2/worker-thread-v2';

type PlaygroundCliWorker = PlaygroundCliWorkerV1 | PlaygroundCliWorkerV2;
import type { PlaygroundCliWorker } from './playground-cli-worker';

// TODO: Let's merge worker management into PHPProcessManager
// when we can have multiple workers in both CLI and web.
Expand Down
30 changes: 30 additions & 0 deletions packages/playground/cli/src/playground-cli-worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { PHPWorker } from '@php-wasm/universal';

export class PlaygroundCliWorker extends PHPWorker {
async runCLIScript(
argv: string[],
options: { env?: Record<string, string> } = {}
) {
const streamedResponse = await this.cli(argv, options);
streamedResponse.stdout.pipeTo(
new WritableStream({
write(chunk) {
process.stdout.write(chunk);
},
})
);
streamedResponse.stderr.pipeTo(
new WritableStream({
write(chunk) {
process.stderr.write(chunk);
},
})
);
return await streamedResponse.exitCode;
}

// Provide a named disposal method that can be invoked via comlink.
async dispose() {
await this[Symbol.asyncDispose]();
}
}
55 changes: 42 additions & 13 deletions packages/playground/cli/src/run-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@ import {
parseMountWithDelimiterArguments,
} from './mounts';
import { startServer } from './start-server';
import type {
Mount,
PlaygroundCliBlueprintV1Worker,
} from './blueprints-v1/worker-thread-v1';
import type { PlaygroundCliBlueprintV2Worker } from './blueprints-v2/worker-thread-v2';
import type { Mount } from './blueprints-v1/worker-thread-v1';
import { FileLockManagerForNode } from '@php-wasm/node';
import { LoadBalancer } from './load-balancer';
/* eslint-disable no-console */
Expand All @@ -49,6 +45,7 @@ import {
cleanupStalePlaygroundTempDirs,
createPlaygroundCliTempDir,
} from './temp-dir';
import type { PlaygroundCliWorker } from './playground-cli-worker';

// Inlined worker URLs for static analysis by downstream bundlers
// These are replaced at build time by the Vite plugin in vite.config.ts
Expand All @@ -75,7 +72,12 @@ export async function parseOptionsAndRunCLI() {
.usage('Usage: wp-playground <command> [options]')
.positional('command', {
describe: 'Command to run',
choices: ['server', 'run-blueprint', 'build-snapshot'] as const,
choices: [
'server',
'run-blueprint',
'build-snapshot',
'php',
] as const,
demandOption: true,
})
.option('outfile', {
Expand Down Expand Up @@ -111,12 +113,14 @@ export async function parseOptionsAndRunCLI() {
'Mount a directory to the PHP runtime (can be used multiple times). Format: /host/path:/vfs/path',
type: 'array',
string: true,
nargs: 1,
coerce: parseMountWithDelimiterArguments,
})
.option('mount-before-install', {
describe:
'Mount a directory to the PHP runtime before WordPress installation (can be used multiple times). Format: /host/path:/vfs/path',
type: 'array',
nargs: 1,
string: true,
coerce: parseMountWithDelimiterArguments,
})
Expand Down Expand Up @@ -347,14 +351,24 @@ export async function parseOptionsAndRunCLI() {
}

return true;
})
.command('php', 'Run a PHP script', (yargs) => {
return yargs.positional('argv', {
describe: 'arguments to pass to the PHP CLI',
type: 'string',
});
});

yargsObject.wrap(yargsObject.terminalWidth());
const args = await yargsObject.argv;

const command = args._[0] as string;

if (!['run-blueprint', 'server', 'build-snapshot'].includes(command)) {
if (
!['run-blueprint', 'server', 'build-snapshot', 'php'].includes(
command
)
) {
yargsObject.showHelp();
process.exit(1);
}
Expand Down Expand Up @@ -393,8 +407,13 @@ export async function parseOptionsAndRunCLI() {
}

export interface RunCLIArgs {
/**
* `_` holds positional tokens in the order they appeared.
* `_[0]` will typically be the command name.
*/
_: string[];
blueprint?: BlueprintDeclaration | BlueprintBundle;
command: 'server' | 'run-blueprint' | 'build-snapshot';
command: 'server' | 'run-blueprint' | 'build-snapshot' | 'php';
debug?: boolean;
login?: boolean;
mount?: Mount[];
Expand Down Expand Up @@ -436,10 +455,6 @@ export interface RunCLIArgs {
allow?: string;
}

type PlaygroundCliWorker =
| PlaygroundCliBlueprintV1Worker
| PlaygroundCliBlueprintV2Worker;

export interface RunCLIServer extends AsyncDisposable {
playground: RemoteAPI<PlaygroundCliWorker>;
server: Server;
Expand Down Expand Up @@ -690,6 +705,21 @@ export async function runCLI(args: RunCLIArgs): Promise<RunCLIServer> {
} else if (args.command === 'run-blueprint') {
logger.log(`Blueprint executed`);
process.exit(0);
} else if (args.command === 'php') {
const argv = [
// @TODO: import this from somewhere? Hardcoding it feels fragile.
'/internal/shared/bin/php',
/**
* args._ are all unparsed positionals arguments, e.g.
*/
...((args as any)['_'] || []).slice(1),
];
// @TODO: Call .cli(). Problem: It returns StreamedPHPResponse which
// fails to go through comlink machinery.
const exitCode = await playground.runCLIScript(argv);
// Wait until the next tick before exiting to ensure the output is flushed.
await new Promise((resolve) => setTimeout(resolve, 0));
process.exit(exitCode);
}

if (
Expand Down Expand Up @@ -836,7 +866,6 @@ async function spawnWorkerThreads(
}
});
worker.once('error', function (e: Error) {
console.error(e);
const error = new Error(
`Worker failed to load worker. ${
e.message ? `Original error: ${e.message}` : ''
Expand Down
Loading