Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Playground npm package #147

Closed
adamziel opened this issue Mar 8, 2023 · 5 comments
Closed

Playground npm package #147

adamziel opened this issue Mar 8, 2023 · 5 comments

Comments

@adamziel
Copy link
Collaborator

adamziel commented Mar 8, 2023

Description

It would be great to build custom apps with WordPress Playground like this:

import { Playground } from '@wordpress/playground';

function MyApp() {
    const playground = useRef( null );
    
    function login() {
        await playground.navigate( '/wp-login.php' );
        await playground.navigate( '/wp-login.php', {
		method: 'POST',
		data: {
			log: 'admin',
			pwd: 'password',
			rememberme: 'forever',
		}
        } );
    }
    
    return (
        <div>
            <h2>My custom app</h2>
            <button onClick={ login }>Login as admin</button>
            <Playground 
                 php="7.4"
                 wp="6.0"
                 plugins={{"gutenberg": "latest"}}
                 onNavigate={ handleNewUrl }
                 ref={ playground } />
        </div>
    );
}

Technically, all these features are already implemented. There's just no package with neat API to import.

@adamziel adamziel changed the title Turn Playground into a library Playground npm package Mar 8, 2023
@dgwyer
Copy link
Contributor

dgwyer commented Mar 8, 2023

1000% yes to this!

@eliot-akira
Copy link
Collaborator

eliot-akira commented Mar 8, 2023

Just a thought.. I wonder if it'd be useful (or practical) to export lower-level primitives of the Playground, without dependency on React or the UI layer in general.

The reason I ask is, when I was experimenting with the "dual pane" idea, I had to create a new entry point for Rollup in src/wordpress-playground that's different from index.tsx, to be able to remix existing features from other files in the folder/package.

In pseudo-code, I picture something like:

import { bootWordPress } from '@wordpress/playground/boot'
import { login } from '@wordpress/playground/wp-macros'

const workerThread = await bootWordPress(config)

await login(workerThread, 'admin', 'password')

That might make it easier for people to build their own playground interfaces, for example using Web Components.

adamziel added a commit that referenced this issue Mar 14, 2023
## Description

With this PR, Playground exposes a consistent communication layer for its consumers:

```ts
const playground = connect( iframe.contentWindow );
playground.writeFile( '/wordpress/test.php', 'Hello, world!' );
playground.goTo( '/test.php' );
```

It means **your app can have the same powers as the official demo**. Just note this PR does not expose any public packages yet. That will be the next step.

## Under the hood

Technically, this PR:

