diff --git a/packages/docs/site/docs/developers/05-local-development/04-wp-playground-cli.md b/packages/docs/site/docs/developers/05-local-development/04-wp-playground-cli.md index f5def5f246..8c40b8275a 100644 --- a/packages/docs/site/docs/developers/05-local-development/04-wp-playground-cli.md +++ b/packages/docs/site/docs/developers/05-local-development/04-wp-playground-cli.md @@ -1,24 +1,24 @@ --- title: Playground CLI slug: /developers/local-development/wp-playground-cli +description: A command-line tool for WordPress development and testing with quick setup, flexible configuration, and minimal dependencies. --- # Playground CLI -[@wp-playground/cli](https://www.npmjs.com/package/@wp-playground/cli) is a command-line tool that simplifies the WordPress development and testing flow. -Playground CLI supports auto-mounting a directory with a plugin, theme, or WordPress installation. But if you need flexibility, the CLI supports mounting commands to personalize your local environment. +[@wp-playground/cli](https://www.npmjs.com/package/@wp-playground/cli) is a command-line tool that simplifies the WordPress development and testing flow. You can use Playground CLI to auto-mount a directory with a plugin, theme, or WordPress installation. If you need flexibility, you can use mounting commands to personalize your local environment. **Key features:** -- **Quick Setup**: Set up a local WordPress environment in seconds. +- **Quick setup**: Set up a local WordPress environment in seconds. - **Flexibility**: Allows for configuration to adapt to different scenarios. -- **Simple Environment**: No extra configuration, just a compatible Node version, and you are ready to use it. +- **Simple environment**: No extra configuration, just a compatible Node version, and you are ready to use it. ## Requirements The Playground CLI requires Node.js 20.18 or higher, which is the recommended Long-Term Support (LTS) version. You can download it from the [Node.js website](https://nodejs.org/en/download). -## Quickstart +## Quick start To run the Playground CLI, open a command line and use the following command: @@ -28,37 +28,44 @@ npx @wp-playground/cli@latest server ![Playground CLI in Action](@site/static/img/developers/npx-wp-playground-server.gif) -With the previous command, you only get a fresh WordPress instance to test. Most developers will want to test their own work. To test a plugin or a theme, navigate to your project folder and run the CLI with the `--auto-mount` flag: +With the previous command, you only get a fresh WordPress instance to test. Most developers will want to test their own work. To test a plugin or a theme, navigate to your project folder and run the CLI with the `--auto-mount` flag. + +The `--auto-mount` flag analyzes the directory structure and automatically determines whether you're working with a plugin, theme, wp-content directory, full WordPress installation, or static HTML/PHP files. It then mounts your project to the correct path in the virtual WordPress environment, so you can start developing immediately without manual configuration. ```bash -cd my-plugin-or-theme-directory +cd my-plugin npx @wp-playground/cli@latest server --auto-mount ``` -### Choosing a WordPress and PHP Version +### Choosing a WordPress and PHP version + +By default, the CLI loads PHP 8.3 and the latest stable version of WordPress. You can specify different versions using the `--wp=` and `--php=` flags. This is particularly useful when you need to test your plugin or theme for compatibility across different WordPress and PHP versions, ensuring your code works correctly for users running older installations. + +```bash +npx @wp-playground/cli@latest server --wp=6.4 --php=8.0 +``` + +### Setting a custom site URL -By default, the CLI loads the latest stable version of WordPress and PHP 8.3 due to its improved performance. To specify your preferred versions, you can use the flag `--wp=` and `--php=`: +You can configure a custom site URL for your development environment, which is useful for testing domain-specific functionality or simulating production environments: ```bash -npx @wp-playground/cli@latest server --wp=6.8 --php=8.3 +npx @wp-playground/cli@latest server --site-url=https://my-local-dev.test ``` -### Loading Blueprints +### Loading blueprints One way to take your Playground CLI development experience to the next level is to integrate with [Blueprints](/blueprints/getting-started/). For those unfamiliar with this technology, it allows developers to configure the initial state for their WordPress Playground instances. -Using the `--blueprint=` flag, developers can run a Playground with a custom initial state. We’ll use the example below to do this. +Using the `--blueprint=` flag, developers can run a Playground with a custom initial state. We'll use the example below to do this. **(my-blueprint.json)** -```bash +```json { - "landingPage": "/wp-admin/options-general.php?page=akismet-key-config", - "login": true, - "plugins": [ - "hello-dolly", - "https://raw.githubusercontent.com/adamziel/blueprints/trunk/docs/assets/hello-from-the-dashboard.zip" - ] + "landingPage": "/wp-admin/options-general.php?page=akismet-key-config", + "login": true, + "plugins": ["hello-dolly", "https://raw.githubusercontent.com/adamziel/blueprints/trunk/docs/assets/hello-from-the-dashboard.zip"] } ``` @@ -76,6 +83,16 @@ Some projects have a specific structure that requires a custom configuration; fo npx @wp-playground/cli@latest server --mount=.:/wordpress/wp-content/plugins/MY-PLUGIN-DIRECTORY ``` +**Multiple mounts:** + +You can mount multiple directories at once: + +```bash +npx @wp-playground/cli@latest server \ + --mount=./my-plugin:/wordpress/wp-content/plugins/my-plugin \ + --mount=./my-theme:/wordpress/wp-content/themes/my-theme +``` + ### Mounting before WordPress installation Consider mounting your WordPress project files before the WordPress installation begins. This approach is beneficial if you want to override the Playground boot process, as it can help connect Playground with `WP-CLI`. The `--mount-before-install` flag supports this process. @@ -84,11 +101,32 @@ Consider mounting your WordPress project files before the WordPress installation npx @wp-playground/cli@latest server --mount-before-install=.:/wordpress/ ``` -:::info -On Windows, the path format `/host/path:/vfs/path` can cause issues. To resolve this, use the flags `--mount-dir` and `--mount-dir-before-install`. These flags let you specify host and virtual file system paths in an alternative format`"/host/path"` `"/vfs/path"`. +### Symlink support for monorepos + +Sometimes you're working with a complex project structure where directories are symlinked to another location on the disk, e.g.: + +``` +/home/alex/my-project +└── wp-content + └── plugins + ├── hello-dolly/ # regular directory + └── secret-plugin → /home/www/plugins/secret-plugin +``` + +By default, Playground CLI only accesses the directories you explicitly mount and won't any load files from `/home/www/plugins/secret-plugin`. You can, however, explicitly instruct Playground CLI to follow that, and other, symlinks with the `--follow-symlink` option: + + +```bash +npx @wp-playground/cli@latest server \ + --follow-symlinks \ + --mount-before-install=./packages/my-plugin:/wordpress/wp-content/plugins/my-plugin +``` + +:::caution +Using `--follow-symlinks` can expose files outside mounted directories to Playground and could be a security risk. Only use this flag when you trust the symlink targets. ::: -### Understanding Data Persistence and SQLite Location +### Understanding data persistence and SQLite location By default, Playground CLI stores WordPress files and the SQLite database in **temporary directories on your operating system**: @@ -99,7 +137,7 @@ By default, Playground CLI stores WordPress files and the SQLite database in **t └── tmp/ # Temporary PHP files ``` -**Finding Your Temp Directory:** +**Finding your temp directory:** The actual location depends on your OS (these are examples or common possibilities): @@ -125,7 +163,7 @@ Mount before WP install: /wordpress -> /private/var/folders/c8/mwz12ycx4s509056kby3hk180000gn/T/node-playground-cli-site-62926--62926-yQNOdvJVIgYC/wordpress ``` -**Where is the SQLite Database Stored?** +**Where is the SQLite database stored?** The database location depends on what you mount: @@ -141,10 +179,10 @@ The database location depends on what you mount: - **Custom mounts**: Database location follows your mount configuration -**Automatic Cleanup:** +**Automatic cleanup:** Playground CLI automatically removes temp directories that are: -- Older than 2 days +- Older than two days - No longer associated with a running process **Recommendation:** To persist both your code and database when developing plugins or themes, mount the entire `wp-content` directory instead of just the plugin/theme folder. @@ -157,9 +195,23 @@ cd my-wordpress-project npx @wp-playground/cli@latest server --mount=./wp-content:/wordpress/wp-content ``` -## Command and Arguments +## Verbosity and debugging + +The CLI supports different output verbosity levels for `quiet`, `normal` (default) and `debug` mode. Sometimes you want to hide all the stdout output, e.g. in a script or a CI automation. You can do that using the `--verbosity=quiet` option: + +```bash +npx @wp-playground/cli@latest server --verbosity=quiet +``` + +Get detailed logging information for troubleshooting, with `debug` mode: -Playground CLI is simple, configurable, and unopinionated. You can set it up according +```bash +npx @wp-playground/cli@latest server --verbosity=debug +``` + +## Commands and arguments + +The Playground CLI is simple, configurable, and unopinionated. You can set it up according to your unique WordPress setup. With the Playground CLI, you can use the following top-level commands: - **`server`**: (Default) Starts a local WordPress server. @@ -190,10 +242,10 @@ The `server` command supports the following optional arguments: - `--internal-cookie-store`: 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). Defaults to false. - `--xdebug`: Enable Xdebug. Defaults to false. - `--experimental-devtools`: Enable experimental browser development tools. Defaults to false. -- `--experimental-multi-worker=`: 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, defaults to the number of CPUs minus 1. +- `--experimental-multi-worker=`: Enable experimental multi-worker support which requires a `/wordpress` directory backed by a real file system. Pass a positive number to specify the number of workers to use. Otherwise, defaults to the number of CPUs minus one. -:::caution -With the flag `--follow-symlinks`, the following symlinks will expose files outside mounted directories to Playground and could be a security risk. +:::info +On Windows, the path format `/host/path:/vfs/path` can cause issues. To resolve this, use the flags `--mount-dir` and `--mount-dir-before-install`. These flags let you specify host and virtual file system paths in an alternative format`"/host/path"` `"/vfs/path"`. ::: ## Need some help with the CLI? @@ -204,7 +256,7 @@ With the Playground CLI, you can use the `--help` flag to get the full list of a npx @wp-playground/cli@latest --help ``` -## Programmatic Usage with JavaScript +## Programmatic usage with JavaScript The Playground CLI can also be controlled programmatically from your JavaScript/TypeScript code using the `runCLI` function. This gives you direct access to all CLI functionalities within your code, which is useful for automating end-to-end tests. Let's cover the basics of using `runCLI`. @@ -225,9 +277,68 @@ cliServer = await runCLI({ } as RunCLIArgs); ``` -To execute the code above, the developer can set their preferred method. A simple way to execute this code is to save it as a `.ts` file and run it with a tool like `tsx`. For example: `tsx my-script.ts` +To execute the code above, you can set your preferred method. A simple way to execute this code is to save it as a `.ts` file and run it with a tool like `tsx`. For example: `tsx my-script.ts` -### Setting a Blueprint +**Testing with specific PHP versions:** + +```TypeScript +import { runCLI } from "@wp-playground/cli"; + +const cliServer = await runCLI({ + command: 'server', + php: '8.0', + skipWordPressSetup: true, + skipSqliteSetup: true, +}); + +// Test PHP version +await cliServer.playground.writeFile( + '/wordpress/version.php', + '' +); + +const versionUrl = new URL('/version.php', cliServer.serverUrl); +const response = await fetch(versionUrl); +const version = await response.text(); +console.log('PHP Version:', version); // Outputs: 8.0.x +``` + +### Setting a custom site URL programmatically + +```TypeScript +const cliServer = await runCLI({ + command: 'server', + 'site-url': 'https://my-staging.example.com', + port: 9500 +}); + +// Verify site URL is set correctly +await cliServer.playground.writeFile( + '/wordpress/check-url.php', + '' +); + +const checkUrl = new URL('/check-url.php', cliServer.serverUrl); +const response = await fetch(checkUrl); +console.log('Site URL:', await response.text()); +``` + +### Controlling verbosity programmatically + +```TypeScript +import { runCLI } from "@wp-playground/cli"; +import { logger } from '@php-wasm/logger'; + +const cliServer = await runCLI({ + command: 'server', + verbosity: 'debug' // or 'quiet' or 'normal' +}); + +// Add custom logging +logger.debug('Custom debug message'); +``` + +### Setting a blueprint You can provide a blueprint in two ways: either as an object literal directly passed to the `blueprint` property, or as a string containing the path to an external `.json` file. @@ -277,25 +388,41 @@ const myBlueprint: BlueprintDeclaration = { ### Mounting a plugin programmatically -It is possible to mount local directories programmatically using `runCLI`. The options `mount` and `mount-before-install` are available. The `hostPath` property expects a path to a directory on your local machine. This path should be relative to where your script is being executed. +You can mount local directories programmatically using `runCLI`. The options `mount` and `mount-before-install` are available. The `hostPath` property expects a path to a directory on your local machine. This path should be relative to where your script is being executed. ```TypeScript - cliServer = await runCLI({ - command: 'server', - login: true, - 'mount-before-install': [ - { - hostPath: './[my-plugin-local-path]', - vfsPath: '/wordpress/wp-content/plugins/my-plugin', - }, - ], - }); +cliServer = await runCLI({ + command: 'server', + login: true, + 'mount-before-install': [ + { + hostPath: './[my-plugin-local-path]', + vfsPath: '/wordpress/wp-content/plugins/my-plugin', + }, + ], +}); ``` -With those options we can combine mounting parts of the project with blueprints, for example: +**Auto-mounting programmatically:** ```TypeScript +import { runCLI } from "@wp-playground/cli"; +import process from 'node:process'; + +// Change to your project directory +process.chdir('./my-plugin'); +const cliServer = await runCLI({ + command: 'server', + autoMount: '' // Empty string triggers auto-detection +}); +``` + +### Combining mounts with blueprints + +You can combine mounting parts of the project with blueprints, for example: + +```TypeScript import { runCLI, RunCLIArgs, RunCLIServer } from "@wp-playground/cli"; let cliServer: RunCLIServer; @@ -321,3 +448,325 @@ cliServer = await runCLI({ } } as RunCLIArgs); ``` + +**Multiple mounts with blueprints:** + +```TypeScript +const cliServer = await runCLI({ + command: 'server', + mount: [ + { + hostPath: './my-plugin', + vfsPath: '/wordpress/wp-content/plugins/my-plugin' + }, + { + hostPath: './my-theme', + vfsPath: '/wordpress/wp-content/themes/my-theme' + } + ], + blueprint: { + steps: [ + { + step: 'activatePlugin', + pluginPath: '/wordpress/wp-content/plugins/my-plugin/plugin.php' + }, + { + step: 'activateTheme', + themeFolderName: 'my-theme' + } + ] + } +}); +``` + +**Complex blueprint with multiple configurations:** + +```TypeScript +const cliServer = await runCLI({ + command: 'server', + php: '8.3', + wp: 'latest', + login: true, + mount: [ + { + hostPath: './my-plugin', + vfsPath: '/wordpress/wp-content/plugins/my-plugin' + } + ], + blueprint: { + landingPage: '/wp-admin/plugins.php', + steps: [ + { + step: 'activatePlugin', + pluginPath: '/wordpress/wp-content/plugins/my-plugin/plugin.php' + }, + { + step: 'setSiteOptions', + options: { + blogname: 'Plugin Test Site', + permalink_structure: '/%postname%/' + } + }, + { + step: 'runPHP', + code: '' + } + ] + } +}); +``` + +### Mode selection (Blueprint v2) + +You can specify different modes when working with Blueprint v2: + +**Creating a new site:** + +```TypeScript +import { runCLI } from "@wp-playground/cli"; + +const cliServer = await runCLI({ + command: 'server', + 'experimental-blueprints-v2-runner': true, + mode: 'create-new-site', + 'mount-before-install': [ + { + hostPath: './my-new-site', + vfsPath: '/wordpress' + } + ] +}); +``` + +**Applying to an existing site:** + +```TypeScript +const cliServer = await runCLI({ + command: 'server', + 'experimental-blueprints-v2-runner': true, + mode: 'apply-to-existing-site', + 'mount-before-install': [ + { + hostPath: './existing-wordpress', + vfsPath: '/wordpress' + } + ], + blueprint: { + steps: [ + { + step: 'setSiteOptions', + options: { + blogname: 'Updated Site Name' + } + } + ] + } +}); +``` + +## Automated testing + +### Integration testing with Vitest + +The programmatic API is excellent for automated testing. Here's a complete example using Vitest: + +```TypeScript +import { describe, test, expect, afterEach } from 'vitest'; +import { runCLI, RunCLIServer } from "@wp-playground/cli"; + +describe('My Plugin Tests', () => { + let cliServer: RunCLIServer; + + afterEach(async () => { + if (cliServer) { + await cliServer[Symbol.asyncDispose](); + } + }); + + test('plugin activates successfully', async () => { + cliServer = await runCLI({ + command: 'server', + mount: [ + { + hostPath: './my-plugin', + vfsPath: '/wordpress/wp-content/plugins/my-plugin' + } + ], + blueprint: { + steps: [ + { + step: 'activatePlugin', + pluginPath: '/wordpress/wp-content/plugins/my-plugin/plugin.php' + } + ] + } + }); + + const homeUrl = new URL('/', cliServer.serverUrl); + const response = await fetch(homeUrl); + + expect(response.status).toBe(200); + const html = await response.text(); + expect(html).toContain('My Plugin'); + }); + + test('plugin settings page loads', async () => { + cliServer = await runCLI({ + command: 'server', + login: true, // Auto-login as admin + mount: [ + { + hostPath: './my-plugin', + vfsPath: '/wordpress/wp-content/plugins/my-plugin' + } + ], + blueprint: { + steps: [ + { + step: 'activatePlugin', + pluginPath: '/wordpress/wp-content/plugins/my-plugin/plugin.php' + } + ] + } + }); + + const settingsUrl = new URL( + '/wp-admin/options-general.php?page=my-plugin', + cliServer.serverUrl + ); + const response = await fetch(settingsUrl); + + expect(response.status).toBe(200); + }); +}); +``` + +### Testing theme customizations + +```TypeScript +test('theme displays custom header', async () => { + cliServer = await runCLI({ + command: 'server', + mount: [ + { + hostPath: './my-theme', + vfsPath: '/wordpress/wp-content/themes/my-theme' + } + ], + blueprint: { + steps: [ + { + step: 'installTheme', + themeData: { + resource: 'vfs', + path: '/wordpress/wp-content/themes/my-theme' + } + }, + { + step: 'activateTheme', + themeFolderName: 'my-theme' + } + ] + } + }); + + const homeUrl = new URL('/', cliServer.serverUrl); + const response = await fetch(homeUrl); + const html = await response.text(); + + expect(html).toContain('