Skip to content

Commit

Permalink
Support installing WP as needed in Playground remote
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonpayton committed Oct 7, 2024
1 parent 24c8d29 commit 9ebeb51
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 48 deletions.
5 changes: 4 additions & 1 deletion packages/playground/client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,11 @@ export interface StartPlaygroundOptions {
*/
onBeforeBlueprint?: () => Promise<void>;
mounts?: Array<MountDescriptor>;
shouldInstallWordPress?: boolean;
/**
* Whether to install WordPress. Value may be boolean or 'auto'.
* If 'auto', WordPress will be installed if it is not already installed.
*/
shouldInstallWordPress?: boolean | 'auto'; /**
* The string prefix used in the site URL served by the currently
* running remote.html. E.g. for a prefix like `/scope:playground/`,
* the scope would be `playground`. See the `@php-wasm/scopes` package
Expand Down
1 change: 1 addition & 0 deletions packages/playground/remote/src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './boot-playground-remote';
export * from './playground-client';
export { looksLikePlaygroundDirectory } from './worker-utils';
export {
MinifiedWordPressVersions,
MinifiedWordPressVersionsList,
Expand Down
21 changes: 20 additions & 1 deletion packages/playground/remote/src/lib/worker-thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
spawnHandlerFactory,
backfillStaticFilesRemovedFromMinifiedBuild,
hasCachedStaticFilesRemovedFromMinifiedBuild,
looksLikePlaygroundDirectory,
} from './worker-utils';
import { EmscriptenDownloadMonitor } from '@php-wasm/progress';
import { createMemoizedFetch } from './create-memoized-fetch';
Expand Down Expand Up @@ -72,7 +73,7 @@ export type WorkerBootOptions = {
scope: string;
withNetworking: boolean;
mounts?: Array<MountDescriptor>;
shouldInstallWordPress?: boolean;
shouldInstallWordPress?: boolean | 'auto';
};

/** @inheritDoc PHPClient */
Expand Down Expand Up @@ -188,6 +189,24 @@ export class PlaygroundWorkerEndpoint extends PHPWorker {
}

try {
if (shouldInstallWordPress === 'auto') {
// Default to installing WordPress unless we detect
// it in one of the mounts.
shouldInstallWordPress = true;

// NOTE: This check is insufficient if a complete WordPress
// installation is composed of multiple mounts.
for (const mount of mounts) {
const dirHandle = await directoryHandleFromMountDevice(
mount.device
);
if (await looksLikePlaygroundDirectory(dirHandle)) {
shouldInstallWordPress = false;
break;
}
}
}

// Start downloading WordPress if needed
let wordPressRequest = null;
if (shouldInstallWordPress) {
Expand Down
44 changes: 44 additions & 0 deletions packages/playground/remote/src/lib/worker-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,3 +245,47 @@ export async function getWordPressStaticZipUrl(php: PHP) {
}
return joinPaths('/', staticAssetsDirectory, 'wordpress-static.zip');
}

/**
* Check if the given directory handle directory is a Playground directory.
*
* @TODO: Create a shared package like @wp-playground/wordpress for such utilities
* and bring in the context detection logic from wp-now – only express it in terms of
* either abstract FS operations or isomorphic PHP FS operations.
* (we can't just use Node.js require('fs') in the browser, for example)
*
* @TODO: Reuse the "isWordPressInstalled" logic implemented in the boot protocol.
* Perhaps mount OPFS first, and only then check for the presence of the
* WordPress installation? Or, if not, perhaps implement a shared file access
* abstraction that can be used both with the PHP module and OPFS directory handles?
*
* @param dirHandle
*/
export async function looksLikePlaygroundDirectory(
dirHandle: FileSystemDirectoryHandle
) {
// Run this loop just to trigger an exception if the directory handle is no good.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for await (const _ of dirHandle.keys()) {
break;
}

try {
/**
* Assume it's a Playground directory if these files exist:
* - wp-config.php
* - wp-content/database/.ht.sqlite
*/
await dirHandle.getFileHandle('wp-config.php', { create: false });
const wpContent = await dirHandle.getDirectoryHandle('wp-content', {
create: false,
});
const database = await wpContent.getDirectoryHandle('database', {
create: false,
});
await database.getFileHandle('.ht.sqlite', { create: false });
} catch (e) {
return false;
}
return true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import { Blueprint, StepDefinition } from '@wp-playground/blueprints';
import { logger } from '@php-wasm/logger';
import { setupPostMessageRelay } from '@php-wasm/web';
import { startPlaygroundWeb } from '@wp-playground/client';
import { PlaygroundClient } from '@wp-playground/remote';
import {
PlaygroundClient,
looksLikePlaygroundDirectory,
} from '@wp-playground/remote';
import { getRemoteUrl } from '../../config';
import { setActiveModal, setActiveSiteError } from './slice-ui';
import { PlaygroundDispatch, PlaygroundReduxState } from './store';
Expand Down Expand Up @@ -72,7 +75,7 @@ export function bootSiteClient(
let isWordPressInstalled = false;
if (mountDescriptor) {
try {
isWordPressInstalled = await playgroundAvailableInOpfs(
isWordPressInstalled = await looksLikePlaygroundDirectory(
await directoryHandleFromMountDevice(mountDescriptor.device)
);
} catch (e) {
Expand Down Expand Up @@ -207,47 +210,3 @@ export function bootSiteClient(
signal.onabort = null;
};
}

/**
* Check if the given directory handle directory is a Playground directory.
*
* @TODO: Create a shared package like @wp-playground/wordpress for such utilities
* and bring in the context detection logic from wp-now – only express it in terms of
* either abstract FS operations or isomorphic PHP FS operations.
* (we can't just use Node.js require('fs') in the browser, for example)
*
* @TODO: Reuse the "isWordPressInstalled" logic implemented in the boot protocol.
* Perhaps mount OPFS first, and only then check for the presence of the
* WordPress installation? Or, if not, perhaps implement a shared file access
* abstraction that can be used both with the PHP module and OPFS directory handles?
*
* @param dirHandle
*/
export async function playgroundAvailableInOpfs(
dirHandle: FileSystemDirectoryHandle
) {
// Run this loop just to trigger an exception if the directory handle is no good.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for await (const _ of dirHandle.keys()) {
break;
}

try {
/**
* Assume it's a Playground directory if these files exist:
* - wp-config.php
* - wp-content/database/.ht.sqlite
*/
await dirHandle.getFileHandle('wp-config.php', { create: false });
const wpContent = await dirHandle.getDirectoryHandle('wp-content', {
create: false,
});
const database = await wpContent.getDirectoryHandle('database', {
create: false,
});
await database.getFileHandle('.ht.sqlite', { create: false });
} catch (e) {
return false;
}
return true;
}

0 comments on commit 9ebeb51

Please sign in to comment.