@wp-playground/cli 3.0.22 → 3.0.31
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 +80 -45
- package/blueprints-v1/worker-thread-v1.d.ts +6 -6
- package/blueprints-v2/worker-thread-v2.d.ts +10 -5
- package/cli.cjs +1 -1
- package/cli.js +1 -1
- package/index.cjs +1 -1
- package/index.js +6 -4
- package/is-valid-wordpress-slug.d.ts +1 -1
- package/mounts.d.ts +1 -4
- package/package.json +16 -14
- package/resolve-blueprint.d.ts +1 -1
- package/run-cli-D8BNUM1f.cjs +42 -0
- package/run-cli-D8BNUM1f.cjs.map +1 -0
- package/run-cli-pLJHMn5d.js +1234 -0
- package/run-cli-pLJHMn5d.js.map +1 -0
- package/run-cli.d.ts +22 -2
- package/utils/progress.d.ts +3 -0
- package/worker-thread-v1.cjs +2 -2
- package/worker-thread-v1.cjs.map +1 -1
- package/worker-thread-v1.js +123 -117
- package/worker-thread-v1.js.map +1 -1
- package/worker-thread-v2.cjs +12 -8
- package/worker-thread-v2.cjs.map +1 -1
- package/worker-thread-v2.js +150 -79
- package/worker-thread-v2.js.map +1 -1
- package/mounts-ChxECdbN.js +0 -131
- package/mounts-ChxECdbN.js.map +0 -1
- package/mounts-R4uHe-O-.cjs +0 -16
- package/mounts-R4uHe-O-.cjs.map +0 -1
- package/run-cli-BIOpeo2v.cjs +0 -46
- package/run-cli-BIOpeo2v.cjs.map +0 -1
- package/run-cli-LDWC8vQD.js +0 -1391
- package/run-cli-LDWC8vQD.js.map +0 -1
- package/xdebug-path-mappings.d.ts +0 -92
package/README.md
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
# Table of contents
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
7
|
+
- [Requirements](#requirements)
|
|
8
|
+
- [Quickstart](#quickstart)
|
|
9
|
+
- [Usage](#usage)
|
|
10
|
+
- [Working with Blueprints](#working-with-blueprints)
|
|
11
|
+
- [Contributing](#contributing)
|
|
12
12
|
|
|
13
13
|
## Requirements
|
|
14
14
|
|
|
@@ -64,41 +64,41 @@ npx @wp-playground/cli@latest server --mount-before-install=./my-local-site:/wor
|
|
|
64
64
|
|
|
65
65
|
The `--auto-mount` flag is the easiest way to get started. It inspects the current directory and automatically mounts it to the correct location in the virtual WordPress site. These are the supported directory types and how they are detected:
|
|
66
66
|
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
67
|
+
- **Plugin Mode**: Presence of a PHP file with `Plugin Name:` in its header.
|
|
68
|
+
- **Theme Mode**: Presence of a style.css file with `Theme Name:` in its header.
|
|
69
|
+
- **wp-content Mode**: Presence of plugins and themes subdirectories.
|
|
70
|
+
- **WordPress Mode**: Presence of a complete WordPress installation. The directory will be mounted to the root `/wordpress` folder.
|
|
71
71
|
|
|
72
72
|
## Command and Arguments
|
|
73
73
|
|
|
74
74
|
Playground CLI is simple, configurable, and unopinionated. You can set it up according
|
|
75
75
|
to your unique WordPress setup. With the Playground CLI, you can use the following top-level commands:
|
|
76
76
|
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
77
|
+
- **`server`**: (Default) Starts a local WordPress server.
|
|
78
|
+
- **`run-blueprint`**: Executes a Blueprint file without starting a web server.
|
|
79
|
+
- **`build-snapshot`**: Builds a ZIP snapshot of a WordPress site based on a Blueprint.
|
|
80
80
|
|
|
81
81
|
The `server` command supports the following optional arguments:
|
|
82
82
|
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
|
|
98
|
-
-
|
|
99
|
-
-
|
|
100
|
-
-
|
|
101
|
-
-
|
|
83
|
+
- `--port=<port>`: The port number for the server to listen on. Defaults to 9400.
|
|
84
|
+
- `--outfile`: When building, write to this output file.
|
|
85
|
+
- `--wp=<version>`: The version of WordPress to use. Defaults to the latest.
|
|
86
|
+
- `--auto-mount`: Automatically mount the current directory (plugin, theme, wp-content, etc.).
|
|
87
|
+
- `--mount=<mapping>`: Manually mount a directory (can be used multiple times). Format: /host/path:/vfs/path
|
|
88
|
+
- `--mount-before-install`: Mount a directory to the PHP runtime before WordPress installation (can be used multiple times). Format: `"/host/path:/vfs/path"`.
|
|
89
|
+
- `--mount-dir`: Mount a directory to the PHP runtime (can be used multiple times). Format: `"/host/path"` `"/vfs/path"`.
|
|
90
|
+
- `--mount-dir-before-install`: Mount a directory before WordPress installation (can be used multiple times). Format: `"/host/path"` `"/vfs/path"`
|
|
91
|
+
- `--blueprint=<path>`: The path to a JSON Blueprint file to execute.
|
|
92
|
+
- `--blueprint-may-read-adjacent-files`: Consent flag: Allow "bundled" resources in a local blueprint to read files in the same directory as the blueprint file.
|
|
93
|
+
- `--login`: Automatically log the user in as an administrator.
|
|
94
|
+
- `--wordpress-install-mode <mode>`: Control how Playground prepares WordPress before booting. Defaults to `download-and-install`. Other options: `install-from-existing-files` (install using files you've mounted), `install-from-existing-files-if-needed` (same, but skip setup when an existing site is detected), and `do-not-attempt-installing` (never download or install WordPress).
|
|
95
|
+
- `--skip-sqlite-setup`: Do not set up the SQLite database integration.
|
|
96
|
+
- `--verbosity`: Output logs and progress messages (choices: "quiet", "normal", "debug"). Defaults to "normal".
|
|
97
|
+
|
|
98
|
+
- `--debug`: Print the PHP error log if an error occurs during boot.
|
|
99
|
+
- `--follow-symlinks`: Allow Playground to follow symlinks by automatically mounting symlinked directories and files encountered in mounted directories. ⚠️ Warning: Following symlinks will expose files outside mounted directories to Playground and could be a security risk.
|
|
100
|
+
- `--experimental-multi-worker`: Enables experimental multi-worker support. It needs JSPI and a /wordpress directory on a real filesystem. You can pass a positive number to use a specific number of workers, otherwise, it defaults to the number of CPUs minus one.
|
|
101
|
+
- `--internal-cookie-store`: Enables Playground's internal cookie handling. When active, Playground uses an HttpCookieStore to manage and persist cookies across requests. If disabled, cookies are handled externally, like by a browser in Node.js.
|
|
102
102
|
|
|
103
103
|
## Need some help with the CLI?
|
|
104
104
|
|
|
@@ -165,26 +165,61 @@ cliServer = await runCLI({
|
|
|
165
165
|
|
|
166
166
|
### Things the Playground does compared to Laravel Valet
|
|
167
167
|
|
|
168
|
-
-
|
|
169
|
-
-
|
|
170
|
-
-
|
|
168
|
+
- Handles the entire WordPress installation for you.
|
|
169
|
+
- Works across all desktop platforms (Mac, Linux, Windows).
|
|
170
|
+
- Does not set up custom host domains for you.
|
|
171
171
|
|
|
172
172
|
### Things the Playground does compared to `wp-env`
|
|
173
173
|
|
|
174
|
-
-
|
|
175
|
-
-
|
|
176
|
-
-
|
|
174
|
+
- Does not require Docker.
|
|
175
|
+
- Is faster to start up for quick tests and development.
|
|
176
|
+
- The Playground doesn't come with a MySQL Server, but you can provide your own MySQL credentials.
|
|
177
177
|
|
|
178
|
-
##
|
|
178
|
+
## Contributing
|
|
179
|
+
|
|
180
|
+
### Running Playground CLI from source
|
|
181
|
+
|
|
182
|
+
To set it up:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# If you don't have the repository cloned yet:
|
|
186
|
+
git clone -b trunk --single-branch --depth 1 --recurse-submodules https://github.com/WordPress/wordpress-playground.git
|
|
187
|
+
cd wordpress-playground
|
|
188
|
+
|
|
189
|
+
# Alternatively, if you already have a clone but forgot to
|
|
190
|
+
# pull the submodules, you can run:
|
|
191
|
+
cd wordpress-playground
|
|
192
|
+
git submodule update --init --recursive
|
|
193
|
+
|
|
194
|
+
nvm use 23
|
|
195
|
+
npm install
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
To run it:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
node --experimental-strip-types --experimental-transform-types --import ./packages/meta/src/node-es-module-loader/register.mts ./packages/playground/cli/src/cli.ts
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Or this instead of the above:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
# Make sure you have the `nx` command available:
|
|
208
|
+
npm install -g nx
|
|
209
|
+
|
|
210
|
+
nx dev playground-cli server
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### How can I contribute?
|
|
179
214
|
|
|
180
215
|
WordPress Playground CLI is an open-source project and welcomes all contributors from documentation to triage. If the feature you need is missing, you are more than welcome to start a discussion, open an issue, and even propose a Pull Request to implement it.
|
|
181
216
|
|
|
182
217
|
Here are a few quick-start guides to get you started:
|
|
183
218
|
|
|
184
|
-
-
|
|
185
|
-
-
|
|
186
|
-
-
|
|
187
|
-
-
|
|
188
|
-
-
|
|
189
|
-
-
|
|
190
|
-
-
|
|
219
|
+
- Code contributions – see the [developer section](https://wordpress.github.io/wordpress-playground/docs/contributing/code).
|
|
220
|
+
- Documentation – see the [documentation section](https://wordpress.github.io/wordpress-playground/docs/contributing/documentation).
|
|
221
|
+
- Triage – see the [triage section](https://wordpress.github.io/wordpress-playground/contributing/#triaging-issues).
|
|
222
|
+
- Contributions to translations – see the [translations section](https://wordpress.github.io/wordpress-playground/contributing/translations).
|
|
223
|
+
- Reporting bugs – open an [issue](https://github.com/WordPress/wordpress-playground/issues/new) in the repository.
|
|
224
|
+
- Ideas, designs, or anything else – open a [GitHub discussion](https://github.com/WordPress/wordpress-playground/discussions) and let's talk!
|
|
225
|
+
- Join our Slack channel [#playground](https://wordpress.slack.com/archives/C04EWKGDJ0K) at Make WordPress
|
|
@@ -5,10 +5,7 @@ import type { RemoteAPI, SupportedPHPVersion } from '@php-wasm/universal';
|
|
|
5
5
|
import { PHPWorker } from '@php-wasm/universal';
|
|
6
6
|
import { type WordPressInstallMode } from '@wp-playground/wordpress';
|
|
7
7
|
import { type MessagePort } from 'worker_threads';
|
|
8
|
-
|
|
9
|
-
hostPath: string;
|
|
10
|
-
vfsPath: string;
|
|
11
|
-
}
|
|
8
|
+
import type { Mount } from '@php-wasm/cli-util';
|
|
12
9
|
export type WorkerBootOptions = {
|
|
13
10
|
phpVersion: SupportedPHPVersion;
|
|
14
11
|
siteUrl: string;
|
|
@@ -26,6 +23,7 @@ export type WorkerBootOptions = {
|
|
|
26
23
|
* Default: false.
|
|
27
24
|
*/
|
|
28
25
|
internalCookieStore?: boolean;
|
|
26
|
+
withIntl?: boolean;
|
|
29
27
|
withXdebug?: boolean;
|
|
30
28
|
nativeInternalDirPath: string;
|
|
31
29
|
};
|
|
@@ -46,6 +44,7 @@ interface WorkerBootRequestHandlerOptions {
|
|
|
46
44
|
nativeInternalDirPath: string;
|
|
47
45
|
mountsBeforeWpInstall: Array<Mount>;
|
|
48
46
|
mountsAfterWpInstall: Array<Mount>;
|
|
47
|
+
withIntl?: boolean;
|
|
49
48
|
withXdebug?: boolean;
|
|
50
49
|
}
|
|
51
50
|
export declare class PlaygroundCliBlueprintV1Worker extends PHPWorker {
|
|
@@ -62,9 +61,10 @@ export declare class PlaygroundCliBlueprintV1Worker extends PHPWorker {
|
|
|
62
61
|
* @see phpwasm-emscripten-library-file-locking-for-node.js
|
|
63
62
|
*/
|
|
64
63
|
useFileLockManager(port: MessagePort): Promise<void>;
|
|
65
|
-
bootAndSetUpInitialWorker(
|
|
64
|
+
bootAndSetUpInitialWorker(options: PrimaryWorkerBootOptions): Promise<void>;
|
|
65
|
+
hello(): Promise<string>;
|
|
66
66
|
bootWorker(args: WorkerBootOptions): Promise<void>;
|
|
67
|
-
bootRequestHandler(
|
|
67
|
+
bootRequestHandler(options: WorkerBootRequestHandlerOptions): Promise<void>;
|
|
68
68
|
dispose(): Promise<void>;
|
|
69
69
|
}
|
|
70
70
|
export {};
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import type { FileLockManager } from '@php-wasm/node';
|
|
3
3
|
import { EmscriptenDownloadMonitor } from '@php-wasm/progress';
|
|
4
|
-
import type { PHP, FileTree, RemoteAPI, SupportedPHPVersion } from '@php-wasm/universal';
|
|
4
|
+
import type { PHP, FileTree, RemoteAPI, SupportedPHPVersion, SpawnHandler } from '@php-wasm/universal';
|
|
5
5
|
import { PHPWorker } from '@php-wasm/universal';
|
|
6
6
|
import { type BlueprintV1Declaration } from '@wp-playground/blueprints';
|
|
7
7
|
import { type ParsedBlueprintV2String, type RawBlueprintV2Data } from '@wp-playground/blueprints';
|
|
8
8
|
import { type MessagePort } from 'worker_threads';
|
|
9
|
-
import type { Mount } from '../mounts';
|
|
10
9
|
import { type RunCLIArgs } from '../run-cli';
|
|
11
10
|
import type { PhpIniOptions, PHPInstanceCreatedHook } from '@wp-playground/wordpress';
|
|
12
|
-
|
|
11
|
+
import type { Mount } from '@php-wasm/cli-util';
|
|
12
|
+
export type PrimaryWorkerBootArgs = Omit<RunCLIArgs, 'mount-before-install' | 'mount'> & {
|
|
13
13
|
phpVersion: SupportedPHPVersion;
|
|
14
14
|
siteUrl: string;
|
|
15
15
|
firstProcessId: number;
|
|
@@ -17,10 +17,13 @@ export type PrimaryWorkerBootArgs = RunCLIArgs & {
|
|
|
17
17
|
trace: boolean;
|
|
18
18
|
blueprint: RawBlueprintV2Data | ParsedBlueprintV2String | BlueprintV1Declaration;
|
|
19
19
|
nativeInternalDirPath: string;
|
|
20
|
+
mountsBeforeWpInstall?: Array<Mount>;
|
|
21
|
+
mountsAfterWpInstall?: Array<Mount>;
|
|
20
22
|
};
|
|
21
|
-
type WorkerRunBlueprintArgs = RunCLIArgs & {
|
|
23
|
+
type WorkerRunBlueprintArgs = Omit<RunCLIArgs, 'mount-before-install' | 'mount'> & {
|
|
22
24
|
siteUrl: string;
|
|
23
25
|
blueprint: RawBlueprintV2Data | ParsedBlueprintV2String | BlueprintV1Declaration;
|
|
26
|
+
mountsAfterWpInstall?: Array<Mount>;
|
|
24
27
|
};
|
|
25
28
|
export type SecondaryWorkerBootArgs = {
|
|
26
29
|
siteUrl: string;
|
|
@@ -33,12 +36,14 @@ export type SecondaryWorkerBootArgs = {
|
|
|
33
36
|
processIdSpaceLength: number;
|
|
34
37
|
trace: boolean;
|
|
35
38
|
nativeInternalDirPath: string;
|
|
39
|
+
withIntl?: boolean;
|
|
36
40
|
withXdebug?: boolean;
|
|
37
41
|
mountsBeforeWpInstall?: Array<Mount>;
|
|
38
42
|
mountsAfterWpInstall?: Array<Mount>;
|
|
39
43
|
};
|
|
40
44
|
export type WorkerBootRequestHandlerOptions = Omit<SecondaryWorkerBootArgs, 'mountsBeforeWpInstall' | 'mountsAfterWpInstall'> & {
|
|
41
45
|
onPHPInstanceCreated: PHPInstanceCreatedHook;
|
|
46
|
+
spawnHandler: () => SpawnHandler;
|
|
42
47
|
};
|
|
43
48
|
export declare class PlaygroundCliBlueprintV2Worker extends PHPWorker {
|
|
44
49
|
booted: boolean;
|
|
@@ -59,7 +64,7 @@ export declare class PlaygroundCliBlueprintV2Worker extends PHPWorker {
|
|
|
59
64
|
bootAndSetUpInitialWorker(args: PrimaryWorkerBootArgs): Promise<void>;
|
|
60
65
|
bootWorker(args: SecondaryWorkerBootArgs): Promise<void>;
|
|
61
66
|
runBlueprintV2(args: WorkerRunBlueprintArgs): Promise<void>;
|
|
62
|
-
bootRequestHandler({ siteUrl, allow, phpVersion, createFiles, constants, phpIniEntries, firstProcessId, processIdSpaceLength, trace, nativeInternalDirPath, withXdebug, onPHPInstanceCreated, }: WorkerBootRequestHandlerOptions): Promise<void>;
|
|
67
|
+
bootRequestHandler({ siteUrl, allow, phpVersion, createFiles, constants, phpIniEntries, firstProcessId, processIdSpaceLength, trace, nativeInternalDirPath, withIntl, withXdebug, onPHPInstanceCreated, spawnHandler, }: WorkerBootRequestHandlerOptions): Promise<void>;
|
|
63
68
|
dispose(): Promise<void>;
|
|
64
69
|
}
|
|
65
70
|
export {};
|
package/cli.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";const s=require("./run-cli-
|
|
1
|
+
"use strict";const s=require("./run-cli-D8BNUM1f.cjs"),r=process.argv.slice(2);s.parseOptionsAndRunCLI(r);
|
|
2
2
|
//# sourceMappingURL=cli.cjs.map
|
package/cli.js
CHANGED
package/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./run-cli-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./run-cli-D8BNUM1f.cjs");exports.LogVerbosity=e.LogVerbosity;exports.internalsKeyForTesting=e.internalsKeyForTesting;exports.parseOptionsAndRunCLI=e.parseOptionsAndRunCLI;exports.runCLI=e.runCLI;exports.spawnWorkerThread=e.spawnWorkerThread;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { L as
|
|
1
|
+
import { L as a, i as e, p as n, r as o, s as i } from "./run-cli-pLJHMn5d.js";
|
|
2
2
|
export {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
n as
|
|
3
|
+
a as LogVerbosity,
|
|
4
|
+
e as internalsKeyForTesting,
|
|
5
|
+
n as parseOptionsAndRunCLI,
|
|
6
|
+
o as runCLI,
|
|
7
|
+
i as spawnWorkerThread
|
|
6
8
|
};
|
|
7
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* The version string can be one of the following formats:
|
|
6
6
|
* - "latest"
|
|
7
7
|
* - "trunk"
|
|
8
|
-
* - "nightly"
|
|
8
|
+
* - "trunk" (legacy alias: "nightly")
|
|
9
9
|
* - "x.y" (x and y are integers) e.g. "6.2"
|
|
10
10
|
* - "x.y.z" (x, y and z are integers) e.g. "6.2.1"
|
|
11
11
|
* - "x.y.z-betaN" (N is an integer) e.g. "6.2.1-beta1"
|
package/mounts.d.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import type { PHP } from '@php-wasm/universal';
|
|
2
2
|
import type { RunCLIArgs } from './run-cli';
|
|
3
|
-
|
|
4
|
-
hostPath: string;
|
|
5
|
-
vfsPath: string;
|
|
6
|
-
}
|
|
3
|
+
import type { Mount } from '@php-wasm/cli-util';
|
|
7
4
|
/**
|
|
8
5
|
* Parse an array of mount argument strings where the host path and VFS path
|
|
9
6
|
* are separated by a colon.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wp-playground/cli",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.31",
|
|
4
4
|
"description": "WordPress Playground CLI",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"bin": {
|
|
35
35
|
"wp-playground-cli": "wp-playground.js"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "e28b6d2a2e663abc3cbb8fce9a3c5cc8abaa5bbe",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@zip.js/zip.js": "2.7.57",
|
|
40
40
|
"ajv": "8.12.0",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"pify": "2.3.0",
|
|
55
55
|
"ps-man": "1.1.8",
|
|
56
56
|
"readable-stream": "3.6.2",
|
|
57
|
+
"selfsigned": "2.4.1",
|
|
57
58
|
"sha.js": "2.4.12",
|
|
58
59
|
"simple-get": "4.0.1",
|
|
59
60
|
"tmp-promise": "3.0.3",
|
|
@@ -61,16 +62,17 @@
|
|
|
61
62
|
"ws": "8.18.3",
|
|
62
63
|
"xml2js": "0.6.2",
|
|
63
64
|
"yargs": "17.7.2",
|
|
64
|
-
"@php-wasm/logger": "3.0.
|
|
65
|
-
"@php-wasm/progress": "3.0.
|
|
66
|
-
"@php-wasm/universal": "3.0.
|
|
67
|
-
"@wp-playground/blueprints": "3.0.
|
|
68
|
-
"@wp-playground/common": "3.0.
|
|
69
|
-
"@wp-playground/wordpress": "3.0.
|
|
70
|
-
"@php-wasm/node": "3.0.
|
|
71
|
-
"@php-wasm/util": "3.0.
|
|
72
|
-
"@
|
|
73
|
-
"@
|
|
65
|
+
"@php-wasm/logger": "3.0.31",
|
|
66
|
+
"@php-wasm/progress": "3.0.31",
|
|
67
|
+
"@php-wasm/universal": "3.0.31",
|
|
68
|
+
"@wp-playground/blueprints": "3.0.31",
|
|
69
|
+
"@wp-playground/common": "3.0.31",
|
|
70
|
+
"@wp-playground/wordpress": "3.0.31",
|
|
71
|
+
"@php-wasm/node": "3.0.31",
|
|
72
|
+
"@php-wasm/util": "3.0.31",
|
|
73
|
+
"@php-wasm/cli-util": "3.0.31",
|
|
74
|
+
"@wp-playground/storage": "3.0.31",
|
|
75
|
+
"@php-wasm/xdebug-bridge": "3.0.31"
|
|
74
76
|
},
|
|
75
77
|
"packageManager": "npm@10.9.2",
|
|
76
78
|
"overrides": {
|
|
@@ -78,8 +80,8 @@
|
|
|
78
80
|
"react": "18.3.1",
|
|
79
81
|
"react-dom": "18.3.1",
|
|
80
82
|
"typescript": "5.4.5",
|
|
81
|
-
"@playwright/test": "1.
|
|
82
|
-
"ws": "
|
|
83
|
+
"@playwright/test": "1.55.1",
|
|
84
|
+
"ws": "8.18.3",
|
|
83
85
|
"tmp": "0.2.5",
|
|
84
86
|
"form-data": "^4.0.4"
|
|
85
87
|
},
|
package/resolve-blueprint.d.ts
CHANGED
|
@@ -13,5 +13,5 @@ type ResolveBlueprintOptions = {
|
|
|
13
13
|
* @param blueprintMayReadAdjacentFiles - Whether the blueprint may read adjacent files.
|
|
14
14
|
* @returns The resolved blueprint.
|
|
15
15
|
*/
|
|
16
|
-
export declare function resolveBlueprint({ sourceString, blueprintMayReadAdjacentFiles, }: ResolveBlueprintOptions): Promise<import("@wp-playground/storage").
|
|
16
|
+
export declare function resolveBlueprint({ sourceString, blueprintMayReadAdjacentFiles, }: ResolveBlueprintOptions): Promise<import("@wp-playground/storage").ReadableFilesystemBackend | undefined>;
|
|
17
17
|
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";var we=Object.create;var J=Object.defineProperty;var ye=Object.getOwnPropertyDescriptor;var be=Object.getOwnPropertyNames;var Pe=Object.getPrototypeOf,ve=Object.prototype.hasOwnProperty;var ke=(e,t,o,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of be(t))!ve.call(e,r)&&r!==o&&J(e,r,{get:()=>t[r],enumerable:!(s=ye(t,r))||s.enumerable});return e};var xe=(e,t,o)=>(o=e!=null?we(Pe(e)):{},ke(t||!e||!e.__esModule?J(o,"default",{value:e,enumerable:!0}):o,e));const u=require("@php-wasm/logger"),b=require("@php-wasm/universal"),L=require("@wp-playground/blueprints"),j=require("@wp-playground/common"),d=require("fs"),z=require("worker_threads"),ae=require("@php-wasm/node"),p=require("path"),Se=require("express"),Z=require("os"),Ie=require("wasm-feature-detect"),Te=require("yargs"),D=require("@wp-playground/storage"),ee=require("@php-wasm/progress"),Ce=require("@wp-playground/wordpress"),W=require("fs-extra"),$e=require("@php-wasm/xdebug-bridge"),te=require("tmp-promise"),Re=require("ps-man"),A=require("@php-wasm/cli-util");var R=typeof document<"u"?document.currentScript:null;function oe(e){const t=[];for(const o of e){const s=o.split(":");if(s.length!==2)throw new Error(`Invalid mount format: ${o}.
|
|
2
|
+
Expected format: /host/path:/vfs/path.
|
|
3
|
+
If your path contains a colon, e.g. C:\\myplugin, use the --mount-dir option instead.
|
|
4
|
+
Example: --mount-dir C:\\my-plugin /wordpress/wp-content/plugins/my-plugin`);const[r,n]=s;if(!d.existsSync(r))throw new Error(`Host path does not exist: ${r}`);t.push({hostPath:r,vfsPath:n})}return t}function re(e){if(e.length%2!==0)throw new Error("Invalid mount format. Expected: /host/path /vfs/path");const t=[];for(let o=0;o<e.length;o+=2){const s=e[o],r=e[o+1];if(!d.existsSync(s))throw new Error(`Host path does not exist: ${s}`);t.push({hostPath:p.resolve(process.cwd(),s),vfsPath:r})}return t}async function Ee(e,t){for(const o of t)await e.mount(o.vfsPath,ae.createNodeFsMountHandler(o.hostPath))}const ne={step:"runPHP",code:{filename:"activate-theme.php",content:`<?php
|
|
5
|
+
$docroot = getenv('DOCROOT') ? getenv('DOCROOT') : '/wordpress';
|
|
6
|
+
require_once "$docroot/wp-load.php";
|
|
7
|
+
$theme = wp_get_theme();
|
|
8
|
+
if (!$theme->exists()) {
|
|
9
|
+
$themes = wp_get_themes();
|
|
10
|
+
if (count($themes) > 0) {
|
|
11
|
+
$themeName = array_keys($themes)[0];
|
|
12
|
+
switch_theme($themeName);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
`}};function Le(e){const t=e.autoMount,o=[...e.mount||[]],s=[...e["mount-before-install"]||[]],r={...e,mount:o,"mount-before-install":s,"additional-blueprint-steps":[...e["additional-blueprint-steps"]||[]]};if(Ue(t)){const n=p.basename(t);o.push({hostPath:t,vfsPath:`/wordpress/wp-content/plugins/${n}`}),r["additional-blueprint-steps"].push({step:"activatePlugin",pluginPath:`/wordpress/wp-content/plugins/${p.basename(t)}`})}else if(Me(t)){const n=p.basename(t);o.push({hostPath:t,vfsPath:`/wordpress/wp-content/themes/${n}`}),r["additional-blueprint-steps"].push(e["experimental-blueprints-v2-runner"]?{step:"activateTheme",themeDirectoryName:n}:{step:"activateTheme",themeFolderName:n})}else if(Be(t)){const n=d.readdirSync(t);for(const a of n)a!=="index.php"&&o.push({hostPath:`${t}/${a}`,vfsPath:`/wordpress/wp-content/${a}`});r["additional-blueprint-steps"].push(ne)}else We(t)?(s.push({hostPath:t,vfsPath:"/wordpress"}),r.mode="apply-to-existing-site",r["additional-blueprint-steps"].push(ne),r.wordpressInstallMode||(r.wordpressInstallMode="install-from-existing-files-if-needed")):(o.push({hostPath:t,vfsPath:"/wordpress"}),r.mode="mount-only");return r}function We(e){const t=d.readdirSync(e);return t.includes("wp-admin")&&t.includes("wp-includes")&&t.includes("wp-content")}function Be(e){const t=d.readdirSync(e);return t.includes("themes")||t.includes("plugins")||t.includes("mu-plugins")||t.includes("uploads")}function Me(e){if(!d.readdirSync(e).includes("style.css"))return!1;const o=d.readFileSync(p.join(e,"style.css"),"utf8");return!!/^(?:[ \t]*<\?php)?[ \t/*#@]*Theme Name:(.*)$/im.exec(o)}function Ue(e){const t=d.readdirSync(e),o=/^(?:[ \t]*<\?php)?[ \t/*#@]*Plugin Name:(.*)$/im;return!!t.filter(r=>r.endsWith(".php")).find(r=>{const n=d.readFileSync(p.join(e,r),"utf8");return!!o.exec(n)})}async function Fe(e){const t=Se(),o=await new Promise((n,a)=>{const l=t.listen(e.port,()=>{const c=l.address();c===null||typeof c=="string"?a(new Error("Server address is not available")):n(l)})});t.use("/",async(n,a)=>{let l;try{l=await e.handleRequest({url:n.url,headers:Ae(n),method:n.method,body:await De(n)})}catch(c){u.logger.error(c),l=b.PHPResponse.forHttpCode(500)}a.statusCode=l.httpStatusCode;for(const c in l.headers)a.setHeader(c,l.headers[c]);a.end(l.bytes)});const r=o.address().port;return await e.onBind(o,r)}const De=async e=>await new Promise(t=>{const o=[];e.on("data",s=>{o.push(s)}),e.on("end",()=>{t(new Uint8Array(Buffer.concat(o)))})}),Ae=e=>{const t={};if(e.rawHeaders&&e.rawHeaders.length)for(let o=0;o<e.rawHeaders.length;o+=2)t[e.rawHeaders[o].toLowerCase()]=e.rawHeaders[o+1];return t};class qe{constructor(t){this.workerLoads=[],this.addWorker(t)}addWorker(t){this.workerLoads.push({worker:t,activeRequests:new Set})}async removeWorker(t){const o=this.workerLoads.findIndex(r=>r.worker===t);if(o===-1)return;const[s]=this.workerLoads.splice(o,1);await Promise.allSettled(s.activeRequests)}async handleRequest(t){let o=this.workerLoads[0];for(let r=1;r<this.workerLoads.length;r++){const n=this.workerLoads[r];n.activeRequests.size<o.activeRequests.size&&(o=n)}const s=o.worker.request(t);return o.activeRequests.add(s),s.url=t.url,s.finally(()=>{o.activeRequests.delete(s)})}}function He(e){return/^latest$|^trunk$|^nightly$|^(?:(\d+)\.(\d+)(?:\.(\d+))?)((?:-beta(?:\d+)?)|(?:-RC(?:\d+)?))?$/.test(e)}async function _e({sourceString:e,blueprintMayReadAdjacentFiles:t}){if(!e)return;if(e.startsWith("http://")||e.startsWith("https://"))return await L.resolveRemoteBlueprint(e);let o=p.resolve(process.cwd(),e);if(!d.existsSync(o))throw new Error(`Blueprint file does not exist: ${o}`);const s=d.statSync(o);if(s.isDirectory()&&(o=p.join(o,"blueprint.json")),!s.isFile()&&s.isSymbolicLink())throw new Error(`Blueprint path is neither a file nor a directory: ${o}`);const r=p.extname(o);switch(r){case".zip":return D.ZipFilesystem.fromArrayBuffer(d.readFileSync(o).buffer);case".json":{const n=d.readFileSync(o,"utf-8");try{JSON.parse(n)}catch{throw new Error(`Blueprint file at ${o} is not a valid JSON file`)}const a=p.dirname(o),l=new D.NodeJsFilesystem(a);return new D.OverlayFilesystem([new D.InMemoryFilesystem({"blueprint.json":n}),{read(c){if(!t)throw new Error(`Error: Blueprint contained tried to read a local file at path "${c}" (via a resource of type "bundled"). Playground restricts access to local resources by default as a security measure.
|
|
16
|
+
|
|
17
|
+
You can allow this Blueprint to read files from the same parent directory by explicitly adding the --blueprint-may-read-adjacent-files option to your command.`);return l.read(c)}}])}default:throw new Error(`Unsupported blueprint file extension: ${r}. Only .zip and .json files are supported.`)}}function Y(e){return process.env.CI==="true"||process.env.CI==="1"||process.env.GITHUB_ACTIONS==="true"||process.env.GITHUB_ACTIONS==="1"||(process.env.TERM||"").toLowerCase()==="dumb"?!1:e?!!e.isTTY:process.stdout.isTTY}class Ve{constructor(t,o){this.lastProgressMessage="",this.args=t,this.siteUrl=o.siteUrl,this.processIdSpaceLength=o.processIdSpaceLength,this.phpVersion=t.php}getWorkerType(){return"v2"}async bootAndSetUpInitialPlayground(t,o,s){const r=b.consumeAPI(t);await r.useFileLockManager(o);const n={...this.args,phpVersion:this.phpVersion,siteUrl:this.siteUrl,firstProcessId:1,processIdSpaceLength:this.processIdSpaceLength,trace:this.args.debug||!1,blueprint:this.args.blueprint,withIntl:this.args.intl,withXdebug:!1,xdebug:void 0,nativeInternalDirPath:s,mountsBeforeWpInstall:this.args["mount-before-install"]||[],mountsAfterWpInstall:this.args.mount||[]};return await r.bootAndSetUpInitialWorker(n),r}async bootPlayground({worker:t,fileLockManagerPort:o,firstProcessId:s,nativeInternalDirPath:r}){const n=b.consumeAPI(t.phpPort);await n.useFileLockManager(o);const a={...this.args,phpVersion:this.phpVersion,siteUrl:this.siteUrl,firstProcessId:s,processIdSpaceLength:this.processIdSpaceLength,trace:this.args.debug||!1,withIntl:this.args.intl,withXdebug:!!this.args.xdebug,nativeInternalDirPath:r,mountsBeforeWpInstall:this.args["mount-before-install"]||[],mountsAfterWpInstall:this.args.mount||[]};return await n.bootWorker(a),n}writeProgressUpdate(t,o,s){Y(t)&&o!==this.lastProgressMessage&&(this.lastProgressMessage=o,t.isTTY?(t.cursorTo(0),t.write(o),t.clearLine(1),s&&t.write(`
|
|
18
|
+
`)):t.write(`${o}
|
|
19
|
+
`))}}const X=p.join(Z.homedir(),".wordpress-playground");async function Ne(e){return await le("https://github.com/WordPress/sqlite-database-integration/archive/refs/heads/develop.zip","sqlite.zip",e)}async function le(e,t,o){const s=p.join(X,t);return W.existsSync(s)||(W.ensureDirSync(X),await Oe(e,s,o)),ue(s)}async function Oe(e,t,o){const r=(await o.monitorFetch(fetch(e))).body.getReader(),n=`${t}.partial`,a=W.createWriteStream(n);for(;;){const{done:l,value:c}=await r.read();if(c&&a.write(c),l)break}a.close(),a.closed||await new Promise((l,c)=>{a.on("finish",()=>{W.renameSync(n,t),l(null)}),a.on("error",f=>{W.removeSync(n),c(f)})})}function ue(e,t){return new File([W.readFileSync(e)],p.basename(e))}class je{constructor(t,o){this.lastProgressMessage="",this.args=t,this.siteUrl=o.siteUrl,this.processIdSpaceLength=o.processIdSpaceLength}getWorkerType(){return"v1"}async bootAndSetUpInitialPlayground(t,o,s){let r,n,a;const l=new ee.EmscriptenDownloadMonitor;if(this.args.wordpressInstallMode==="download-and-install"){let T=!1;l.addEventListener("progress",G=>{if(T)return;const{loaded:H,total:S}=G.detail,B=Math.floor(Math.min(100,100*H/S));T=B===100,this.writeProgressUpdate(process.stdout,`Downloading WordPress ${B}%...`,T)}),r=await Ce.resolveWordPressRelease(this.args.wp),a=p.join(X,`prebuilt-wp-content-for-wp-${r.version}.zip`),n=d.existsSync(a)?ue(a):await le(r.releaseUrl,`${r.version}.zip`,l),u.logger.log(`Resolved WordPress release URL: ${r?.releaseUrl}`)}let c;this.args.skipSqliteSetup?(u.logger.log("Skipping SQLite integration plugin setup..."),c=void 0):(u.logger.log("Fetching SQLite integration plugin..."),c=await Ne(l));const f=this.args.followSymlinks===!0,i=this.args.experimentalTrace===!0,y=this.args["mount-before-install"]||[],w=this.args.mount||[],v=b.consumeAPI(t);await v.isConnected(),u.logger.log("Booting WordPress...");const x=await L.resolveRuntimeConfiguration(this.getEffectiveBlueprint());return await v.useFileLockManager(o),await v.bootAndSetUpInitialWorker({phpVersion:x.phpVersion,wpVersion:x.wpVersion,siteUrl:this.siteUrl,mountsBeforeWpInstall:y,mountsAfterWpInstall:w,wordpressInstallMode:this.args.wordpressInstallMode||"download-and-install",wordPressZip:n&&await n.arrayBuffer(),sqliteIntegrationPluginZip:await c?.arrayBuffer(),firstProcessId:0,processIdSpaceLength:this.processIdSpaceLength,followSymlinks:f,trace:i,internalCookieStore:this.args.internalCookieStore,withIntl:this.args.intl,withXdebug:!1,nativeInternalDirPath:s}),a&&!this.args["mount-before-install"]&&!d.existsSync(a)&&(u.logger.log("Caching preinstalled WordPress for the next boot..."),d.writeFileSync(a,await j.zipDirectory(v,"/wordpress")),u.logger.log("Cached!")),v}async bootPlayground({worker:t,fileLockManagerPort:o,firstProcessId:s,nativeInternalDirPath:r}){const n=b.consumeAPI(t.phpPort);await n.isConnected();const a=await L.resolveRuntimeConfiguration(this.getEffectiveBlueprint());return await n.useFileLockManager(o),await n.bootWorker({phpVersion:a.phpVersion,siteUrl:this.siteUrl,mountsBeforeWpInstall:this.args["mount-before-install"]||[],mountsAfterWpInstall:this.args.mount||[],firstProcessId:s,processIdSpaceLength:this.processIdSpaceLength,followSymlinks:this.args.followSymlinks===!0,trace:this.args.experimentalTrace===!0,internalCookieStore:this.args.internalCookieStore,withIntl:this.args.intl,withXdebug:!!this.args.xdebug,nativeInternalDirPath:r}),await n.isReady(),n}async compileInputBlueprint(t){const o=this.getEffectiveBlueprint(),s=new ee.ProgressTracker;let r="",n=!1;return s.addEventListener("progress",a=>{if(n)return;n=a.detail.progress===100;const l=Math.floor(a.detail.progress);r=a.detail.caption||r||"Running the Blueprint";const c=`${r.trim()} – ${l}%`;this.writeProgressUpdate(process.stdout,c,n)}),await L.compileBlueprintV1(o,{progress:s,additionalSteps:t})}getEffectiveBlueprint(){const t=this.args.blueprint;return L.isBlueprintBundle(t)?t:{login:this.args.login,...t||{},preferredVersions:{php:this.args.php??t?.preferredVersions?.php??j.RecommendedPHPVersion,wp:this.args.wp??t?.preferredVersions?.wp??"latest",...t?.preferredVersions||{}}}}writeProgressUpdate(t,o,s){this.args.verbosity!==q.Quiet.name&&Y(t)&&o!==this.lastProgressMessage&&(this.lastProgressMessage=o,t.isTTY?(t.cursorTo(0),t.write(o),t.clearLine(1),s&&t.write(`
|
|
20
|
+
`)):t.write(`${o}
|
|
21
|
+
`))}}async function ze(e,t=!0){const s=`${p.basename(process.argv0)}${e}${process.pid}-`,r=await te.dir({prefix:s,unsafeCleanup:!0});return t&&te.setGracefulCleanup(),r}async function Xe(e,t,o){const r=(await Ze(e,t,o)).map(n=>new Promise(a=>{d.rm(n,{recursive:!0},l=>{l?u.logger.warn(`Failed to delete stale Playground temp dir: ${n}`,l):u.logger.info(`Deleted stale Playground temp dir: ${n}`),a()})}));await Promise.all(r)}async function Ze(e,t,o){try{const s=d.readdirSync(o).map(n=>p.join(o,n)),r=[];for(const n of s)await Ye(e,t,n)&&r.push(n);return r}catch(s){return u.logger.warn(`Failed to find stale Playground temp dirs: ${s}`),[]}}async function Ye(e,t,o){if(!d.lstatSync(o).isDirectory())return!1;const r=p.basename(o);if(!r.includes(e))return!1;const n=r.match(new RegExp(`^(.+)${e}(\\d+)-`));if(!n)return!1;const a={executableName:n[1],pid:n[2]};if(await Ge(a.pid,a.executableName))return!1;const l=Date.now()-t;return d.statSync(o).mtime.getTime()<l}async function Ge(e,t){const[o]=await new Promise((s,r)=>{Re.list({pid:e,name:t,clean:!0},(n,a)=>{n?r(n):s(a)})});return!!o&&o.pid===e&&o.command===t}const q={Quiet:{name:"quiet",severity:u.LogSeverity.Fatal},Normal:{name:"normal",severity:u.LogSeverity.Info},Debug:{name:"debug",severity:u.LogSeverity.Debug}};async function Ke(e){try{const t={"site-url":{describe:"Site URL to use for WordPress. Defaults to http://127.0.0.1:{port}",type:"string"},php:{describe:"PHP version to use.",type:"string",default:j.RecommendedPHPVersion,choices:b.SupportedPHPVersions},wp:{describe:"WordPress version to use.",type:"string",default:"latest"},mount:{describe:"Mount a directory to the PHP runtime (can be used multiple times). Format: /host/path:/vfs/path",type:"array",string:!0,coerce:oe},"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",string:!0,coerce:oe},"mount-dir":{describe:'Mount a directory to the PHP runtime (can be used multiple times). Format: "/host/path" "/vfs/path"',type:"array",nargs:2,array:!0,coerce:re},"mount-dir-before-install":{describe:'Mount a directory before WordPress installation (can be used multiple times). Format: "/host/path" "/vfs/path"',type:"string",nargs:2,array:!0,coerce:re},login:{describe:"Should log the user in",type:"boolean",default:!1},blueprint:{describe:"Blueprint to execute.",type:"string"},"blueprint-may-read-adjacent-files":{describe:'Consent flag: Allow "bundled" resources in a local blueprint to read files in the same directory as the blueprint file.',type:"boolean",default:!1},"wordpress-install-mode":{describe:"Control how Playground prepares WordPress before booting.",type:"string",default:"download-and-install",choices:["download-and-install","install-from-existing-files","install-from-existing-files-if-needed","do-not-attempt-installing"]},"skip-wordpress-install":{describe:"[Deprecated] Use --wordpress-install-mode instead.",type:"boolean",hidden:!0},"skip-sqlite-setup":{describe:"Skip the SQLite integration plugin setup to allow the WordPress site to use MySQL.",type:"boolean",default:!1},quiet:{describe:"Do not output logs and progress messages.",type:"boolean",default:!1,hidden:!0},verbosity:{describe:"Output logs and progress messages.",type:"string",choices:Object.values(q).map(i=>i.name),default:"normal"},debug:{describe:"Print PHP error log content if an error occurs during Playground boot.",type:"boolean",default:!1},"auto-mount":{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.",type:"string"},"follow-symlinks":{describe:`Allow Playground to follow symlinks by automatically mounting symlinked directories and files encountered in mounted directories.
|
|
22
|
+
Warning: Following symlinks will expose files outside mounted directories to Playground and could be a security risk.`,type:"boolean",default:!1},"experimental-trace":{describe:"Print detailed messages about system behavior to the console. Useful for troubleshooting.",type:"boolean",default:!1,hidden:!0},"internal-cookie-store":{describe:"Enable internal cookie handling. When enabled, Playground will manage cookies internally using an HttpCookieStore that persists cookies across requests. When disabled, cookies are handled externally (e.g., by a browser in Node.js environments).",type:"boolean",default:!1},intl:{describe:"Enable Intl.",type:"boolean",default:!0},xdebug:{describe:"Enable Xdebug.",type:"boolean",default:!1},"experimental-unsafe-ide-integration":{describe:"Enable experimental IDE development tools. This option edits IDE config files to set Xdebug path mappings and web server details. CAUTION: If there are bugs, this feature may break your IDE config files. Please consider backing up your IDE configs before using this feature.",type:"string",choices:["","vscode","phpstorm"],coerce:i=>i===""?["vscode","phpstorm"]:[i]},"experimental-blueprints-v2-runner":{describe:"Use the experimental Blueprint V2 runner.",type:"boolean",default:!1,hidden:!0},mode:{describe:"Blueprints v2 runner mode to use. This option is required when using the --experimental-blueprints-v2-runner flag with a blueprint.",type:"string",choices:["create-new-site","apply-to-existing-site"],hidden:!0}},o={port:{describe:"Port to listen on when serving.",type:"number",default:9400},"experimental-multi-worker":{describe:"Enable experimental multi-worker support which requires a /wordpress directory backed by a real filesystem. Pass a positive number to specify the number of workers to use. Otherwise, default to the number of CPUs minus 1.",type:"number",coerce:i=>i??Z.cpus().length-1},"experimental-devtools":{describe:"Enable experimental browser development tools.",type:"boolean"}},s={outfile:{describe:"When building, write to this output file.",type:"string",default:"wordpress.zip"}},r=Te(e).usage("Usage: wp-playground <command> [options]").command("server","Start a local WordPress server",i=>i.options({...t,...o})).command("run-blueprint","Execute a Blueprint without starting a server",i=>i.options({...t})).command("build-snapshot","Build a ZIP snapshot of a WordPress site based on a Blueprint",i=>i.options({...t,...s})).demandCommand(1,"Please specify a command").strictCommands().conflicts("experimental-unsafe-ide-integration","experimental-devtools").showHelpOnFail(!1).fail((i,y,w)=>{if(y)throw y;i&&i.includes("Please specify a command")&&(w.showHelp(),console.error(`
|
|
23
|
+
`+i),process.exit(1)),console.error(i),process.exit(1)}).strictOptions().check(async i=>{if(i["skip-wordpress-install"]===!0&&(i["wordpress-install-mode"]="do-not-attempt-installing",i.wordpressInstallMode="do-not-attempt-installing"),i.wp!==void 0&&typeof i.wp=="string"&&!He(i.wp))try{new URL(i.wp)}catch{throw new Error('Unrecognized WordPress version. Please use "latest", a URL, or a numeric version such as "6.2", "6.0.1", "6.2-beta1", or "6.2-RC1"')}const y=i["site-url"];if(typeof y=="string"&&y.trim()!=="")try{new URL(y)}catch{throw new Error(`Invalid site-url "${y}". Please provide a valid URL (e.g., http://localhost:8080 or https://example.com)`)}if(i["auto-mount"]){let w=!1;try{w=d.statSync(i["auto-mount"]).isDirectory()}catch{w=!1}if(!w)throw new Error(`The specified --auto-mount path is not a directory: '${i["auto-mount"]}'.`)}if(i["experimental-multi-worker"]!==void 0){if(i._[0]!=="server")throw new Error("The --experimental-multi-worker flag is only supported when running the server command.");if(i["experimental-multi-worker"]!==void 0&&typeof i["experimental-multi-worker"]=="number"&&i["experimental-multi-worker"]<=1)throw new Error("The --experimental-multi-worker flag must be a positive integer greater than 1.")}if(i["experimental-blueprints-v2-runner"]===!0){if(i.mode!==void 0){if(i["wordpress-install-mode"]!==void 0)throw new Error("The --wordpress-install-mode option cannot be used with the --mode option. Use one or the other.");if("skip-sqlite-setup"in i)throw new Error("The --skipSqliteSetup option is not supported in Blueprint V2 mode.");if(i["auto-mount"]!==void 0)throw new Error("The --mode option cannot be used with --auto-mount because --auto-mount automatically sets the mode.")}else i["wordpress-install-mode"]==="do-not-attempt-installing"?i.mode="apply-to-existing-site":i.mode="create-new-site";const w=i.allow||[];i.followSymlinks===!0&&w.push("follow-symlinks"),i["blueprint-may-read-adjacent-files"]===!0&&w.push("read-local-fs"),i.allow=w}else if(i.mode!==void 0)throw new Error("The --mode option requires the --experimentalBlueprintsV2Runner flag.");return!0});r.wrap(r.terminalWidth());const n=await r.argv,a=n._[0];["run-blueprint","server","build-snapshot"].includes(a)||(r.showHelp(),process.exit(1));const l={...n,command:a,mount:[...n.mount||[],...n["mount-dir"]||[]],"mount-before-install":[...n["mount-before-install"]||[],...n["mount-dir-before-install"]||[]]},c=await de(l);c===void 0&&process.exit(0);const f=(()=>{let i;return async()=>{i!==void 0&&(i=c[Symbol.asyncDispose]()),await i,process.exit(0)}})();process.on("SIGINT",f),process.on("SIGTERM",f)}catch(t){if(!(t instanceof Error))throw t;if(process.argv.includes("--debug"))b.printDebugDetails(t);else{const s=[];let r=t;do s.push(r.message),r=r.cause;while(r instanceof Error);console.error("\x1B[1m"+s.join(" caused by: ")+"\x1B[0m")}process.exit(1)}}const ce=Symbol("playground-cli-testing"),E=e=>process.stdout.isTTY?"\x1B[1m"+e+"\x1B[0m":e,Qe=e=>process.stdout.isTTY?`\x1B[2m${e}\x1B[0m`:e,O=e=>process.stdout.isTTY?`\x1B[3m${e}\x1B[0m`:e,se=e=>process.stdout.isTTY?`\x1B[33m${e}\x1B[0m`:e;async function de(e){let t,o;const s=new Map;if(e.autoMount!==void 0&&(e.autoMount===""&&(e={...e,autoMount:process.cwd()}),e=Le(e)),e.wordpressInstallMode===void 0&&(e.wordpressInstallMode="download-and-install"),e.quiet&&(e.verbosity="quiet",delete e.quiet),e.debug?e.verbosity="debug":e.verbosity==="debug"&&(e.debug=!0),e.verbosity){const f=Object.values(q).find(i=>i.name===e.verbosity).severity;u.logger.setSeverityFilterLevel(f)}e.intl||(e.intl=!0);const r=e.command==="server"?e.port??9400:0,n=Z.platform()==="win32"?void 0:await import("fs-ext").then(f=>f.flockSync).catch(()=>{u.logger.warn("The fs-ext package is not installed. Internal file locking will not be integrated with host OS file locking.")}),a=new ae.FileLockManagerForNode(n);let l=!1,c=!0;return u.logger.log("Starting a PHP server..."),Fe({port:r,onBind:async(f,i)=>{const y="127.0.0.1",w=`http://${y}:${i}`,v=e["site-url"]||w,x=e.command==="server"?e.experimentalMultiWorker??1:1,T=e.command==="server"?x+1:x,H=2**31-1,S=Math.floor(H/T),B="-playground-cli-site-",C=await ze(B);u.logger.debug(`Native temp dir for VFS root: ${C.path}`);const M="WP Playground CLI - Listen for Xdebug",K=".playground-xdebug-root",Q=p.join(process.cwd(),K);if(await A.removeTempDirSymlink(Q),e.xdebug&&e.experimentalUnsafeIdeIntegration){await A.createTempDirSymlink(C.path,Q,process.platform);const h={hostPath:p.join(".",p.sep,K),vfsPath:"/"};try{await A.clearXdebugIDEConfig(M,process.cwd());const g=typeof e.xdebug=="object"?e.xdebug:void 0,k=await A.addXdebugIDEConfig({name:M,host:y,port:i,ides:e.experimentalUnsafeIdeIntegration,cwd:process.cwd(),mounts:[h,...e["mount-before-install"]||[],...e.mount||[]],ideKey:g?.ideKey}),m=e.experimentalUnsafeIdeIntegration,P=m.includes("vscode"),I=m.includes("phpstorm"),F=Object.values(k);console.log(""),F.length>0?(console.log(E("Xdebug configured successfully")),console.log(se("Updated IDE config: ")+F.join(" ")),console.log(se("Playground source root: ")+".playground-xdebug-root"+O(Qe(" – you can set breakpoints and preview Playground's VFS structure in there.")))):(console.log(E("Xdebug configuration failed.")),console.log("No IDE-specific project settings directory was found in the current working directory.")),console.log(""),P&&k.vscode&&(console.log(E("VS Code / Cursor instructions:")),console.log(" 1. Ensure you have installed an IDE extension for PHP Debugging"),console.log(` (The ${E("PHP Debug")} extension by ${E("Xdebug")} has been a solid option)`),console.log(" 2. Open the Run and Debug panel on the left sidebar"),console.log(` 3. Select "${O(M)}" from the dropdown`),console.log(' 3. Click "start debugging"'),console.log(" 5. Set a breakpoint. For example, in .playground-xdebug-root/wordpress/index.php"),console.log(" 6. Visit Playground in your browser to hit the breakpoint"),I&&console.log("")),I&&k.phpstorm&&(console.log(E("PhpStorm instructions:")),console.log(` 1. Choose "${O(M)}" debug configuration in the toolbar`),console.log(" 2. Click the debug button (bug icon)`"),console.log(" 3. Set a breakpoint. For example, in .playground-xdebug-root/wordpress/index.php"),console.log(" 4. Visit Playground in your browser to hit the breakpoint")),console.log("")}catch(g){throw new Error("Could not configure Xdebug",{cause:g})}}const he=p.dirname(C.path),fe=2*24*60*60*1e3;Xe(B,fe,he);const _=p.join(C.path,"internal");d.mkdirSync(_);const me=["wordpress","tmp","home"];for(const h of me){const g=m=>m.vfsPath===`/${h}`;if(!(e["mount-before-install"]?.some(g)||e.mount?.some(g))){const m=p.join(C.path,h);d.mkdirSync(m),e["mount-before-install"]===void 0&&(e["mount-before-install"]=[]),e["mount-before-install"].unshift({vfsPath:`/${h}`,hostPath:m})}}if(e["mount-before-install"])for(const h of e["mount-before-install"])u.logger.debug(`Mount before WP install: ${h.vfsPath} -> ${h.hostPath}`);if(e.mount)for(const h of e.mount)u.logger.debug(`Mount after WP install: ${h.vfsPath} -> ${h.hostPath}`);let $;e["experimental-blueprints-v2-runner"]?$=new Ve(e,{siteUrl:v,processIdSpaceLength:S}):($=new je(e,{siteUrl:v,processIdSpaceLength:S}),typeof e.blueprint=="string"&&(e.blueprint=await _e({sourceString:e.blueprint,blueprintMayReadAdjacentFiles:e["blueprint-may-read-adjacent-files"]===!0})));let V=!1;const U=async function(){V||(V=!0,await Promise.all([...s].map(async([g,k])=>{await k.dispose(),await g.terminate()})),f&&await new Promise(g=>f.close(g)),await C.cleanup())},ge=Je(T,$.getWorkerType(),({exitCode:h,workerIndex:g})=>{V||h===0&&u.logger.error(`Worker ${g} exited with code ${h}
|
|
24
|
+
`)});u.logger.log("Starting up workers");try{const h=await ge,g=await ie(a);{const m=h.shift(),P=await $.bootAndSetUpInitialPlayground(m.phpPort,g,_);if(s.set(m.worker,P),await P.isReady(),l=!0,u.logger.log("Booted!"),t=new qe(P),!e["experimental-blueprints-v2-runner"]){const I=await $.compileInputBlueprint(e["additional-blueprint-steps"]||[]);I&&(u.logger.log("Running the Blueprint..."),await L.runBlueprintV1Steps(I,P),u.logger.log("Finished running the blueprint"))}if(e.command==="build-snapshot"){await et(o,e.outfile),u.logger.log(`WordPress exported to ${e.outfile}`),await U();return}else if(e.command==="run-blueprint"){u.logger.log("Blueprint executed"),await U();return}await t.removeWorker(P),await P.dispose(),await m.worker.terminate(),s.delete(m.worker)}u.logger.log("Preparing workers...");const k=S;return[o]=await Promise.all(h.map(async(m,P)=>{const I=k+P*S,F=await ie(a),N=await $.bootPlayground({worker:m,fileLockManagerPort:F,firstProcessId:I,nativeInternalDirPath:_});return s.set(m.worker,N),t.addWorker(N),N})),u.logger.log(`WordPress is running on ${w} with ${x} worker(s)`),e.xdebug&&e.experimentalDevtools&&(await $e.startBridge({phpInstance:o,phpRoot:"/wordpress"})).start(),{playground:o,server:f,serverUrl:w,[Symbol.asyncDispose]:U,[ce]:{workerThreadCount:x,getWorkerNumberFromProcessId:m=>Math.floor(m/S)}}}catch(h){if(!e.debug)throw h;let g="";throw await o?.fileExists(u.errorLogPath)&&(g=await o.readFileAsText(u.errorLogPath)),await U(),new Error(g,{cause:h})}},async handleRequest(f){if(!l)return b.PHPResponse.forHttpCode(502,"WordPress is not ready yet");if(c){c=!1;const i={"Content-Type":["text/plain"],"Content-Length":["0"],Location:[f.url]};return f.headers?.cookie?.includes("playground_auto_login_already_happened")&&(i["Set-Cookie"]=["playground_auto_login_already_happened=1; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/"]),new b.PHPResponse(302,i,new Uint8Array)}return await t.handleRequest(f)}})}async function Je(e,t,o){const s=[];for(let r=0;r<e;r++){const a=pe(t,{onExit:l=>{o({exitCode:l,workerIndex:r})}});s.push(a)}return Promise.all(s)}function pe(e,{onExit:t}={}){let o;return e==="v1"?o=new z.Worker(new URL("./worker-thread-v1.cjs",typeof document>"u"?require("url").pathToFileURL(__filename).href:R&&R.tagName.toUpperCase()==="SCRIPT"&&R.src||new URL("run-cli-D8BNUM1f.cjs",document.baseURI).href)):o=new z.Worker(new URL("./worker-thread-v2.cjs",typeof document>"u"?require("url").pathToFileURL(__filename).href:R&&R.tagName.toUpperCase()==="SCRIPT"&&R.src||new URL("run-cli-D8BNUM1f.cjs",document.baseURI).href)),new Promise((s,r)=>{o.once("message",function(a){a.command==="worker-script-initialized"&&s({worker:o,phpPort:a.phpPort})}),o.once("error",function(a){console.error(a);const l=new Error(`Worker failed to load worker. ${a.message?`Original error: ${a.message}`:""}`);r(l)});let n=!1;o.once("spawn",()=>{n=!0}),o.once("exit",a=>{n||r(new Error(`Worker exited before spawning: ${a}`)),t?.(a)})})}async function ie(e){const{port1:t,port2:o}=new z.MessageChannel;return await Ie.jspi()?b.exposeAPI(e,null,t):await b.exposeSyncAPI(e,t),o}async function et(e,t){await e.run({code:`<?php
|
|
25
|
+
$zip = new ZipArchive();
|
|
26
|
+
if(false === $zip->open('/tmp/build.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE)) {
|
|
27
|
+
throw new Exception('Failed to create ZIP');
|
|
28
|
+
}
|
|
29
|
+
$files = new RecursiveIteratorIterator(
|
|
30
|
+
new RecursiveDirectoryIterator('/wordpress')
|
|
31
|
+
);
|
|
32
|
+
foreach ($files as $file) {
|
|
33
|
+
echo $file . PHP_EOL;
|
|
34
|
+
if (!$file->isFile()) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
$zip->addFile($file->getPathname(), $file->getPathname());
|
|
38
|
+
}
|
|
39
|
+
$zip->close();
|
|
40
|
+
|
|
41
|
+
`});const o=await e.readFileAsBuffer("/tmp/build.zip");d.writeFileSync(t,o)}exports.LogVerbosity=q;exports.internalsKeyForTesting=ce;exports.mountResources=Ee;exports.parseOptionsAndRunCLI=Ke;exports.runCLI=de;exports.shouldRenderProgress=Y;exports.spawnWorkerThread=pe;
|
|
42
|
+
//# sourceMappingURL=run-cli-D8BNUM1f.cjs.map
|