1. Formalizes `php-wasm` public API
2. Formalizes Playground public API as an extension of the above
3. Uses the [comlink](https://github.com/GoogleChromeLabs/comlink) library to expose Playground's public API

There are a few layers to this:

* Playground worker initializes the PHP and WordPress and exposes an internal API using comlink
* Playground "connector" HTML page consumes the worker API, extends it, and re-exposes it publicly using comlink
* Playground UI is now a separate app that connects to the "connector" page and consumes its API using comlink

All the public-facing features, like plugin pre-installation or import/export, are now implemented by consuming the public API. Once I [publish these features in npm](#147), consuming Playground will be as simple as importing a package.

This PR also refactors the Playground website to use React – this process was indispensable in shaping the public API.

## Public API

Here's the raw interface – the documentation will be shipped with the npm package and provided in this repo.

```ts
interface PlaygroundAPI {
	absoluteUrl: string;
	goTo(requestedPath: string): Promise<void>;
	getCurrentURL(): Promise<void>;
	setIframeSandboxFlags(flags: string[]): Promise<void>;

	onNavigation(callback: (newURL: string) => void): Promise<void>;
	onDownloadProgress(
		callback: (progress: CustomEvent<ProgressEvent>) => void
	): Promise<void>;

	getWordPressModuleDetails(): Promise<{ staticAssetsDirectory: string; defaultTheme: string; }>;

	pathToInternalUrl(path: string): Promise<string>;
	internalUrlToPath(internalUrl: string): Promise<string>;

	request(
		request: PHPServerRequest,
		redirects?: number
	): Promise<PHPResponse>;
	run(request?: PHPRequest | undefined): Promise<PHPResponse>;
	setPhpIniPath(path: string): Promise<void>;
	setPhpIniEntry(key: string, value: string): Promise<void>;
	mkdirTree(path: string): Promise<void>;
	readFileAsText(path: string): Promise<string>;
	readFileAsBuffer(path: string): Promise<Uint8Array>;
	writeFile(path: string, data: string | Uint8Array): Promise<void>;
	unlink(path: string): Promise<void>;
	listFiles(path: string): Promise<string[]>;
	isDir(path: string): Promise<boolean>;
	fileExists(path: string): Promise<boolean>;
}
```
adamziel added a commit that referenced this issue Mar 16, 2023
## Description

Sets up the correct build pipeline for all parts of Playground and PHP.wasm. This enables a public release of the [Playground API](#149) npm package!

I've been [struggling](#146) with [this](#70) for [a while](#150) and couldn't understand what's so hard. NX made it apparent – look at this dependency graph!

<img width="1291" alt="CleanShot 2023-03-16 at 23 16 26@2x" src="https://user-images.githubusercontent.com/205419/225764795-7fa8e972-09f8-41ef-aac2-1c96bd100ea0.png">

No wonder it's been almost impossible to set everything up by hand!

## Usage

Start with `yarn install`

### Shortcuts

To start a copy of `wasm.wordpress.net` locally, run `yarn run dev`.
To build it, run `yarn run build`.

### Fully qualified commands

In reality, these `yarn run` commands are just triggering the underlying project's nx `dev` and `build` commands:

```bash
nx dev playground-website
nx build playground-website
```

Here's a few more interesting commands:

```bash
# Build and run PHP.wasm CLI
nx start php-wasm-cli

# Build latest WordPress releases
nx recompile-wordpress:all playground-remote

# Recompile PHP 5.6 - 8.2 releases to .wasm for web
nx recompile-php:all php-wasm-web

# Recompile PHP 5.6 - 8.2 releases to .wasm for node
nx recompile-php:all php-wasm-node

# Builds markdown files for readthedocs site
nx build docs-site

# Builds the Playground Client npm package
nx build playground-client
```

## NX is the tool Playground needed from the outset

It's ridiculous how many problems this solves:

* The build pipeline is explicitly defined and easy to modify
* Tasks only run once their dependencies are ready
* The dev mode works and is fast
* The build works and is fast
* We get CI checks to confirm the entire build process still works (which solves #150)
* Cross-package TypeScript just works
* There are linters and formatters (which solves #14)
* Documentation is correctly generated from the latest built artifacts
* There are nice generators for bootstraping new packages and moving the existing ones around
* There are checks to ensure the private `php-wasm-common` package is not imported by anything else than `php-wasm-web` and `php-wasm-node`

## Next steps

* Add Lerna to harness package publishing
* Additional developer documentation for the nx-based flow

Related to #148 and #147
@adamziel
Copy link
Collaborator Author

adamziel commented Mar 17, 2023

@eliot-akira a generic Playground client package is now available!

import { connectPlayground } from '@wp-playground/client';

const client = await connectPlayground(
    document.getElementById('wp')! as HTMLIFrameElement,
    `https://wasm.wordpress.net/remote.html`
);
await client.isReady();
await client.goTo('/wp-admin/');

const result = await client.run({
    code: '<?php echo "Hi!"; ',
});
console.log(new TextDecoder().decode(result.body));

Boarding a plane now, I’ll write another comment here later on.

Edit: I forgot to follow-up and what I had to say :D

@eliot-akira
Copy link
Collaborator

eliot-akira commented Mar 18, 2023

Wonderful! I saw the pull request for Playground Public API, separating the functional layers of PHP WASM, the API with convenient wrapper for WebWorker interaction, and the UI as its own module that uses the API. It really clarified the separation of concerns, and defined the building blocks that the public can use to create their own playgrounds.

And the related code refactoring to use NX for package build orchestration, it makes a lot of sense. Looked like a huge overhaul to bring all the work so far into a new level of logical organization.

It's been so educational to watch the evolution of this project.

@adamziel
Copy link
Collaborator Author

I'm glad you like it! Does it help with the dual-pane use-case you've mentioned?

@adamziel adamziel closed this as completed May 2, 2023
Pookie717 added a commit to Pookie717/wordpress-playground that referenced this issue Oct 1, 2023
## Description

With this PR, Playground exposes a consistent communication layer for its consumers:

```ts
const playground = connect( iframe.contentWindow );
playground.writeFile( '/wordpress/test.php', 'Hello, world!' );
playground.goTo( '/test.php' );
```

It means **your app can have the same powers as the official demo**. Just note this PR does not expose any public packages yet. That will be the next step.

## Under the hood

Technically, this PR:

1. Formalizes `php-wasm` public API
2. Formalizes Playground public API as an extension of the above
3. Uses the [comlink](https://github.com/GoogleChromeLabs/comlink) library to expose Playground's public API

There are a few layers to this:

* Playground worker initializes the PHP and WordPress and exposes an internal API using comlink
* Playground "connector" HTML page consumes the worker API, extends it, and re-exposes it publicly using comlink
* Playground UI is now a separate app that connects to the "connector" page and consumes its API using comlink

All the public-facing features, like plugin pre-installation or import/export, are now implemented by consuming the public API. Once I [publish these features in npm](WordPress/wordpress-playground#147), consuming Playground will be as simple as importing a package.

This PR also refactors the Playground website to use React – this process was indispensable in shaping the public API.

## Public API

Here's the raw interface – the documentation will be shipped with the npm package and provided in this repo.

```ts
interface PlaygroundAPI {
	absoluteUrl: string;
	goTo(requestedPath: string): Promise<void>;
	getCurrentURL(): Promise<void>;
	setIframeSandboxFlags(flags: string[]): Promise<void>;

	onNavigation(callback: (newURL: string) => void): Promise<void>;
	onDownloadProgress(
		callback: (progress: CustomEvent<ProgressEvent>) => void
	): Promise<void>;

	getWordPressModuleDetails(): Promise<{ staticAssetsDirectory: string; defaultTheme: string; }>;

	pathToInternalUrl(path: string): Promise<string>;
	internalUrlToPath(internalUrl: string): Promise<string>;

	request(
		request: PHPServerRequest,
		redirects?: number
	): Promise<PHPResponse>;
	run(request?: PHPRequest | undefined): Promise<PHPResponse>;
	setPhpIniPath(path: string): Promise<void>;
	setPhpIniEntry(key: string, value: string): Promise<void>;
	mkdirTree(path: string): Promise<void>;
	readFileAsText(path: string): Promise<string>;
	readFileAsBuffer(path: string): Promise<Uint8Array>;
	writeFile(path: string, data: string | Uint8Array): Promise<void>;
	unlink(path: string): Promise<void>;
	listFiles(path: string): Promise<string[]>;
	isDir(path: string): Promise<boolean>;
	fileExists(path: string): Promise<boolean>;
}
```
Pookie717 added a commit to Pookie717/wordpress-playground that referenced this issue Oct 1, 2023
## Description

Sets up the correct build pipeline for all parts of Playground and PHP.wasm. This enables a public release of the [Playground API](WordPress/wordpress-playground#149) npm package!

I've been [struggling](WordPress/wordpress-playground#146) with [this](WordPress/wordpress-playground#70) for [a while](WordPress/wordpress-playground#150) and couldn't understand what's so hard. NX made it apparent – look at this dependency graph!

<img width="1291" alt="CleanShot 2023-03-16 at 23 16 26@2x" src="https://user-images.githubusercontent.com/205419/225764795-7fa8e972-09f8-41ef-aac2-1c96bd100ea0.png">

No wonder it's been almost impossible to set everything up by hand!

## Usage

Start with `yarn install`

### Shortcuts

To start a copy of `wasm.wordpress.net` locally, run `yarn run dev`.
To build it, run `yarn run build`.

### Fully qualified commands

In reality, these `yarn run` commands are just triggering the underlying project's nx `dev` and `build` commands:

```bash
nx dev playground-website
nx build playground-website
```

Here's a few more interesting commands:

```bash
# Build and run PHP.wasm CLI
nx start php-wasm-cli

# Build latest WordPress releases
nx recompile-wordpress:all playground-remote

# Recompile PHP 5.6 - 8.2 releases to .wasm for web
nx recompile-php:all php-wasm-web

# Recompile PHP 5.6 - 8.2 releases to .wasm for node
nx recompile-php:all php-wasm-node

# Builds markdown files for readthedocs site
nx build docs-site

# Builds the Playground Client npm package
nx build playground-client
```

## NX is the tool Playground needed from the outset

It's ridiculous how many problems this solves:

* The build pipeline is explicitly defined and easy to modify
* Tasks only run once their dependencies are ready
* The dev mode works and is fast
* The build works and is fast
* We get CI checks to confirm the entire build process still works (which solves #150)
* Cross-package TypeScript just works
* There are linters and formatters (which solves #14)
* Documentation is correctly generated from the latest built artifacts
* There are nice generators for bootstraping new packages and moving the existing ones around
* There are checks to ensure the private `php-wasm-common` package is not imported by anything else than `php-wasm-web` and `php-wasm-node`

## Next steps

* Add Lerna to harness package publishing
* Additional developer documentation for the nx-based flow

Related to WordPress/wordpress-playground#148 and WordPress/wordpress-playground#147
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants