-
Notifications
You must be signed in to change notification settings - Fork 274
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove DOMParser from Blueprints: installPlugin and installTheme (#427)
## What? The goal of this pull request is to remove the use of any browser-specific API such as `DOMParser` from the Blueprints package. This allows all Blueprint steps to run on the browser and server side. ## Why? Currently, the steps `installPlugin` and `installTheme` can only be run with the Playground in the browser, not on server side with `wp-now` and Node.js, because they use the `asDOM` helper function which depends on the `DOMParser` class only available in the browser. Related discussion: - #379 The initial idea was to introduce an "isomorphic DOM" library that exports native DOM API for the browser and [jsdom](https://github.com/jsdom/jsdom) for Node.js. That would have allowed the existing implementation of `installPlugin` and `installTheme` to work as is on server side. However, after weighing the pros and cons, it was decided that it's simpler to maintain if we rewrite these steps to perform their actions without using any DOM operations. ## How? - Rewrite the Blueprint steps `installPlugin` and `installTheme` to use Playground and PHP WASM API. - Remove the `asDOM` helper function. ## Testing Instructions 1. Check out the branch. 2. Run `npx nx test playground-blueprints`
- Loading branch information
1 parent
424f24b
commit 069930c
Showing
10 changed files
with
402 additions
and
173 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
96 changes: 96 additions & 0 deletions
96
packages/playground/blueprints/src/lib/steps/install-asset.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import type { UniversalPHP } from '@php-wasm/universal'; | ||
import { writeFile } from './client-methods'; | ||
import { unzip } from './import-export'; | ||
|
||
export interface InstallAssetOptions { | ||
/** | ||
* The zip file to install. | ||
*/ | ||
zipFile: File; | ||
/** | ||
* Target path to extract the main folder. | ||
* @example | ||
* | ||
* <code> | ||
* const targetPath = `${await playground.documentRoot}/wp-content/plugins`; | ||
* </code> | ||
*/ | ||
targetPath: string; | ||
} | ||
|
||
/** | ||
* Install asset: Extract folder from zip file and move it to target | ||
*/ | ||
export async function installAsset( | ||
playground: UniversalPHP, | ||
{ targetPath, zipFile }: InstallAssetOptions | ||
): Promise<{ | ||
assetFolderPath: string; | ||
assetFolderName: string; | ||
}> { | ||
// Extract to temporary folder so we can find asset folder name | ||
|
||
const zipFileName = zipFile.name; | ||
const tmpFolder = `/tmp/assets`; | ||
const tmpZipPath = `/tmp/${zipFileName}`; | ||
|
||
const removeTmpFolder = () => | ||
playground.rmdir(tmpFolder, { | ||
recursive: true, | ||
}); | ||
|
||
if (await playground.fileExists(tmpFolder)) { | ||
await removeTmpFolder(); | ||
} | ||
|
||
await writeFile(playground, { | ||
path: tmpZipPath, | ||
data: zipFile, | ||
}); | ||
|
||
const cleanup = () => | ||
Promise.all([removeTmpFolder, () => playground.unlink(tmpZipPath)]); | ||
|
||
try { | ||
await unzip(playground, { | ||
zipPath: tmpZipPath, | ||
extractToPath: tmpFolder, | ||
}); | ||
|
||
// Find extracted asset folder name | ||
|
||
const files = await playground.listFiles(tmpFolder); | ||
|
||
let assetFolderName; | ||
let tmpAssetPath = ''; | ||
|
||
for (const file of files) { | ||
tmpAssetPath = `${tmpFolder}/${file}`; | ||
if (await playground.isDir(tmpAssetPath)) { | ||
assetFolderName = file; | ||
break; | ||
} | ||
} | ||
|
||
if (!assetFolderName) { | ||
throw new Error( | ||
`The zip file should contain a single folder with files inside, but the provided zip file (${zipFileName}) does not contain such a folder.` | ||
); | ||
} | ||
|
||
// Move asset folder to target path | ||
|
||
const assetFolderPath = `${targetPath}/${assetFolderName}`; | ||
await playground.mv(tmpAssetPath, assetFolderPath); | ||
|
||
await cleanup(); | ||
|
||
return { | ||
assetFolderPath, | ||
assetFolderName, | ||
}; | ||
} catch (error) { | ||
await cleanup(); | ||
throw error; | ||
} | ||
} |
66 changes: 66 additions & 0 deletions
66
packages/playground/blueprints/src/lib/steps/install-plugin.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { NodePHP } from '@php-wasm/node'; | ||
import { compileBlueprint, runBlueprintSteps } from '../compile'; | ||
|
||
const phpVersion = '8.0'; | ||
describe('Blueprint step installPlugin', () => { | ||
let php: NodePHP; | ||
beforeEach(async () => { | ||
php = await NodePHP.load(phpVersion, { | ||
requestHandler: { | ||
documentRoot: '/wordpress', | ||
isStaticFilePath: (path) => !path.endsWith('.php'), | ||
}, | ||
}); | ||
}); | ||
|
||
it('should install a plugin', async () => { | ||
// Create test plugin | ||
|
||
const pluginName = 'test-plugin'; | ||
|
||
php.mkdir(`/${pluginName}`); | ||
php.writeFile( | ||
`/${pluginName}/index.php`, | ||
`/**\n * Plugin Name: Test Plugin` | ||
); | ||
|
||
// Note the package name is different from plugin folder name | ||
const zipFileName = `${pluginName}-0.0.1.zip`; | ||
|
||
await php.run({ | ||
code: `<?php $zip = new ZipArchive(); $zip->open("${zipFileName}", ZIPARCHIVE::CREATE); $zip->addFile("/${pluginName}/index.php"); $zip->close();`, | ||
}); | ||
|
||
php.rmdir(`/${pluginName}`); | ||
|
||
expect(php.fileExists(zipFileName)).toBe(true); | ||
|
||
// Create plugins folder | ||
const rootPath = await php.documentRoot; | ||
const pluginsPath = `${rootPath}/wp-content/plugins`; | ||
|
||
php.mkdir(pluginsPath); | ||
|
||
await runBlueprintSteps( | ||
compileBlueprint({ | ||
steps: [ | ||
{ | ||
step: 'installPlugin', | ||
pluginZipFile: { | ||
resource: 'vfs', | ||
path: zipFileName, | ||
}, | ||
options: { | ||
activate: false, | ||
}, | ||
}, | ||
], | ||
}), | ||
php | ||
); | ||
|
||
php.unlink(zipFileName); | ||
|
||
expect(php.fileExists(`${pluginsPath}/${pluginName}`)).toBe(true); | ||
}); | ||
}); |
Oops, something went wrong.