From db5ef9691f41d34a07a832a2cf34d725fb816bb2 Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 09:06:45 +0100 Subject: [PATCH 01/37] blueprints: install JSDOM. DOMParser is not available in Node --- package.json | 2 +- .../blueprints/src/lib/steps/common.ts | 3 +- packages/playground/blueprints/vite.config.ts | 2 +- yarn.lock | 78 ++++++++++++++++++- 4 files changed, 78 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 4656e3ddc7..3f42a996d2 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "jest": "^29.4.1", "jest-environment-jsdom": "^29.4.1", "jest-environment-node": "^29.4.1", - "jsdom": "~20.0.3", + "jsdom": "22.0.0", "jsonc-eslint-parser": "^2.1.0", "lerna": "6.6.1", "nx": "15.8.6", diff --git a/packages/playground/blueprints/src/lib/steps/common.ts b/packages/playground/blueprints/src/lib/steps/common.ts index 4af86c4068..03d05bfec8 100644 --- a/packages/playground/blueprints/src/lib/steps/common.ts +++ b/packages/playground/blueprints/src/lib/steps/common.ts @@ -1,7 +1,8 @@ import type { PHPResponse, UniversalPHP } from '@php-wasm/universal'; +import { JSDOM } from 'jsdom'; export function asDOM(response: PHPResponse) { - return new DOMParser().parseFromString(response.text, 'text/html')!; + return new JSDOM(response.text).window.document; } export function zipNameToHumanName(zipName: string) { diff --git a/packages/playground/blueprints/vite.config.ts b/packages/playground/blueprints/vite.config.ts index caeafbae41..f4dfd8449a 100644 --- a/packages/playground/blueprints/vite.config.ts +++ b/packages/playground/blueprints/vite.config.ts @@ -43,7 +43,7 @@ export default defineConfig({ }, rollupOptions: { // External packages that should not be bundled into your library. - external: [], + external: ['jsdom'], }, }, diff --git a/yarn.lock b/yarn.lock index 5faf587a6a..0f883b916b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7160,6 +7160,13 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" +cssstyle@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a" + integrity sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg== + dependencies: + rrweb-cssom "^0.6.0" + csstype@^2.6.8: version "2.6.21" resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e" @@ -7189,6 +7196,15 @@ data-urls@^3.0.2: whatwg-mimetype "^3.0.0" whatwg-url "^11.0.0" +data-urls@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4" + integrity sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g== + dependencies: + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.0" + date-fns@^2.28.0, date-fns@^2.29.2: version "2.30.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" @@ -7242,7 +7258,7 @@ decamelize@^1.1.0, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== -decimal.js@^10.4.2: +decimal.js@^10.4.2, decimal.js@^10.4.3: version "10.4.3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== @@ -10683,7 +10699,36 @@ js-yaml@^3.10.0, js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -jsdom@^20.0.0, jsdom@~20.0.3: +jsdom@22.0.0: + version "22.0.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-22.0.0.tgz#3295c6992c70089c4b8f5cf060489fddf7ee9816" + integrity sha512-p5ZTEb5h+O+iU02t0GfEjAnkdYPrQSkfuTSMkMYyIoMvUNEHsbG0bHHbfXIcfTqD2UfvjQX7mmgiFsyRwGscVw== + dependencies: + abab "^2.0.6" + cssstyle "^3.0.0" + data-urls "^4.0.0" + decimal.js "^10.4.3" + domexception "^4.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.4" + parse5 "^7.1.2" + rrweb-cssom "^0.6.0" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.1" + ws "^8.13.0" + xml-name-validator "^4.0.0" + +jsdom@^20.0.0: version "20.0.3" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== @@ -12113,6 +12158,11 @@ nwsapi@^2.2.2: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== +nwsapi@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.4.tgz#fd59d5e904e8e1f03c25a7d5a15cfa16c714a1e5" + integrity sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g== + nx@15.8.6: version "15.8.6" resolved "https://registry.yarnpkg.com/nx/-/nx-15.8.6.tgz#ffe9a32b0c4614ec25d7308e3a37455dc386d29f" @@ -12691,7 +12741,7 @@ parse5@4.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== -parse5@^7.0.0, parse5@^7.1.1: +parse5@^7.0.0, parse5@^7.1.1, parse5@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== @@ -13339,7 +13389,7 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^2.1.0, punycode@^2.1.1: +punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== @@ -14010,6 +14060,11 @@ rollup@^3.18.0: optionalDependencies: fsevents "~2.3.2" +rrweb-cssom@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" + integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== + run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -15105,6 +15160,13 @@ tr46@^3.0.0: dependencies: punycode "^2.1.1" +tr46@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469" + integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw== + dependencies: + punycode "^2.3.0" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -16017,6 +16079,14 @@ whatwg-url@^11.0.0: tr46 "^3.0.0" webidl-conversions "^7.0.0" +whatwg-url@^12.0.0, whatwg-url@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c" + integrity sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ== + dependencies: + tr46 "^4.1.1" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" From c7275d32b132f393c90db6bb3b91c5842d32fa35 Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 09:08:02 +0100 Subject: [PATCH 02/37] wp-now: activate plugin and theme the first time --- .../src/lib/steps/activate-theme.ts | 35 ++++++++++++++ .../blueprints/src/lib/steps/handlers.ts | 1 + .../blueprints/src/lib/steps/index.ts | 2 + packages/wp-now/src/wp-now.ts | 27 +++++++++++ .../wp-playground-wordpress/extract-name.ts | 47 +++++++++++++++++++ .../get-plugin-file.ts | 18 +++++++ .../src/wp-playground-wordpress/slugify.ts | 8 ++++ 7 files changed, 138 insertions(+) create mode 100644 packages/playground/blueprints/src/lib/steps/activate-theme.ts create mode 100644 packages/wp-now/src/wp-playground-wordpress/extract-name.ts create mode 100644 packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts create mode 100644 packages/wp-now/src/wp-playground-wordpress/slugify.ts diff --git a/packages/playground/blueprints/src/lib/steps/activate-theme.ts b/packages/playground/blueprints/src/lib/steps/activate-theme.ts new file mode 100644 index 0000000000..8a705f9095 --- /dev/null +++ b/packages/playground/blueprints/src/lib/steps/activate-theme.ts @@ -0,0 +1,35 @@ +import { StepHandler } from '.'; +import { asDOM } from './common'; + +export interface ActivateThemeStep { + step: 'activateTheme'; + themeFolderName: string; +} + +/** + * Activates a WordPress theme in the Playground. + * + * @param playground The playground client. + * @param theme The theme slug. + */ +export const activateTheme: StepHandler = async ( + playground, + { themeFolderName }, + progress +) => { + progress?.tracker.setCaption(`Activating ${themeFolderName}`); + const themesPage = asDOM( + await playground.request({ + url: '/wp-admin/themes.php', + }) + ); + + const link = themesPage.querySelector( + `a[href*="action=activate&stylesheet=${themeFolderName}"]` + )! as HTMLAnchorElement; + const href = link.attributes.getNamedItem('href')!.value; + + await playground.request({ + url: href, + }); +}; diff --git a/packages/playground/blueprints/src/lib/steps/handlers.ts b/packages/playground/blueprints/src/lib/steps/handlers.ts index 9224e1d2ec..1b4fe87c0b 100644 --- a/packages/playground/blueprints/src/lib/steps/handlers.ts +++ b/packages/playground/blueprints/src/lib/steps/handlers.ts @@ -1,4 +1,5 @@ export { activatePlugin } from './activate-plugin'; +export { activateTheme } from './activate-theme'; export { applyWordPressPatches } from './apply-wordpress-patches'; export { rm, diff --git a/packages/playground/blueprints/src/lib/steps/index.ts b/packages/playground/blueprints/src/lib/steps/index.ts index 722c9da850..5772d20392 100644 --- a/packages/playground/blueprints/src/lib/steps/index.ts +++ b/packages/playground/blueprints/src/lib/steps/index.ts @@ -26,6 +26,7 @@ import { WriteFileStep, } from './client-methods'; import { DefineWpConfigConstsStep } from './define-wp-config-consts'; +import { ActivateThemeStep } from './activate-theme'; export type Step = GenericStep; export type StepDefinition = Step & { @@ -37,6 +38,7 @@ export type StepDefinition = Step & { export type GenericStep = | ActivatePluginStep + | ActivateThemeStep | ApplyWordPressPatchesStep | CpStep | DefineWpConfigConstsStep diff --git a/packages/wp-now/src/wp-now.ts b/packages/wp-now/src/wp-now.ts index 53ea811c45..c1ae427ea7 100644 --- a/packages/wp-now/src/wp-now.ts +++ b/packages/wp-now/src/wp-now.ts @@ -21,6 +21,8 @@ import { } from './download'; import { portFinder } from './port-finder'; import { + activatePlugin, + activateTheme, cp, defineSiteUrl, defineWpConfigConsts, @@ -33,6 +35,7 @@ import { isWordPressDirectory, isWordPressDevelopDirectory, } from './wp-playground-wordpress'; +import { extractPluginName } from './wp-playground-wordpress/extract-name'; export const enum WPNowMode { PLUGIN = 'plugin', @@ -136,6 +139,7 @@ export default async function startWPNow( await downloadWordPress(options.wordPressVersion); await downloadSqliteIntegrationPlugin(); await downloadMuPlugins(); + const isFirstTimeProject = !fs.existsSync(options.wpContentPath); switch (options.mode) { case WPNowMode.WP_CONTENT: await runWpContentMode(php, options); @@ -158,6 +162,14 @@ export default async function startWPNow( username: 'admin', password: 'password', }); + + if ( + isFirstTimeProject && + [WPNowMode.PLUGIN, WPNowMode.THEME].includes(options.mode) + ) { + activatePluginOrTheme(php, options); + } + return { php, options, @@ -278,6 +290,21 @@ async function initWordPress( } } +async function activatePluginOrTheme( + php: NodePHP, + { projectPath, mode }: WPNowOptions +) { + if (mode === WPNowMode.PLUGIN) { + const { slug } = extractPluginName(projectPath); + if (slug) { + await activatePlugin(php, { plugin: slug }); + } + } else if (mode === WPNowMode.THEME) { + const themeFolderName = path.basename(projectPath); + await activateTheme(php, { themeFolderName }); + } +} + function mountMuPlugins(php: NodePHP, vfsDocumentRoot: string) { php.mount( path.join(WP_NOW_PATH, 'mu-plugins'), diff --git a/packages/wp-now/src/wp-playground-wordpress/extract-name.ts b/packages/wp-now/src/wp-playground-wordpress/extract-name.ts new file mode 100644 index 0000000000..480455645c --- /dev/null +++ b/packages/wp-now/src/wp-playground-wordpress/extract-name.ts @@ -0,0 +1,47 @@ +import fs from 'fs-extra'; +import path from 'path'; +import { slugify } from './slugify'; +import { getPluginFile } from './get-plugin-file'; + +interface ExtractNameResult { + name: string; + slug: string; +} + +function extractName( + pluginFileContent: string, + nameRegEx: RegExp +): ExtractNameResult | null { + const match = pluginFileContent.match(nameRegEx); + + if (match) { + const name = match[1].trim(); + return { + name, + slug: slugify(name), + }; + } else { + return null; + } +} + +export function extractPluginName(projectPath: string) { + const pluginFile = getPluginFile(projectPath); + if (!pluginFile) { + return null; + } + const pluginFileContent = fs.readFileSync( + path.join(projectPath, pluginFile), + 'utf-8' + ); + return extractName(pluginFileContent, /Plugin Name:\s*(.*)/i); +} + +export function extractThemeName(projectPath: string) { + const styleCssPath = path.join(projectPath, 'style.css'); + if (!fs.existsSync(styleCssPath)) { + return null; + } + const styleCssContent = fs.readFileSync(styleCssPath, 'utf-8'); + return extractName(styleCssContent, /Theme Name:\s*(.*)/i); +} diff --git a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts new file mode 100644 index 0000000000..256b45a433 --- /dev/null +++ b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts @@ -0,0 +1,18 @@ +import fs from 'fs-extra'; +import path from 'path'; + +export function getPluginFile(projectPath: string) { + const files = fs.readdirSync(projectPath); + for (const file of files) { + if (file.endsWith('.php')) { + const fileContent = fs.readFileSync( + path.join(projectPath, file), + 'utf8' + ); + if (fileContent.includes('Plugin Name:')) { + return file; + } + } + } + return null; +} diff --git a/packages/wp-now/src/wp-playground-wordpress/slugify.ts b/packages/wp-now/src/wp-playground-wordpress/slugify.ts new file mode 100644 index 0000000000..0a11c7efd4 --- /dev/null +++ b/packages/wp-now/src/wp-playground-wordpress/slugify.ts @@ -0,0 +1,8 @@ +export function slugify(text: string): string { + return text + .toLowerCase() + .trim() // trim leading and trailing spaces + .replace(/\s+/g, '-') // replace spaces with - + .replace(/[^\w-]+/g, '') // remove all non-word chars + .replace(/--+/g, '-'); // replace multiple - with single - +} From f57d901d99076cec2a128b4c566d12f4b4942dac Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 09:48:40 +0100 Subject: [PATCH 03/37] playground-client: add jsdom as external --- packages/playground/client/vite.config.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/playground/client/vite.config.ts b/packages/playground/client/vite.config.ts index bc578db062..4d347f156d 100644 --- a/packages/playground/client/vite.config.ts +++ b/packages/playground/client/vite.config.ts @@ -30,6 +30,10 @@ export default defineConfig({ fileName: 'index', formats: ['es', 'cjs'], }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: ['jsdom'], + }, }, test: { From 932dd3c7626a9c12a635292cb6fa257a24b2ee26 Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 10:03:29 +0100 Subject: [PATCH 04/37] blueprints: improve activateTheme docblock --- packages/playground/blueprints/src/lib/steps/activate-theme.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/playground/blueprints/src/lib/steps/activate-theme.ts b/packages/playground/blueprints/src/lib/steps/activate-theme.ts index 8a705f9095..33a4532b4d 100644 --- a/packages/playground/blueprints/src/lib/steps/activate-theme.ts +++ b/packages/playground/blueprints/src/lib/steps/activate-theme.ts @@ -10,7 +10,7 @@ export interface ActivateThemeStep { * Activates a WordPress theme in the Playground. * * @param playground The playground client. - * @param theme The theme slug. + * @param themeFolderName The theme folder name. */ export const activateTheme: StepHandler = async ( playground, From e9010afad007b529420f9993e798662265034d8d Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 10:09:17 +0100 Subject: [PATCH 05/37] chore: move jsdom from devDependencies to dependencies in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3f42a996d2..12969f40b4 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "express-fileupload": "1.4.0", "file-saver": "^2.0.5", "follow-redirects": "1.15.2", + "jsdom": "22.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-modal": "^3.16.1", @@ -129,7 +130,6 @@ "jest": "^29.4.1", "jest-environment-jsdom": "^29.4.1", "jest-environment-node": "^29.4.1", - "jsdom": "22.0.0", "jsonc-eslint-parser": "^2.1.0", "lerna": "6.6.1", "nx": "15.8.6", From cb7c70dc81a693a554abe9a8780715d5a9d2e937 Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 12:14:34 +0100 Subject: [PATCH 06/37] wp-now: await unitl the installation finishes --- packages/wp-now/src/wp-now.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wp-now/src/wp-now.ts b/packages/wp-now/src/wp-now.ts index 640ebfdeb0..64bc892ef6 100644 --- a/packages/wp-now/src/wp-now.ts +++ b/packages/wp-now/src/wp-now.ts @@ -168,7 +168,7 @@ export default async function startWPNow( isFirstTimeProject && [WPNowMode.PLUGIN, WPNowMode.THEME].includes(options.mode) ) { - activatePluginOrTheme(php, options); + await activatePluginOrTheme(php, options); } return { From 24821ee5de866878362dcfee531eda8fd22a4113 Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 16:51:31 +0100 Subject: [PATCH 07/37] blueprints: refactor activateTheme to use switch_theme --- .../src/lib/steps/activate-theme.ts | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/packages/playground/blueprints/src/lib/steps/activate-theme.ts b/packages/playground/blueprints/src/lib/steps/activate-theme.ts index 33a4532b4d..7ec85ba67c 100644 --- a/packages/playground/blueprints/src/lib/steps/activate-theme.ts +++ b/packages/playground/blueprints/src/lib/steps/activate-theme.ts @@ -18,18 +18,13 @@ export const activateTheme: StepHandler = async ( progress ) => { progress?.tracker.setCaption(`Activating ${themeFolderName}`); - const themesPage = asDOM( - await playground.request({ - url: '/wp-admin/themes.php', - }) - ); - - const link = themesPage.querySelector( - `a[href*="action=activate&stylesheet=${themeFolderName}"]` - )! as HTMLAnchorElement; - const href = link.attributes.getNamedItem('href')!.value; - - await playground.request({ - url: href, - }); + const wpLoadPath = `${playground.documentRoot}/wp-load.php`; + if (playground.fileExists(wpLoadPath)) { + await playground.run({ + code: ` Date: Tue, 16 May 2023 16:53:01 +0100 Subject: [PATCH 08/37] blueprints: remove unused import for activateTheme --- packages/playground/blueprints/src/lib/steps/activate-theme.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/playground/blueprints/src/lib/steps/activate-theme.ts b/packages/playground/blueprints/src/lib/steps/activate-theme.ts index 7ec85ba67c..17a0fc27c9 100644 --- a/packages/playground/blueprints/src/lib/steps/activate-theme.ts +++ b/packages/playground/blueprints/src/lib/steps/activate-theme.ts @@ -1,5 +1,4 @@ import { StepHandler } from '.'; -import { asDOM } from './common'; export interface ActivateThemeStep { step: 'activateTheme'; From ed28a2be646445f436936510b08206ace597376e Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 17:41:43 +0100 Subject: [PATCH 09/37] blueprints: make asDOM isomorphic conditionally loading jsdom for NodeJS --- .../blueprints/src/lib/steps/activate-plugin.ts | 2 +- packages/playground/blueprints/src/lib/steps/common.ts | 10 +++++++--- .../blueprints/src/lib/steps/install-plugin.ts | 4 ++-- .../blueprints/src/lib/steps/install-theme.ts | 4 ++-- packages/playground/client/vite.config.ts | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/playground/blueprints/src/lib/steps/activate-plugin.ts b/packages/playground/blueprints/src/lib/steps/activate-plugin.ts index 4cf1ed507c..632d661878 100644 --- a/packages/playground/blueprints/src/lib/steps/activate-plugin.ts +++ b/packages/playground/blueprints/src/lib/steps/activate-plugin.ts @@ -18,7 +18,7 @@ export const activatePlugin: StepHandler = async ( progress ) => { progress?.tracker.setCaption(`Activating ${plugin}`); - const pluginsPage = asDOM( + const pluginsPage = await asDOM( await playground.request({ url: '/wp-admin/plugins.php', }) diff --git a/packages/playground/blueprints/src/lib/steps/common.ts b/packages/playground/blueprints/src/lib/steps/common.ts index ebf0e7aab3..779a07bc6e 100644 --- a/packages/playground/blueprints/src/lib/steps/common.ts +++ b/packages/playground/blueprints/src/lib/steps/common.ts @@ -1,8 +1,12 @@ import type { PHPResponse, UniversalPHP } from '@php-wasm/universal'; -import { JSDOM } from 'jsdom'; -export function asDOM(response: PHPResponse) { - return new JSDOM(response.text).window.document; +export async function asDOM(response: PHPResponse) { + if (typeof process !== 'undefined' && process.release?.name === 'node') { + const { JSDOM } = await import('jsdom'); + return new JSDOM(response.text).window.document; + } else { + return new DOMParser().parseFromString(response.text, 'text/html')!; + } } export function zipNameToHumanName(zipName: string) { diff --git a/packages/playground/blueprints/src/lib/steps/install-plugin.ts b/packages/playground/blueprints/src/lib/steps/install-plugin.ts index c90d07d20c..b50d9d3b5a 100644 --- a/packages/playground/blueprints/src/lib/steps/install-plugin.ts +++ b/packages/playground/blueprints/src/lib/steps/install-plugin.ts @@ -63,7 +63,7 @@ export const installPlugin: StepHandler> = async ( const pluginForm = await playground.request({ url: '/wp-admin/plugin-install.php?tab=upload', }); - const pluginFormPage = asDOM(pluginForm); + const pluginFormPage = await asDOM(pluginForm); const pluginFormData = new FormData( pluginFormPage.querySelector('.wp-upload-form')! as HTMLFormElement ) as any; @@ -81,7 +81,7 @@ export const installPlugin: StepHandler> = async ( // Activate if needed if (activate) { - const pluginInstalledPage = asDOM(pluginInstalledResponse); + const pluginInstalledPage = await asDOM(pluginInstalledResponse); const activateButtonHref = pluginInstalledPage .querySelector('#wpbody-content .button.button-primary')! .attributes.getNamedItem('href')!.value; diff --git a/packages/playground/blueprints/src/lib/steps/install-theme.ts b/packages/playground/blueprints/src/lib/steps/install-theme.ts index 294e73ee89..52d48301ad 100644 --- a/packages/playground/blueprints/src/lib/steps/install-theme.ts +++ b/packages/playground/blueprints/src/lib/steps/install-theme.ts @@ -38,7 +38,7 @@ export const installTheme: StepHandler> = async ( const themeForm = await playground.request({ url: '/wp-admin/theme-install.php', }); - const themeFormPage = asDOM(themeForm); + const themeFormPage = await asDOM(themeForm); const themeFormData = new FormData( themeFormPage.querySelector('.wp-upload-form')! as HTMLFormElement ) as any; @@ -56,7 +56,7 @@ export const installTheme: StepHandler> = async ( // Activate if needed if (activate) { - const themeInstalledPage = asDOM(themeInstalledResponse); + const themeInstalledPage = await asDOM(themeInstalledResponse); const messageContainer = themeInstalledPage.querySelector( '#wpbody-content > .wrap' diff --git a/packages/playground/client/vite.config.ts b/packages/playground/client/vite.config.ts index 4d347f156d..c92e768a0e 100644 --- a/packages/playground/client/vite.config.ts +++ b/packages/playground/client/vite.config.ts @@ -32,7 +32,7 @@ export default defineConfig({ }, rollupOptions: { // External packages that should not be bundled into your library. - external: ['jsdom'], + external: [], }, }, From 571ce938f4c0e9ae4e8d3c9e6eadb5f593bdd636 Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 17:45:25 +0100 Subject: [PATCH 10/37] wp-now: remove unused extractThemeName --- .../wp-now/src/wp-playground-wordpress/extract-name.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/wp-now/src/wp-playground-wordpress/extract-name.ts b/packages/wp-now/src/wp-playground-wordpress/extract-name.ts index 480455645c..404e30a363 100644 --- a/packages/wp-now/src/wp-playground-wordpress/extract-name.ts +++ b/packages/wp-now/src/wp-playground-wordpress/extract-name.ts @@ -37,11 +37,3 @@ export function extractPluginName(projectPath: string) { return extractName(pluginFileContent, /Plugin Name:\s*(.*)/i); } -export function extractThemeName(projectPath: string) { - const styleCssPath = path.join(projectPath, 'style.css'); - if (!fs.existsSync(styleCssPath)) { - return null; - } - const styleCssContent = fs.readFileSync(styleCssPath, 'utf-8'); - return extractName(styleCssContent, /Theme Name:\s*(.*)/i); -} From 5a44dca6471fa1b84aeea099c3e592bc4fc9794b Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 18:11:40 +0100 Subject: [PATCH 11/37] fix code style --- packages/wp-now/src/wp-playground-wordpress/extract-name.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/wp-now/src/wp-playground-wordpress/extract-name.ts b/packages/wp-now/src/wp-playground-wordpress/extract-name.ts index 404e30a363..94bd5782a9 100644 --- a/packages/wp-now/src/wp-playground-wordpress/extract-name.ts +++ b/packages/wp-now/src/wp-playground-wordpress/extract-name.ts @@ -36,4 +36,3 @@ export function extractPluginName(projectPath: string) { ); return extractName(pluginFileContent, /Plugin Name:\s*(.*)/i); } - From 4a5b2beed39ef90a9fcbce495979550816244b1a Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 19:27:59 +0100 Subject: [PATCH 12/37] blueprints: refactor activatePlugin to accept path and run php commands --- .../src/lib/steps/activate-plugin.ts | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/playground/blueprints/src/lib/steps/activate-plugin.ts b/packages/playground/blueprints/src/lib/steps/activate-plugin.ts index 632d661878..98e665fab0 100644 --- a/packages/playground/blueprints/src/lib/steps/activate-plugin.ts +++ b/packages/playground/blueprints/src/lib/steps/activate-plugin.ts @@ -1,9 +1,9 @@ import { StepHandler } from '.'; -import { asDOM } from './common'; export interface ActivatePluginStep { step: 'activatePlugin'; - plugin: string; + /* Path to the plugin file relative to the plugins directory. */ + pluginPath: string; } /** @@ -14,22 +14,23 @@ export interface ActivatePluginStep { */ export const activatePlugin: StepHandler = async ( playground, - { plugin }, + { pluginPath }, progress ) => { - progress?.tracker.setCaption(`Activating ${plugin}`); - const pluginsPage = await asDOM( - await playground.request({ - url: '/wp-admin/plugins.php', - }) + progress?.tracker.setCaption(`Activating ${pluginPath}`); + const requiredFiles = [ + `${playground.documentRoot}/wp-load.php`, + `${playground.documentRoot}/wp-admin/includes/plugin.php`, + ]; + const requiredFilesExist = requiredFiles.every((file) => + playground.fileExists(file) ); - - const link = pluginsPage.querySelector( - `tr[data-slug="${plugin}"] a` - )! as HTMLAnchorElement; - const href = link.attributes.getNamedItem('href')!.value; - - await playground.request({ - url: '/wp-admin/' + href, - }); + if (requiredFilesExist) { + await playground.run({ + code: ` `require_once( '${file}' );`).join('\n')} + activate_plugin('${pluginPath}'); + `, + }); + } }; From d814103ef9c3b1200ca921c4fd84c1d5ef79d9de Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 19:28:29 +0100 Subject: [PATCH 13/37] wp-now: refactor plugin activation to use the file path --- packages/wp-now/src/wp-now.ts | 9 +++-- .../wp-playground-wordpress/extract-name.ts | 38 ------------------- .../get-plugin-file.ts | 7 +++- .../src/wp-playground-wordpress/index.ts | 1 + 4 files changed, 12 insertions(+), 43 deletions(-) delete mode 100644 packages/wp-now/src/wp-playground-wordpress/extract-name.ts diff --git a/packages/wp-now/src/wp-now.ts b/packages/wp-now/src/wp-now.ts index ecaca3933d..14aa0bbcdd 100644 --- a/packages/wp-now/src/wp-now.ts +++ b/packages/wp-now/src/wp-now.ts @@ -32,8 +32,8 @@ import { isWpContentDirectory, isWordPressDirectory, isWordPressDevelopDirectory, + getPluginFile, } from './wp-playground-wordpress'; -import { extractPluginName } from './wp-playground-wordpress/extract-name'; import { output } from './output'; export const enum WPNowMode { @@ -298,9 +298,10 @@ async function activatePluginOrTheme( { projectPath, mode }: WPNowOptions ) { if (mode === WPNowMode.PLUGIN) { - const { slug } = extractPluginName(projectPath); - if (slug) { - await activatePlugin(php, { plugin: slug }); + const pluginFile = getPluginFile(projectPath); + console.log('pluginFile', pluginFile); + if (pluginFile) { + await activatePlugin(php, { pluginPath: pluginFile }); } } else if (mode === WPNowMode.THEME) { const themeFolderName = path.basename(projectPath); diff --git a/packages/wp-now/src/wp-playground-wordpress/extract-name.ts b/packages/wp-now/src/wp-playground-wordpress/extract-name.ts deleted file mode 100644 index 94bd5782a9..0000000000 --- a/packages/wp-now/src/wp-playground-wordpress/extract-name.ts +++ /dev/null @@ -1,38 +0,0 @@ -import fs from 'fs-extra'; -import path from 'path'; -import { slugify } from './slugify'; -import { getPluginFile } from './get-plugin-file'; - -interface ExtractNameResult { - name: string; - slug: string; -} - -function extractName( - pluginFileContent: string, - nameRegEx: RegExp -): ExtractNameResult | null { - const match = pluginFileContent.match(nameRegEx); - - if (match) { - const name = match[1].trim(); - return { - name, - slug: slugify(name), - }; - } else { - return null; - } -} - -export function extractPluginName(projectPath: string) { - const pluginFile = getPluginFile(projectPath); - if (!pluginFile) { - return null; - } - const pluginFileContent = fs.readFileSync( - path.join(projectPath, pluginFile), - 'utf-8' - ); - return extractName(pluginFileContent, /Plugin Name:\s*(.*)/i); -} diff --git a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts index 256b45a433..fb15915aaf 100644 --- a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts +++ b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts @@ -1,6 +1,11 @@ import fs from 'fs-extra'; import path from 'path'; +/** + * + * @param projectPath The path to the plugin. + * @returns Path to the plugin file relative to the plugins directory. + */ export function getPluginFile(projectPath: string) { const files = fs.readdirSync(projectPath); for (const file of files) { @@ -10,7 +15,7 @@ export function getPluginFile(projectPath: string) { 'utf8' ); if (fileContent.includes('Plugin Name:')) { - return file; + return path.join(path.basename(projectPath), file); } } } diff --git a/packages/wp-now/src/wp-playground-wordpress/index.ts b/packages/wp-now/src/wp-playground-wordpress/index.ts index ac984df705..bdf7edf02e 100644 --- a/packages/wp-now/src/wp-playground-wordpress/index.ts +++ b/packages/wp-now/src/wp-playground-wordpress/index.ts @@ -4,3 +4,4 @@ export * from './is-theme-directory'; export * from './is-wp-content-directory'; export * from './is-wordpress-directory'; export * from './is-wordpress-develop-directory'; +export * from './get-plugin-file'; From fe116cb49630850c1d8ea0d14cbe71cfebe5dca5 Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 19:33:37 +0100 Subject: [PATCH 14/37] blueprints: undo asDOM function and restore jsdom to devDependencies --- package.json | 2 +- .../blueprints/src/lib/steps/common.ts | 9 +-- .../src/lib/steps/install-plugin.ts | 4 +- .../blueprints/src/lib/steps/install-theme.ts | 4 +- packages/playground/blueprints/vite.config.ts | 2 +- yarn.lock | 78 +------------------ 6 files changed, 12 insertions(+), 87 deletions(-) diff --git a/package.json b/package.json index bdb94d621e..379b18f648 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "express-fileupload": "1.4.0", "file-saver": "^2.0.5", "follow-redirects": "1.15.2", - "jsdom": "22.0.0", + "jsdom": "~20.0.3", "react": "^18.2.0", "react-dom": "^18.2.0", "react-modal": "^3.16.1", diff --git a/packages/playground/blueprints/src/lib/steps/common.ts b/packages/playground/blueprints/src/lib/steps/common.ts index 779a07bc6e..aff3d21e45 100644 --- a/packages/playground/blueprints/src/lib/steps/common.ts +++ b/packages/playground/blueprints/src/lib/steps/common.ts @@ -1,12 +1,7 @@ import type { PHPResponse, UniversalPHP } from '@php-wasm/universal'; -export async function asDOM(response: PHPResponse) { - if (typeof process !== 'undefined' && process.release?.name === 'node') { - const { JSDOM } = await import('jsdom'); - return new JSDOM(response.text).window.document; - } else { - return new DOMParser().parseFromString(response.text, 'text/html')!; - } +export function asDOM(response: PHPResponse) { + return new DOMParser().parseFromString(response.text, 'text/html')!; } export function zipNameToHumanName(zipName: string) { diff --git a/packages/playground/blueprints/src/lib/steps/install-plugin.ts b/packages/playground/blueprints/src/lib/steps/install-plugin.ts index b50d9d3b5a..c90d07d20c 100644 --- a/packages/playground/blueprints/src/lib/steps/install-plugin.ts +++ b/packages/playground/blueprints/src/lib/steps/install-plugin.ts @@ -63,7 +63,7 @@ export const installPlugin: StepHandler> = async ( const pluginForm = await playground.request({ url: '/wp-admin/plugin-install.php?tab=upload', }); - const pluginFormPage = await asDOM(pluginForm); + const pluginFormPage = asDOM(pluginForm); const pluginFormData = new FormData( pluginFormPage.querySelector('.wp-upload-form')! as HTMLFormElement ) as any; @@ -81,7 +81,7 @@ export const installPlugin: StepHandler> = async ( // Activate if needed if (activate) { - const pluginInstalledPage = await asDOM(pluginInstalledResponse); + const pluginInstalledPage = asDOM(pluginInstalledResponse); const activateButtonHref = pluginInstalledPage .querySelector('#wpbody-content .button.button-primary')! .attributes.getNamedItem('href')!.value; diff --git a/packages/playground/blueprints/src/lib/steps/install-theme.ts b/packages/playground/blueprints/src/lib/steps/install-theme.ts index 52d48301ad..294e73ee89 100644 --- a/packages/playground/blueprints/src/lib/steps/install-theme.ts +++ b/packages/playground/blueprints/src/lib/steps/install-theme.ts @@ -38,7 +38,7 @@ export const installTheme: StepHandler> = async ( const themeForm = await playground.request({ url: '/wp-admin/theme-install.php', }); - const themeFormPage = await asDOM(themeForm); + const themeFormPage = asDOM(themeForm); const themeFormData = new FormData( themeFormPage.querySelector('.wp-upload-form')! as HTMLFormElement ) as any; @@ -56,7 +56,7 @@ export const installTheme: StepHandler> = async ( // Activate if needed if (activate) { - const themeInstalledPage = await asDOM(themeInstalledResponse); + const themeInstalledPage = asDOM(themeInstalledResponse); const messageContainer = themeInstalledPage.querySelector( '#wpbody-content > .wrap' diff --git a/packages/playground/blueprints/vite.config.ts b/packages/playground/blueprints/vite.config.ts index f4dfd8449a..caeafbae41 100644 --- a/packages/playground/blueprints/vite.config.ts +++ b/packages/playground/blueprints/vite.config.ts @@ -43,7 +43,7 @@ export default defineConfig({ }, rollupOptions: { // External packages that should not be bundled into your library. - external: ['jsdom'], + external: [], }, }, diff --git a/yarn.lock b/yarn.lock index a43cb5acde..a764b44b3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7160,13 +7160,6 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -cssstyle@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a" - integrity sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg== - dependencies: - rrweb-cssom "^0.6.0" - csstype@^2.6.8: version "2.6.21" resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e" @@ -7196,15 +7189,6 @@ data-urls@^3.0.2: whatwg-mimetype "^3.0.0" whatwg-url "^11.0.0" -data-urls@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4" - integrity sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g== - dependencies: - abab "^2.0.6" - whatwg-mimetype "^3.0.0" - whatwg-url "^12.0.0" - date-fns@^2.28.0, date-fns@^2.29.2: version "2.30.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" @@ -7258,7 +7242,7 @@ decamelize@^1.1.0, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== -decimal.js@^10.4.2, decimal.js@^10.4.3: +decimal.js@^10.4.2: version "10.4.3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== @@ -10704,36 +10688,7 @@ js-yaml@^3.10.0, js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -jsdom@22.0.0: - version "22.0.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-22.0.0.tgz#3295c6992c70089c4b8f5cf060489fddf7ee9816" - integrity sha512-p5ZTEb5h+O+iU02t0GfEjAnkdYPrQSkfuTSMkMYyIoMvUNEHsbG0bHHbfXIcfTqD2UfvjQX7mmgiFsyRwGscVw== - dependencies: - abab "^2.0.6" - cssstyle "^3.0.0" - data-urls "^4.0.0" - decimal.js "^10.4.3" - domexception "^4.0.0" - form-data "^4.0.0" - html-encoding-sniffer "^3.0.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.1" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.4" - parse5 "^7.1.2" - rrweb-cssom "^0.6.0" - saxes "^6.0.0" - symbol-tree "^3.2.4" - tough-cookie "^4.1.2" - w3c-xmlserializer "^4.0.0" - webidl-conversions "^7.0.0" - whatwg-encoding "^2.0.0" - whatwg-mimetype "^3.0.0" - whatwg-url "^12.0.1" - ws "^8.13.0" - xml-name-validator "^4.0.0" - -jsdom@^20.0.0: +jsdom@^20.0.0, jsdom@~20.0.3: version "20.0.3" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== @@ -12163,11 +12118,6 @@ nwsapi@^2.2.2: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== -nwsapi@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.4.tgz#fd59d5e904e8e1f03c25a7d5a15cfa16c714a1e5" - integrity sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g== - nx@15.8.6: version "15.8.6" resolved "https://registry.yarnpkg.com/nx/-/nx-15.8.6.tgz#ffe9a32b0c4614ec25d7308e3a37455dc386d29f" @@ -12746,7 +12696,7 @@ parse5@4.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== -parse5@^7.0.0, parse5@^7.1.1, parse5@^7.1.2: +parse5@^7.0.0, parse5@^7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== @@ -13394,7 +13344,7 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: +punycode@^2.1.0, punycode@^2.1.1: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== @@ -14065,11 +14015,6 @@ rollup@^3.18.0: optionalDependencies: fsevents "~2.3.2" -rrweb-cssom@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" - integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== - run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -15165,13 +15110,6 @@ tr46@^3.0.0: dependencies: punycode "^2.1.1" -tr46@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469" - integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw== - dependencies: - punycode "^2.3.0" - tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -16084,14 +16022,6 @@ whatwg-url@^11.0.0: tr46 "^3.0.0" webidl-conversions "^7.0.0" -whatwg-url@^12.0.0, whatwg-url@^12.0.1: - version "12.0.1" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c" - integrity sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ== - dependencies: - tr46 "^4.1.1" - webidl-conversions "^7.0.0" - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" From d6303969df7913cccf66e4cfe598683062083a04 Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 20:02:46 +0100 Subject: [PATCH 15/37] wp-now: add tests for getPluginFile --- .../tests/get-plugin-file.spec.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 packages/wp-now/src/wp-playground-wordpress/tests/get-plugin-file.spec.ts diff --git a/packages/wp-now/src/wp-playground-wordpress/tests/get-plugin-file.spec.ts b/packages/wp-now/src/wp-playground-wordpress/tests/get-plugin-file.spec.ts new file mode 100644 index 0000000000..927985814e --- /dev/null +++ b/packages/wp-now/src/wp-playground-wordpress/tests/get-plugin-file.spec.ts @@ -0,0 +1,26 @@ +import path from 'path'; +import { getPluginFile } from '../get-plugin-file'; + +const modesPath = path.resolve(__dirname, '..', '..', 'tests', 'mode-examples'); + +describe('getPluginFile', () => { + it('should return the path to the plugin file if it exists', () => { + const pluginFilePath = getPluginFile(path.join(modesPath, 'plugin')); + expect(pluginFilePath).toEqual( + path.join('plugin', 'sample-plugin.php') + ); + }); + + it('should return null if no plugin file is found', () => { + const pluginFilePath = getPluginFile( + path.join(modesPath, 'not-plugin') + ); + expect(pluginFilePath).toBeNull(); + }); + + it('should handle non-existing directories', () => { + expect(() => + getPluginFile(path.resolve('non-existing')) + ).toThrowError(); + }); +}); From 11422b88288dc9d7a096994feb94fe39d5c0a304 Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 20:06:25 +0100 Subject: [PATCH 16/37] wp-now: refactor isPluginDirectory to use getPluginFile --- .../is-plugin-directory.ts | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/packages/wp-now/src/wp-playground-wordpress/is-plugin-directory.ts b/packages/wp-now/src/wp-playground-wordpress/is-plugin-directory.ts index 4e69c33355..6840890f83 100644 --- a/packages/wp-now/src/wp-playground-wordpress/is-plugin-directory.ts +++ b/packages/wp-now/src/wp-playground-wordpress/is-plugin-directory.ts @@ -1,5 +1,4 @@ -import fs from 'fs-extra'; -import path from 'path'; +import { getPluginFile } from './get-plugin-file'; /** * Checks if the given path is a WordPress plugin. @@ -8,17 +7,6 @@ import path from 'path'; * @returns A boolean value indicating whether the project is a WordPress plugin. */ export function isPluginDirectory(projectPath: string): Boolean { - const files = fs.readdirSync(projectPath); - for (const file of files) { - if (file.endsWith('.php')) { - const fileContent = fs.readFileSync( - path.join(projectPath, file), - 'utf8' - ); - if (fileContent.includes('Plugin Name:')) { - return true; - } - } - } - return false; + const pluginFile = getPluginFile(projectPath); + return pluginFile !== null; } From 87fdf1f0853307845b5f7b96c4793f588885eddf Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 20:09:26 +0100 Subject: [PATCH 17/37] playground: undo unintended changes --- package.json | 2 +- packages/playground/client/vite.config.ts | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/package.json b/package.json index 379b18f648..20901bb9ff 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,6 @@ "express-fileupload": "1.4.0", "file-saver": "^2.0.5", "follow-redirects": "1.15.2", - "jsdom": "~20.0.3", "react": "^18.2.0", "react-dom": "^18.2.0", "react-modal": "^3.16.1", @@ -133,6 +132,7 @@ "jest": "^29.4.1", "jest-environment-jsdom": "^29.4.1", "jest-environment-node": "^29.4.1", + "jsdom": "~20.0.3", "jsonc-eslint-parser": "^2.1.0", "lerna": "6.6.1", "nx": "15.8.6", diff --git a/packages/playground/client/vite.config.ts b/packages/playground/client/vite.config.ts index c92e768a0e..bc578db062 100644 --- a/packages/playground/client/vite.config.ts +++ b/packages/playground/client/vite.config.ts @@ -30,10 +30,6 @@ export default defineConfig({ fileName: 'index', formats: ['es', 'cjs'], }, - rollupOptions: { - // External packages that should not be bundled into your library. - external: [], - }, }, test: { From 83a794f2e88a555e4be2fa04ce2bba7a84dd3861 Mon Sep 17 00:00:00 2001 From: sejas Date: Tue, 16 May 2023 20:10:15 +0100 Subject: [PATCH 18/37] wp-now: remove unused slugify --- packages/wp-now/src/wp-playground-wordpress/slugify.ts | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 packages/wp-now/src/wp-playground-wordpress/slugify.ts diff --git a/packages/wp-now/src/wp-playground-wordpress/slugify.ts b/packages/wp-now/src/wp-playground-wordpress/slugify.ts deleted file mode 100644 index 0a11c7efd4..0000000000 --- a/packages/wp-now/src/wp-playground-wordpress/slugify.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function slugify(text: string): string { - return text - .toLowerCase() - .trim() // trim leading and trailing spaces - .replace(/\s+/g, '-') // replace spaces with - - .replace(/[^\w-]+/g, '') // remove all non-word chars - .replace(/--+/g, '-'); // replace multiple - with single - -} From 28b2bac9937e6017759e99e61a09db749dada988 Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 10:43:49 +0100 Subject: [PATCH 19/37] wp-now: remove pluginFile console log --- packages/wp-now/src/wp-now.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/wp-now/src/wp-now.ts b/packages/wp-now/src/wp-now.ts index 14aa0bbcdd..53c38f6a5f 100644 --- a/packages/wp-now/src/wp-now.ts +++ b/packages/wp-now/src/wp-now.ts @@ -299,7 +299,6 @@ async function activatePluginOrTheme( ) { if (mode === WPNowMode.PLUGIN) { const pluginFile = getPluginFile(projectPath); - console.log('pluginFile', pluginFile); if (pluginFile) { await activatePlugin(php, { pluginPath: pluginFile }); } From 187199106fc071e8541e2a315affb9d8b614be3e Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 10:48:21 +0100 Subject: [PATCH 20/37] wp-now: display plugin file not found and avoid activate the plugin --- packages/wp-now/src/wp-now.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/wp-now/src/wp-now.ts b/packages/wp-now/src/wp-now.ts index 53c38f6a5f..7ce7e052c4 100644 --- a/packages/wp-now/src/wp-now.ts +++ b/packages/wp-now/src/wp-now.ts @@ -301,6 +301,8 @@ async function activatePluginOrTheme( const pluginFile = getPluginFile(projectPath); if (pluginFile) { await activatePlugin(php, { pluginPath: pluginFile }); + } else { + output?.log(`Error: plugin file not found in ${projectPath}`); } } else if (mode === WPNowMode.THEME) { const themeFolderName = path.basename(projectPath); From 91e488838f5aff75633aba8d2f97e8b220129362 Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 10:55:14 +0100 Subject: [PATCH 21/37] wp-now: getPluginFile use regular expression to match lower cases --- packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts index fb15915aaf..8c7d177ad2 100644 --- a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts +++ b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts @@ -14,7 +14,8 @@ export function getPluginFile(projectPath: string) { path.join(projectPath, file), 'utf8' ); - if (fileContent.includes('Plugin Name:')) { + const pluginNameRegex = /plugin name:/i; + if (pluginNameRegex.test(fileContent)) { return path.join(path.basename(projectPath), file); } } From e07ca89dce9bf7e83a7e4073e03efc11c632f49d Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 11:14:03 +0100 Subject: [PATCH 22/37] wp-now: limit plugin search to first 4KB of the file Related to: https://github.com/WordPress/wordpress-playground/issues/227 --- .../get-plugin-file.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts index 8c7d177ad2..7072a38c9b 100644 --- a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts +++ b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts @@ -1,6 +1,21 @@ import fs from 'fs-extra'; import path from 'path'; +/** + * + * @param filePath The path to the file to read. + * @param length The number of bytes to read from the file. By default 4KB. + * @returns The first `length` bytes of the file as string. + */ +function readFileHead(filePath: string, length = 4096) { + const buffer = Buffer.alloc(length); + const fd = fs.openSync(filePath, 'r'); + fs.readSync(fd, buffer, 0, buffer.length, 0); + const fileContentBuffer = buffer.toString('utf8'); + fs.closeSync(fd); + return fileContentBuffer.toString(); +} + /** * * @param projectPath The path to the plugin. @@ -10,10 +25,7 @@ export function getPluginFile(projectPath: string) { const files = fs.readdirSync(projectPath); for (const file of files) { if (file.endsWith('.php')) { - const fileContent = fs.readFileSync( - path.join(projectPath, file), - 'utf8' - ); + const fileContent = readFileHead(path.join(projectPath, file)); const pluginNameRegex = /plugin name:/i; if (pluginNameRegex.test(fileContent)) { return path.join(path.basename(projectPath), file); From b9fe2c0d31ef5b44ecf26e32cd2d33f4bc9f63a9 Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 11:31:43 +0100 Subject: [PATCH 23/37] wp-now: search to be inside a comment block on getPluginFile --- packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts index 7072a38c9b..668b4eb8b5 100644 --- a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts +++ b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts @@ -26,7 +26,7 @@ export function getPluginFile(projectPath: string) { for (const file of files) { if (file.endsWith('.php')) { const fileContent = readFileHead(path.join(projectPath, file)); - const pluginNameRegex = /plugin name:/i; + const pluginNameRegex = /\*[\s]*Plugin name:\s*/i; if (pluginNameRegex.test(fileContent)) { return path.join(path.basename(projectPath), file); } From ca5e41c4edb3606c351dd1430421d0e295f36ab0 Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 11:44:12 +0100 Subject: [PATCH 24/37] wp-now: add heuristicSort to getPluginFile --- .../get-plugin-file.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts index 668b4eb8b5..6217ad6208 100644 --- a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts +++ b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts @@ -1,5 +1,5 @@ import fs from 'fs-extra'; -import path from 'path'; +import path, { basename } from 'path'; /** * @@ -16,13 +16,29 @@ function readFileHead(filePath: string, length = 4096) { return fileContentBuffer.toString(); } +/** + * Sorts the files in the given array using a heuristic. + * The plugin file usually has the same name as the project folder. + * @param files The files to sort. + * @returns The sorted files. + */ +function heuristicSort(files: string[], projectPath: string) { + const heuristicsBestGuess = `${basename(projectPath)}.php`; + const heuristicsBestGuessIndex = files.indexOf(heuristicsBestGuess); + if (heuristicsBestGuessIndex !== -1) { + files.splice(heuristicsBestGuessIndex, 1); + files.unshift(heuristicsBestGuess); + } + return files; +} + /** * * @param projectPath The path to the plugin. * @returns Path to the plugin file relative to the plugins directory. */ export function getPluginFile(projectPath: string) { - const files = fs.readdirSync(projectPath); + const files = heuristicSort(fs.readdirSync(projectPath), projectPath); for (const file of files) { if (file.endsWith('.php')) { const fileContent = readFileHead(path.join(projectPath, file)); From 659d75ecda100eaa5732d9cdcb800d4bebff944e Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 11:52:15 +0100 Subject: [PATCH 25/37] wp-now: remove condition before activatePlugin. Currently in plugin mode there will be always a pluginFile auto detected. --- packages/wp-now/src/wp-now.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/wp-now/src/wp-now.ts b/packages/wp-now/src/wp-now.ts index 7ce7e052c4..c4ce9b2c24 100644 --- a/packages/wp-now/src/wp-now.ts +++ b/packages/wp-now/src/wp-now.ts @@ -299,11 +299,7 @@ async function activatePluginOrTheme( ) { if (mode === WPNowMode.PLUGIN) { const pluginFile = getPluginFile(projectPath); - if (pluginFile) { - await activatePlugin(php, { pluginPath: pluginFile }); - } else { - output?.log(`Error: plugin file not found in ${projectPath}`); - } + await activatePlugin(php, { pluginPath: pluginFile }); } else if (mode === WPNowMode.THEME) { const themeFolderName = path.basename(projectPath); await activateTheme(php, { themeFolderName }); From d161ef447ee58cebae78500bc0174e3ff57bf055 Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 11:59:37 +0100 Subject: [PATCH 26/37] blueprints: throw error if required files to activate plugin or theme were not found - See: https://github.com/WordPress/wordpress-playground/pull/351#discussion_r1195707409 --- .../playground/blueprints/src/lib/steps/activate-plugin.ts | 4 ++++ .../playground/blueprints/src/lib/steps/activate-theme.ts | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/playground/blueprints/src/lib/steps/activate-plugin.ts b/packages/playground/blueprints/src/lib/steps/activate-plugin.ts index 98e665fab0..5a6eff26e7 100644 --- a/packages/playground/blueprints/src/lib/steps/activate-plugin.ts +++ b/packages/playground/blueprints/src/lib/steps/activate-plugin.ts @@ -32,5 +32,9 @@ export const activatePlugin: StepHandler = async ( activate_plugin('${pluginPath}'); `, }); + } else { + throw new Error( + `Required WordPress files do not exist: ${requiredFiles.join(', ')}` + ); } }; diff --git a/packages/playground/blueprints/src/lib/steps/activate-theme.ts b/packages/playground/blueprints/src/lib/steps/activate-theme.ts index 17a0fc27c9..dc1be5db24 100644 --- a/packages/playground/blueprints/src/lib/steps/activate-theme.ts +++ b/packages/playground/blueprints/src/lib/steps/activate-theme.ts @@ -21,9 +21,13 @@ export const activateTheme: StepHandler = async ( if (playground.fileExists(wpLoadPath)) { await playground.run({ code: ` Date: Wed, 17 May 2023 12:38:35 +0100 Subject: [PATCH 27/37] wp-now: add auto activateTheme activatePlugin integration tests --- .../src/tests/mode-examples/theme/style.css | 2 +- packages/wp-now/src/tests/wp-now.spec.ts | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/packages/wp-now/src/tests/mode-examples/theme/style.css b/packages/wp-now/src/tests/mode-examples/theme/style.css index 74e8804cc1..97d1186a3c 100644 --- a/packages/wp-now/src/tests/mode-examples/theme/style.css +++ b/packages/wp-now/src/tests/mode-examples/theme/style.css @@ -1,5 +1,5 @@ /* -Theme Name: Twenty Twenty +Theme Name: Yolo Theme Text Domain: twentytwenty Version: 2.2 Tested up to: 6.2 diff --git a/packages/wp-now/src/tests/wp-now.spec.ts b/packages/wp-now/src/tests/wp-now.spec.ts index 2f77735b1d..94ab3c6337 100644 --- a/packages/wp-now/src/tests/wp-now.spec.ts +++ b/packages/wp-now/src/tests/wp-now.spec.ts @@ -318,4 +318,46 @@ describe('Test starting different modes', () => { expectRequiredRootFiles(requiredFiles, wpNowOptions.documentRoot, php); }); + + /** + * Test that startWPNow in "plugin" mode auto installs the plugin. + */ + test('startWPNow auto installs the plugin', async () => { + const projectPath = path.join(tmpExampleDirectory, 'plugin'); + const { php } = await startWPNow({ projectPath }); + const codeIsPluginActivePhp = ` { + const projectPath = path.join(tmpExampleDirectory, 'theme'); + const { php } = await startWPNow({ projectPath }); + const codeActiveThemeNamePhp = `get('Name'); + `; + const themeName = await php.run({ + code: codeActiveThemeNamePhp, + }); + + expect(themeName.text).toContain('Yolo Theme'); + }); }); From 4438ed0817bdd12b26f2f942bc6904f5bc230fe4 Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 12:51:01 +0100 Subject: [PATCH 28/37] wp-now: include tests for second time the plugin should not be installed --- packages/wp-now/src/tests/wp-now.spec.ts | 33 ++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/wp-now/src/tests/wp-now.spec.ts b/packages/wp-now/src/tests/wp-now.spec.ts index 94ab3c6337..edb605b90d 100644 --- a/packages/wp-now/src/tests/wp-now.spec.ts +++ b/packages/wp-now/src/tests/wp-now.spec.ts @@ -331,8 +331,6 @@ describe('Test starting different modes', () => { if (is_plugin_active('plugin/sample-plugin.php')) { echo 'plugin/sample-plugin.php is active'; - } else { - echo 'plugin not active'; } `; const isPluginActive = await php.run({ @@ -344,6 +342,37 @@ describe('Test starting different modes', () => { ); }); + /** + * Test that startWPNow in "plugin" mode does not auto install the plugin the second time. + */ + test('startWPNow auto installs the plugin', async () => { + const projectPath = path.join(tmpExampleDirectory, 'plugin'); + const { php } = await startWPNow({ projectPath }); + const deactivatePluginPhp = ` Date: Wed, 17 May 2023 12:55:40 +0100 Subject: [PATCH 29/37] wp-now: include tests for second time the theme should not be installed --- packages/wp-now/src/tests/wp-now.spec.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/wp-now/src/tests/wp-now.spec.ts b/packages/wp-now/src/tests/wp-now.spec.ts index edb605b90d..ecc8a61ee8 100644 --- a/packages/wp-now/src/tests/wp-now.spec.ts +++ b/packages/wp-now/src/tests/wp-now.spec.ts @@ -389,4 +389,28 @@ describe('Test starting different modes', () => { expect(themeName.text).toContain('Yolo Theme'); }); + + /** + * Test that startWPNow in "theme" mode does not auto activate the theme the second time. + */ + test('startWPNow auto installs the theme', async () => { + const projectPath = path.join(tmpExampleDirectory, 'theme'); + const { php } = await startWPNow({ projectPath }); + const switchThemePhp = `get('Name'); + `; + const themeName = await phpSecondTime.run({ + code: codeActiveThemeNamePhp, + }); + + expect(themeName.text).toContain('Twenty Twenty-Three'); + }); }); From 2db98e99d483020c5006520ebc9ef8626840bca0 Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 15:23:06 +0100 Subject: [PATCH 30/37] wp-now: update getPluginFile regex --- packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts index 6217ad6208..7e2cfa499d 100644 --- a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts +++ b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts @@ -42,7 +42,7 @@ export function getPluginFile(projectPath: string) { for (const file of files) { if (file.endsWith('.php')) { const fileContent = readFileHead(path.join(projectPath, file)); - const pluginNameRegex = /\*[\s]*Plugin name:\s*/i; + const pluginNameRegex = /\/\*[\s\S]*?Plugin Name:[\s\S]*?\*\//i; if (pluginNameRegex.test(fileContent)) { return path.join(path.basename(projectPath), file); } From f719eba118c23547682f74be8a388559fba47693 Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 15:32:44 +0100 Subject: [PATCH 31/37] wp-now: refactor activate plugin/theme tests to use getWpNowConfig --- packages/wp-now/src/tests/wp-now.spec.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/wp-now/src/tests/wp-now.spec.ts b/packages/wp-now/src/tests/wp-now.spec.ts index acd4d04a6a..e71d4e41f5 100644 --- a/packages/wp-now/src/tests/wp-now.spec.ts +++ b/packages/wp-now/src/tests/wp-now.spec.ts @@ -390,7 +390,8 @@ describe('Test starting different modes', () => { */ test('startWPNow auto installs the plugin', async () => { const projectPath = path.join(tmpExampleDirectory, 'plugin'); - const { php } = await startWPNow({ projectPath }); + const options = await getWpNowConfig({ path: projectPath }); + const { php } = await startWPNow(options); const codeIsPluginActivePhp = ` { */ test('startWPNow auto installs the plugin', async () => { const projectPath = path.join(tmpExampleDirectory, 'plugin'); - const { php } = await startWPNow({ projectPath }); + const options = await getWpNowConfig({ path: projectPath }); + const { php } = await startWPNow(options); const deactivatePluginPhp = ` { `; await php.run({ code: deactivatePluginPhp }); // Run startWPNow a second time. - const { php: phpSecondTime } = await startWPNow({ projectPath }); + const { php: phpSecondTime } = await startWPNow(options); const codeIsPluginActivePhp = ` { */ test('startWPNow auto installs the theme', async () => { const projectPath = path.join(tmpExampleDirectory, 'theme'); - const { php } = await startWPNow({ projectPath }); + const options = await getWpNowConfig({ path: projectPath }); + const { php } = await startWPNow(options); const codeActiveThemeNamePhp = `get('Name'); @@ -461,14 +464,15 @@ describe('Test starting different modes', () => { */ test('startWPNow auto installs the theme', async () => { const projectPath = path.join(tmpExampleDirectory, 'theme'); - const { php } = await startWPNow({ projectPath }); + const options = await getWpNowConfig({ path: projectPath }); + const { php } = await startWPNow(options); const switchThemePhp = `get('Name'); From 7eb976786ac40dd0a6ac582ffe068dd04ec13d70 Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 15:40:56 +0100 Subject: [PATCH 32/37] blueprints: add guard statements for activatePlugin and activateTheme --- .../blueprints/src/lib/steps/activate-plugin.ts | 13 ++++++------- .../blueprints/src/lib/steps/activate-theme.ts | 15 +++++++-------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/playground/blueprints/src/lib/steps/activate-plugin.ts b/packages/playground/blueprints/src/lib/steps/activate-plugin.ts index 5a6eff26e7..6379ebc45e 100644 --- a/packages/playground/blueprints/src/lib/steps/activate-plugin.ts +++ b/packages/playground/blueprints/src/lib/steps/activate-plugin.ts @@ -26,15 +26,14 @@ export const activatePlugin: StepHandler = async ( playground.fileExists(file) ); if (requiredFilesExist) { - await playground.run({ - code: ` `require_once( '${file}' );`).join('\n')} - activate_plugin('${pluginPath}'); - `, - }); - } else { throw new Error( `Required WordPress files do not exist: ${requiredFiles.join(', ')}` ); } + await playground.run({ + code: ` `require_once( '${file}' );`).join('\n')} + activate_plugin('${pluginPath}'); + `, + }); }; diff --git a/packages/playground/blueprints/src/lib/steps/activate-theme.ts b/packages/playground/blueprints/src/lib/steps/activate-theme.ts index dc1be5db24..009194e4d0 100644 --- a/packages/playground/blueprints/src/lib/steps/activate-theme.ts +++ b/packages/playground/blueprints/src/lib/steps/activate-theme.ts @@ -18,16 +18,15 @@ export const activateTheme: StepHandler = async ( ) => { progress?.tracker.setCaption(`Activating ${themeFolderName}`); const wpLoadPath = `${playground.documentRoot}/wp-load.php`; - if (playground.fileExists(wpLoadPath)) { - await playground.run({ - code: ` Date: Wed, 17 May 2023 17:14:00 +0100 Subject: [PATCH 33/37] wp-now: increase readFileHead to 8KB --- .../wp-now/src/wp-playground-wordpress/get-plugin-file.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts index 7e2cfa499d..242820773b 100644 --- a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts +++ b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts @@ -4,10 +4,11 @@ import path, { basename } from 'path'; /** * * @param filePath The path to the file to read. - * @param length The number of bytes to read from the file. By default 4KB. + * @param length The number of bytes to read from the file. By default 8KB. * @returns The first `length` bytes of the file as string. + * @see https://developer.wordpress.org/reference/functions/get_file_data/ */ -function readFileHead(filePath: string, length = 4096) { +function readFileHead(filePath: string, length = 8192) { const buffer = Buffer.alloc(length); const fd = fs.openSync(filePath, 'r'); fs.readSync(fd, buffer, 0, buffer.length, 0); From 49818b08af1c544a8bfe8911460d57fab167f4e3 Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 17:37:31 +0100 Subject: [PATCH 34/37] wp-now: use WordPress core RegEx to match Plugin name Regex based on https://github.com/WordPress/WordPress/blob/95bcde947f74dfb3cc2d57c624af5d904cf28b6c/wp-includes/functions.php#L6688 --- packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts index 242820773b..43ddfa6735 100644 --- a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts +++ b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts @@ -43,7 +43,8 @@ export function getPluginFile(projectPath: string) { for (const file of files) { if (file.endsWith('.php')) { const fileContent = readFileHead(path.join(projectPath, file)); - const pluginNameRegex = /\/\*[\s\S]*?Plugin Name:[\s\S]*?\*\//i; + const pluginNameRegex = + /^(?:[ \t]*<\?php)?[ \t/*#@]*Plugin Name:(.*)$/im; if (pluginNameRegex.test(fileContent)) { return path.join(path.basename(projectPath), file); } From ff48d9d323971044618e8cb7afb1ee0e92759aa8 Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 17:44:59 +0100 Subject: [PATCH 35/37] wp-now: refactor readFileHead to independent file --- .../wp-playground-wordpress/get-plugin-file.ts | 17 +---------------- .../wp-now/src/wp-playground-wordpress/index.ts | 1 + .../wp-playground-wordpress/read-file-head.ts | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 16 deletions(-) create mode 100644 packages/wp-now/src/wp-playground-wordpress/read-file-head.ts diff --git a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts index 43ddfa6735..4af44c1c2d 100644 --- a/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts +++ b/packages/wp-now/src/wp-playground-wordpress/get-plugin-file.ts @@ -1,21 +1,6 @@ import fs from 'fs-extra'; import path, { basename } from 'path'; - -/** - * - * @param filePath The path to the file to read. - * @param length The number of bytes to read from the file. By default 8KB. - * @returns The first `length` bytes of the file as string. - * @see https://developer.wordpress.org/reference/functions/get_file_data/ - */ -function readFileHead(filePath: string, length = 8192) { - const buffer = Buffer.alloc(length); - const fd = fs.openSync(filePath, 'r'); - fs.readSync(fd, buffer, 0, buffer.length, 0); - const fileContentBuffer = buffer.toString('utf8'); - fs.closeSync(fd); - return fileContentBuffer.toString(); -} +import { readFileHead } from './read-file-head'; /** * Sorts the files in the given array using a heuristic. diff --git a/packages/wp-now/src/wp-playground-wordpress/index.ts b/packages/wp-now/src/wp-playground-wordpress/index.ts index bdf7edf02e..20be9f17f1 100644 --- a/packages/wp-now/src/wp-playground-wordpress/index.ts +++ b/packages/wp-now/src/wp-playground-wordpress/index.ts @@ -5,3 +5,4 @@ export * from './is-wp-content-directory'; export * from './is-wordpress-directory'; export * from './is-wordpress-develop-directory'; export * from './get-plugin-file'; +export * from './read-file-head'; diff --git a/packages/wp-now/src/wp-playground-wordpress/read-file-head.ts b/packages/wp-now/src/wp-playground-wordpress/read-file-head.ts new file mode 100644 index 0000000000..c9f308e86a --- /dev/null +++ b/packages/wp-now/src/wp-playground-wordpress/read-file-head.ts @@ -0,0 +1,17 @@ +import fs from 'fs-extra'; + +/** + * + * @param filePath The path to the file to read. + * @param length The number of bytes to read from the file. By default 8KB. + * @returns The first `length` bytes of the file as string. + * @see https://developer.wordpress.org/reference/functions/get_file_data/ + */ +export function readFileHead(filePath: string, length = 8192) { + const buffer = Buffer.alloc(length); + const fd = fs.openSync(filePath, 'r'); + fs.readSync(fd, buffer, 0, buffer.length, 0); + const fileContentBuffer = buffer.toString('utf8'); + fs.closeSync(fd); + return fileContentBuffer.toString(); +} From a829a6a099da09f9e9e63c5bfc4e14243d8664fb Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 17:48:25 +0100 Subject: [PATCH 36/37] wp-now: improve theme inference by using same core regex Regex based on https://github.com/WordPress/WordPress/blob/95bcde947f74dfb3cc2d57c624af5d904cf28b6c/wp-includes/functions.php#L6688 --- .../src/wp-playground-wordpress/is-theme-directory.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/wp-now/src/wp-playground-wordpress/is-theme-directory.ts b/packages/wp-now/src/wp-playground-wordpress/is-theme-directory.ts index 60633f0236..2d7d5c3e8f 100644 --- a/packages/wp-now/src/wp-playground-wordpress/is-theme-directory.ts +++ b/packages/wp-now/src/wp-playground-wordpress/is-theme-directory.ts @@ -1,5 +1,6 @@ import fs from 'fs-extra'; import path from 'path'; +import { readFileHead } from './read-file-head'; /** * Checks if the given path is a WordPress theme directory. @@ -12,9 +13,7 @@ export function isThemeDirectory(projectPath: string): Boolean { if (!styleCSSExists) { return false; } - const styleCSS = fs.readFileSync( - path.join(projectPath, 'style.css'), - 'utf-8' - ); - return styleCSS.includes('Theme Name:'); + const styleCSS = readFileHead(path.join(projectPath, 'style.css')); + const themeNameRegex = /^(?:[ \t]*<\?php)?[ \t/*#@]*Theme Name:(.*)$/im; + return themeNameRegex.test(styleCSS); } From f736d005a48aec9e42a19dad139ac0996018e9f2 Mon Sep 17 00:00:00 2001 From: sejas Date: Wed, 17 May 2023 18:18:58 +0100 Subject: [PATCH 37/37] blueprints: fix condition on activatePlugin --- packages/playground/blueprints/src/lib/steps/activate-plugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/playground/blueprints/src/lib/steps/activate-plugin.ts b/packages/playground/blueprints/src/lib/steps/activate-plugin.ts index 6379ebc45e..6d8c5237c2 100644 --- a/packages/playground/blueprints/src/lib/steps/activate-plugin.ts +++ b/packages/playground/blueprints/src/lib/steps/activate-plugin.ts @@ -25,7 +25,7 @@ export const activatePlugin: StepHandler = async ( const requiredFilesExist = requiredFiles.every((file) => playground.fileExists(file) ); - if (requiredFilesExist) { + if (!requiredFilesExist) { throw new Error( `Required WordPress files do not exist: ${requiredFiles.join(', ')}` );