@wp-operations/wp-app 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -59,4 +59,4 @@ Auto-detected from the project directory, or forced with `--mode`:
59
59
 
60
60
  ## Credits & License
61
61
 
62
- GPL-2.0-or-later. Continues the workflow of [`@wp-now/wp-now`](https://github.com/WordPress/playground-tools) by the WordPress contributors, rebuilt on the current [WordPress Playground](https://github.com/WordPress/wordpress-playground) engine (`@php-wasm/*`, `@wp-playground/wordpress`).
62
+ GPL-2.0-or-later. Continues the workflow of [`@wp-now/wp-now`](https://github.com/WordPress/playground-tools) by the WordPress contributors, rebuilt on `@php-wasm/*`.
package/dist/index.js CHANGED
@@ -12,17 +12,165 @@ function getCodeSpaceURL(port) {
12
12
  // src/engine.ts
13
13
  import fs10 from "fs";
14
14
  import path9 from "path";
15
- import { createNodeFsMountHandler, loadNodeRuntime } from "@php-wasm/node";
16
- import { bootWordPressAndRequestHandler } from "@wp-playground/wordpress";
15
+ import { createNodeFsMountHandler } from "@php-wasm/node";
16
+
17
+ // src/boot.ts
18
+ import { rootCertificates } from "tls";
19
+ import { loadNodeRuntime } from "@php-wasm/node";
20
+ import { PHP, PHPRequestHandler, setPhpIniEntries } from "@php-wasm/universal";
21
+ var CA_BUNDLE_PATH = "/internal/ca-bundle.crt";
22
+ async function createPhp(phpVersion) {
23
+ const php = new PHP(
24
+ await loadNodeRuntime(phpVersion, {
25
+ emscriptenOptions: { processId: process.pid }
26
+ })
27
+ );
28
+ php.setSapiName("cli");
29
+ php.mkdir("/internal");
30
+ php.writeFile(CA_BUNDLE_PATH, rootCertificates.join("\n"));
31
+ await setPhpIniEntries(php, {
32
+ "openssl.cafile": CA_BUNDLE_PATH,
33
+ "curl.cainfo": CA_BUNDLE_PATH,
34
+ allow_url_fopen: "1",
35
+ disable_functions: ""
36
+ });
37
+ return php;
38
+ }
39
+ function createRequestHandler(php, documentRoot, absoluteUrl) {
40
+ return new PHPRequestHandler({ php, documentRoot, absoluteUrl });
41
+ }
42
+ async function runPhp(php, code) {
43
+ const response = await php.run({ code: `<?php ${code}` });
44
+ if (response.exitCode !== 0) {
45
+ throw new Error(response.errors || `PHP exited ${response.exitCode}`);
46
+ }
47
+ return response;
48
+ }
49
+ async function unzipTo(php, zip, destination) {
50
+ const tmpZip = `/internal/upload-${Date.now()}.zip`;
51
+ php.writeFile(tmpZip, new Uint8Array(await zip.arrayBuffer()));
52
+ await runPhp(
53
+ php,
54
+ `
55
+ $zip = new ZipArchive();
56
+ if ($zip->open(${str(tmpZip)}) !== true) exit(1);
57
+ if (!is_dir(${str(destination)})) mkdir(${str(destination)}, 0777, true);
58
+ if (!$zip->extractTo(${str(destination)})) exit(1);
59
+ $zip->close();
60
+ unlink(${str(tmpZip)});
61
+ `
62
+ );
63
+ }
64
+ async function installCore(php, coreZip, docroot) {
65
+ const staging = `${docroot}/.wpapp-staging`;
66
+ await unzipTo(php, coreZip, staging);
67
+ await runPhp(
68
+ php,
69
+ `
70
+ function wpapp_core_root($dir) {
71
+ if (file_exists("$dir/wp-config-sample.php")) return $dir;
72
+ foreach (scandir($dir) as $entry) {
73
+ if ($entry === '.' || $entry === '..') continue;
74
+ $candidate = "$dir/$entry";
75
+ if (is_dir($candidate) && file_exists("$candidate/wp-config-sample.php")) {
76
+ return $candidate;
77
+ }
78
+ }
79
+ exit(1);
80
+ }
81
+ function wpapp_rrmdir($dir) {
82
+ foreach (scandir($dir) as $entry) {
83
+ if ($entry === '.' || $entry === '..') continue;
84
+ $path = "$dir/$entry";
85
+ is_dir($path) ? wpapp_rrmdir($path) : unlink($path);
86
+ }
87
+ rmdir($dir);
88
+ }
89
+ $root = wpapp_core_root(${str(staging)});
90
+ foreach (scandir($root) as $entry) {
91
+ if ($entry === '.' || $entry === '..') continue;
92
+ rename("$root/$entry", ${str(docroot)} . "/$entry");
93
+ }
94
+ wpapp_rrmdir(${str(staging)});
95
+ if (!file_exists(${str(docroot)} . '/wp-config.php')) {
96
+ copy(
97
+ ${str(docroot)} . '/wp-config-sample.php',
98
+ ${str(docroot)} . '/wp-config.php'
99
+ );
100
+ }
101
+ `
102
+ );
103
+ }
104
+ async function installSqliteIntegration(php, pluginZip, docroot) {
105
+ const pluginDir = `${docroot}/wp-content/plugins/sqlite-database-integration`;
106
+ const staging = `${docroot}/wp-content/.wpapp-sqlite-staging`;
107
+ await unzipTo(php, pluginZip, staging);
108
+ await runPhp(
109
+ php,
110
+ `
111
+ $root = ${str(staging)};
112
+ $entries = array_values(array_diff(scandir($root), ['.', '..']));
113
+ if (count($entries) === 1 && is_dir("$root/$entries[0]")) {
114
+ $root = "$root/$entries[0]";
115
+ }
116
+ if (!is_dir(${str(pluginDir)})) {
117
+ rename($root, ${str(pluginDir)});
118
+ }
119
+ if (is_dir(${str(staging)})) {
120
+ @rmdir(${str(staging)});
121
+ }
122
+ $dropIn = file_get_contents(${str(pluginDir)} . '/db.copy');
123
+ $dropIn = str_replace(
124
+ '{SQLITE_IMPLEMENTATION_FOLDER_PATH}',
125
+ ${str(pluginDir)},
126
+ $dropIn
127
+ );
128
+ $dropIn = str_replace(
129
+ '{SQLITE_PLUGIN}',
130
+ 'sqlite-database-integration/load.php',
131
+ $dropIn
132
+ );
133
+ file_put_contents(${str(docroot)} . '/wp-content/db.php', $dropIn);
134
+ `
135
+ );
136
+ }
137
+ function defineSiteConstants(php, siteUrl, databaseDir) {
138
+ php.defineConstant("WP_HOME", siteUrl);
139
+ php.defineConstant("WP_SITEURL", siteUrl);
140
+ php.defineConstant("DB_DIR", databaseDir);
141
+ php.defineConstant("DB_FILE", ".ht.sqlite");
142
+ php.defineConstant("WP_SQLITE_AST_DRIVER", true);
143
+ }
144
+ async function runInstaller(requestHandler) {
145
+ const fields = new URLSearchParams({
146
+ language: "en",
147
+ prefix: "wp_",
148
+ weblog_title: "My WordPress Website",
149
+ user_name: "admin",
150
+ admin_password: "password",
151
+ admin_password2: "password",
152
+ Submit: "Install WordPress",
153
+ pw_weak: "1",
154
+ admin_email: "admin@localhost.com"
155
+ });
156
+ return requestHandler.request({
157
+ url: "/wp-admin/install.php?step=2",
158
+ method: "POST",
159
+ headers: { "content-type": "application/x-www-form-urlencoded" },
160
+ body: new TextEncoder().encode(fields.toString())
161
+ });
162
+ }
163
+ function str(value) {
164
+ return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
165
+ }
17
166
 
18
167
  // src/constants.ts
19
- import { RecommendedPHPVersion } from "@wp-playground/common";
20
168
  var WP_APP_HIDDEN_FOLDER = ".wp-app";
21
169
  var WP_APP_HOME_ENV = "WP_APP_HOME";
22
170
  var SQLITE_URL = "https://github.com/WordPress/sqlite-database-integration/archive/refs/heads/main.zip";
23
171
  var CLASSICPRESS_LATEST_URL = "https://www.classicpress.net/latest.zip";
24
172
  var DEFAULT_PORT = 8881;
25
- var DEFAULT_PHP_VERSION = RecommendedPHPVersion;
173
+ var DEFAULT_PHP_VERSION = "8.3";
26
174
  var DEFAULT_WORDPRESS_VERSION = "latest";
27
175
  var DOCROOT = "/wordpress";
28
176
 
@@ -30,14 +178,14 @@ var DOCROOT = "/wordpress";
30
178
  import fs9 from "fs";
31
179
  import path8 from "path";
32
180
 
33
- // src/wp-playground-wordpress/has-index-file.ts
181
+ // src/detect/has-index-file.ts
34
182
  import fs from "fs";
35
183
  import path from "path";
36
184
  function hasIndexFile(projectPath) {
37
185
  return fs.existsSync(path.join(projectPath, "index.php"));
38
186
  }
39
187
 
40
- // src/wp-playground-wordpress/is-valid-wordpress-version.ts
188
+ // src/detect/is-valid-wordpress-version.ts
41
189
  function isValidWordPressVersion(version) {
42
190
  const versionPattern = /^latest$|^(?:(\d+)\.(\d+)(?:\.(\d+))?)((?:-beta(?:\d+)?)|(?:-RC(?:\d+)?))?$/;
43
191
  const classicPressPattern = /^classicpress(?:-(\d+)\.(\d+)(?:\.(\d+))?)?$/;
@@ -47,11 +195,11 @@ function isClassicPressVersion(version) {
47
195
  return version === "classicpress" || version.startsWith("classicpress-");
48
196
  }
49
197
 
50
- // src/wp-playground-wordpress/get-plugin-file.ts
198
+ // src/detect/get-plugin-file.ts
51
199
  import fs3 from "fs";
52
200
  import path2, { basename } from "path";
53
201
 
54
- // src/wp-playground-wordpress/read-file-head.ts
202
+ // src/detect/read-file-head.ts
55
203
  import fs2 from "fs";
56
204
  function readFileHead(filePath, length = 8192) {
57
205
  const buffer = Buffer.alloc(length);
@@ -62,7 +210,7 @@ function readFileHead(filePath, length = 8192) {
62
210
  return fileContentBuffer.toString();
63
211
  }
64
212
 
65
- // src/wp-playground-wordpress/get-plugin-file.ts
213
+ // src/detect/get-plugin-file.ts
66
214
  function heuristicSort(files, projectPath) {
67
215
  const heuristicsBestGuess = `${basename(projectPath)}.php`;
68
216
  const heuristicsBestGuessIndex = files.indexOf(heuristicsBestGuess);
@@ -86,13 +234,13 @@ function getPluginFile(projectPath) {
86
234
  return null;
87
235
  }
88
236
 
89
- // src/wp-playground-wordpress/is-plugin-directory.ts
237
+ // src/detect/is-plugin-directory.ts
90
238
  function isPluginDirectory(projectPath) {
91
239
  const pluginFile = getPluginFile(projectPath);
92
240
  return pluginFile !== null;
93
241
  }
94
242
 
95
- // src/wp-playground-wordpress/is-theme-directory.ts
243
+ // src/detect/is-theme-directory.ts
96
244
  import fs4 from "fs";
97
245
  import path3 from "path";
98
246
  function isThemeDirectory(projectPath) {
@@ -105,7 +253,7 @@ function isThemeDirectory(projectPath) {
105
253
  return themeNameRegex.test(styleCSS);
106
254
  }
107
255
 
108
- // src/wp-playground-wordpress/is-wp-content-directory.ts
256
+ // src/detect/is-wp-content-directory.ts
109
257
  import fs5 from "fs";
110
258
  import path4 from "path";
111
259
  function isWpContentDirectory(projectPath) {
@@ -118,14 +266,14 @@ function isWpContentDirectory(projectPath) {
118
266
  return false;
119
267
  }
120
268
 
121
- // src/wp-playground-wordpress/is-wordpress-directory.ts
269
+ // src/detect/is-wordpress-directory.ts
122
270
  import fs6 from "fs";
123
271
  import path5 from "path";
124
272
  function isWordPressDirectory(projectPath) {
125
273
  return fs6.existsSync(path5.join(projectPath, "wp-content")) && fs6.existsSync(path5.join(projectPath, "wp-includes")) && fs6.existsSync(path5.join(projectPath, "wp-load.php"));
126
274
  }
127
275
 
128
- // src/wp-playground-wordpress/is-wordpress-develop-directory.ts
276
+ // src/detect/is-wordpress-develop-directory.ts
129
277
  import fs7 from "fs";
130
278
  import path6 from "path";
131
279
  function isWordPressDevelopDirectory(projectPath) {
@@ -228,31 +376,38 @@ async function startWPApp(options) {
228
376
  output?.log(`wp: ${options.wordPressVersion}`);
229
377
  output?.log(`site data: ${hostDocroot}`);
230
378
  }
231
- const requestHandler = await bootWordPressAndRequestHandler({
232
- siteUrl: options.absoluteUrl,
233
- documentRoot: DOCROOT,
234
- phpVersion: options.phpVersion,
235
- maxPhpInstances: 1,
236
- sapiName: "cli",
237
- cookieStore: false,
238
- createPhpRuntime: () => loadNodeRuntime(options.phpVersion, {
239
- emscriptenOptions: { processId: process.pid }
240
- }),
241
- hooks: {
242
- beforeWordPressFiles: async (php2) => {
243
- php2.mkdir(DOCROOT);
244
- await php2.mount(
245
- DOCROOT,
246
- createNodeFsMountHandler(hostDocroot)
247
- );
248
- }
249
- },
250
- wordPressZip: freshSite ? await getCoreZip(options.wordPressVersion) : void 0,
251
- sqliteIntegrationPluginZip: isWordPressBacked ? await getSqliteIntegrationZip() : void 0,
252
- dataSqlPath: isWordPressBacked ? `${DOCROOT}/wp-content/database/.ht.sqlite` : void 0,
253
- wordpressInstallMode: isWordPressBacked ? "install-from-existing-files-if-needed" : "do-not-attempt-installing"
254
- });
255
- const php = await requestHandler.getPrimaryPhp();
379
+ const php = await createPhp(options.phpVersion);
380
+ php.mkdir(DOCROOT);
381
+ await php.mount(DOCROOT, createNodeFsMountHandler(hostDocroot));
382
+ const requestHandler = createRequestHandler(
383
+ php,
384
+ DOCROOT,
385
+ options.absoluteUrl
386
+ );
387
+ if (freshSite) {
388
+ await installCore(
389
+ php,
390
+ await getCoreZip(options.wordPressVersion),
391
+ DOCROOT
392
+ );
393
+ }
394
+ if (isWordPressBacked) {
395
+ if (!php.fileExists(`${DOCROOT}/wp-content/db.php`)) {
396
+ await installSqliteIntegration(
397
+ php,
398
+ await getSqliteIntegrationZip(),
399
+ DOCROOT
400
+ );
401
+ }
402
+ defineSiteConstants(
403
+ php,
404
+ options.absoluteUrl,
405
+ `${DOCROOT}/wp-content/database`
406
+ );
407
+ }
408
+ if (freshSite) {
409
+ await runInstaller(requestHandler);
410
+ }
256
411
  await applyProjectMounts(php, mode, projectPath);
257
412
  if (freshSite) {
258
413
  await activateProject(php, mode, projectPath);
@@ -327,15 +482,17 @@ async function activateProject(php, mode, projectPath) {
327
482
  }
328
483
  }
329
484
  async function runWordPressCode(php, code) {
330
- const response = await php.run({
331
- code: `<?php
485
+ try {
486
+ await runPhp(
487
+ php,
488
+ `
332
489
  require ${phpString(`${DOCROOT}/wp-load.php`)};
333
490
  require_once ${phpString(`${DOCROOT}/wp-admin/includes/plugin.php`)};
334
491
  ${code}
335
- `
336
- });
337
- if (response.exitCode !== 0) {
338
- output?.error(`Activation step failed: ${response.errors}`);
492
+ `
493
+ );
494
+ } catch (error) {
495
+ output?.error(`Activation step failed: ${error.message}`);
339
496
  }
340
497
  }
341
498
  function phpString(value) {
package/dist/main.js CHANGED
@@ -14,17 +14,165 @@ import express from "express";
14
14
  // src/engine.ts
15
15
  import fs10 from "fs";
16
16
  import path9 from "path";
17
- import { createNodeFsMountHandler, loadNodeRuntime } from "@php-wasm/node";
18
- import { bootWordPressAndRequestHandler } from "@wp-playground/wordpress";
17
+ import { createNodeFsMountHandler } from "@php-wasm/node";
18
+
19
+ // src/boot.ts
20
+ import { rootCertificates } from "tls";
21
+ import { loadNodeRuntime } from "@php-wasm/node";
22
+ import { PHP, PHPRequestHandler, setPhpIniEntries } from "@php-wasm/universal";
23
+ var CA_BUNDLE_PATH = "/internal/ca-bundle.crt";
24
+ async function createPhp(phpVersion) {
25
+ const php = new PHP(
26
+ await loadNodeRuntime(phpVersion, {
27
+ emscriptenOptions: { processId: process.pid }
28
+ })
29
+ );
30
+ php.setSapiName("cli");
31
+ php.mkdir("/internal");
32
+ php.writeFile(CA_BUNDLE_PATH, rootCertificates.join("\n"));
33
+ await setPhpIniEntries(php, {
34
+ "openssl.cafile": CA_BUNDLE_PATH,
35
+ "curl.cainfo": CA_BUNDLE_PATH,
36
+ allow_url_fopen: "1",
37
+ disable_functions: ""
38
+ });
39
+ return php;
40
+ }
41
+ function createRequestHandler(php, documentRoot, absoluteUrl) {
42
+ return new PHPRequestHandler({ php, documentRoot, absoluteUrl });
43
+ }
44
+ async function runPhp(php, code) {
45
+ const response = await php.run({ code: `<?php ${code}` });
46
+ if (response.exitCode !== 0) {
47
+ throw new Error(response.errors || `PHP exited ${response.exitCode}`);
48
+ }
49
+ return response;
50
+ }
51
+ async function unzipTo(php, zip, destination) {
52
+ const tmpZip = `/internal/upload-${Date.now()}.zip`;
53
+ php.writeFile(tmpZip, new Uint8Array(await zip.arrayBuffer()));
54
+ await runPhp(
55
+ php,
56
+ `
57
+ $zip = new ZipArchive();
58
+ if ($zip->open(${str(tmpZip)}) !== true) exit(1);
59
+ if (!is_dir(${str(destination)})) mkdir(${str(destination)}, 0777, true);
60
+ if (!$zip->extractTo(${str(destination)})) exit(1);
61
+ $zip->close();
62
+ unlink(${str(tmpZip)});
63
+ `
64
+ );
65
+ }
66
+ async function installCore(php, coreZip, docroot) {
67
+ const staging = `${docroot}/.wpapp-staging`;
68
+ await unzipTo(php, coreZip, staging);
69
+ await runPhp(
70
+ php,
71
+ `
72
+ function wpapp_core_root($dir) {
73
+ if (file_exists("$dir/wp-config-sample.php")) return $dir;
74
+ foreach (scandir($dir) as $entry) {
75
+ if ($entry === '.' || $entry === '..') continue;
76
+ $candidate = "$dir/$entry";
77
+ if (is_dir($candidate) && file_exists("$candidate/wp-config-sample.php")) {
78
+ return $candidate;
79
+ }
80
+ }
81
+ exit(1);
82
+ }
83
+ function wpapp_rrmdir($dir) {
84
+ foreach (scandir($dir) as $entry) {
85
+ if ($entry === '.' || $entry === '..') continue;
86
+ $path = "$dir/$entry";
87
+ is_dir($path) ? wpapp_rrmdir($path) : unlink($path);
88
+ }
89
+ rmdir($dir);
90
+ }
91
+ $root = wpapp_core_root(${str(staging)});
92
+ foreach (scandir($root) as $entry) {
93
+ if ($entry === '.' || $entry === '..') continue;
94
+ rename("$root/$entry", ${str(docroot)} . "/$entry");
95
+ }
96
+ wpapp_rrmdir(${str(staging)});
97
+ if (!file_exists(${str(docroot)} . '/wp-config.php')) {
98
+ copy(
99
+ ${str(docroot)} . '/wp-config-sample.php',
100
+ ${str(docroot)} . '/wp-config.php'
101
+ );
102
+ }
103
+ `
104
+ );
105
+ }
106
+ async function installSqliteIntegration(php, pluginZip, docroot) {
107
+ const pluginDir = `${docroot}/wp-content/plugins/sqlite-database-integration`;
108
+ const staging = `${docroot}/wp-content/.wpapp-sqlite-staging`;
109
+ await unzipTo(php, pluginZip, staging);
110
+ await runPhp(
111
+ php,
112
+ `
113
+ $root = ${str(staging)};
114
+ $entries = array_values(array_diff(scandir($root), ['.', '..']));
115
+ if (count($entries) === 1 && is_dir("$root/$entries[0]")) {
116
+ $root = "$root/$entries[0]";
117
+ }
118
+ if (!is_dir(${str(pluginDir)})) {
119
+ rename($root, ${str(pluginDir)});
120
+ }
121
+ if (is_dir(${str(staging)})) {
122
+ @rmdir(${str(staging)});
123
+ }
124
+ $dropIn = file_get_contents(${str(pluginDir)} . '/db.copy');
125
+ $dropIn = str_replace(
126
+ '{SQLITE_IMPLEMENTATION_FOLDER_PATH}',
127
+ ${str(pluginDir)},
128
+ $dropIn
129
+ );
130
+ $dropIn = str_replace(
131
+ '{SQLITE_PLUGIN}',
132
+ 'sqlite-database-integration/load.php',
133
+ $dropIn
134
+ );
135
+ file_put_contents(${str(docroot)} . '/wp-content/db.php', $dropIn);
136
+ `
137
+ );
138
+ }
139
+ function defineSiteConstants(php, siteUrl, databaseDir) {
140
+ php.defineConstant("WP_HOME", siteUrl);
141
+ php.defineConstant("WP_SITEURL", siteUrl);
142
+ php.defineConstant("DB_DIR", databaseDir);
143
+ php.defineConstant("DB_FILE", ".ht.sqlite");
144
+ php.defineConstant("WP_SQLITE_AST_DRIVER", true);
145
+ }
146
+ async function runInstaller(requestHandler) {
147
+ const fields = new URLSearchParams({
148
+ language: "en",
149
+ prefix: "wp_",
150
+ weblog_title: "My WordPress Website",
151
+ user_name: "admin",
152
+ admin_password: "password",
153
+ admin_password2: "password",
154
+ Submit: "Install WordPress",
155
+ pw_weak: "1",
156
+ admin_email: "admin@localhost.com"
157
+ });
158
+ return requestHandler.request({
159
+ url: "/wp-admin/install.php?step=2",
160
+ method: "POST",
161
+ headers: { "content-type": "application/x-www-form-urlencoded" },
162
+ body: new TextEncoder().encode(fields.toString())
163
+ });
164
+ }
165
+ function str(value) {
166
+ return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
167
+ }
19
168
 
20
169
  // src/constants.ts
21
- import { RecommendedPHPVersion } from "@wp-playground/common";
22
170
  var WP_APP_HIDDEN_FOLDER = ".wp-app";
23
171
  var WP_APP_HOME_ENV = "WP_APP_HOME";
24
172
  var SQLITE_URL = "https://github.com/WordPress/sqlite-database-integration/archive/refs/heads/main.zip";
25
173
  var CLASSICPRESS_LATEST_URL = "https://www.classicpress.net/latest.zip";
26
174
  var DEFAULT_PORT = 8881;
27
- var DEFAULT_PHP_VERSION = RecommendedPHPVersion;
175
+ var DEFAULT_PHP_VERSION = "8.3";
28
176
  var DEFAULT_WORDPRESS_VERSION = "latest";
29
177
  var DOCROOT = "/wordpress";
30
178
 
@@ -32,14 +180,14 @@ var DOCROOT = "/wordpress";
32
180
  import fs9 from "fs";
33
181
  import path8 from "path";
34
182
 
35
- // src/wp-playground-wordpress/has-index-file.ts
183
+ // src/detect/has-index-file.ts
36
184
  import fs from "fs";
37
185
  import path from "path";
38
186
  function hasIndexFile(projectPath) {
39
187
  return fs.existsSync(path.join(projectPath, "index.php"));
40
188
  }
41
189
 
42
- // src/wp-playground-wordpress/is-valid-wordpress-version.ts
190
+ // src/detect/is-valid-wordpress-version.ts
43
191
  function isValidWordPressVersion(version) {
44
192
  const versionPattern = /^latest$|^(?:(\d+)\.(\d+)(?:\.(\d+))?)((?:-beta(?:\d+)?)|(?:-RC(?:\d+)?))?$/;
45
193
  const classicPressPattern = /^classicpress(?:-(\d+)\.(\d+)(?:\.(\d+))?)?$/;
@@ -49,11 +197,11 @@ function isClassicPressVersion(version) {
49
197
  return version === "classicpress" || version.startsWith("classicpress-");
50
198
  }
51
199
 
52
- // src/wp-playground-wordpress/get-plugin-file.ts
200
+ // src/detect/get-plugin-file.ts
53
201
  import fs3 from "fs";
54
202
  import path2, { basename } from "path";
55
203
 
56
- // src/wp-playground-wordpress/read-file-head.ts
204
+ // src/detect/read-file-head.ts
57
205
  import fs2 from "fs";
58
206
  function readFileHead(filePath, length = 8192) {
59
207
  const buffer = Buffer.alloc(length);
@@ -64,7 +212,7 @@ function readFileHead(filePath, length = 8192) {
64
212
  return fileContentBuffer.toString();
65
213
  }
66
214
 
67
- // src/wp-playground-wordpress/get-plugin-file.ts
215
+ // src/detect/get-plugin-file.ts
68
216
  function heuristicSort(files, projectPath) {
69
217
  const heuristicsBestGuess = `${basename(projectPath)}.php`;
70
218
  const heuristicsBestGuessIndex = files.indexOf(heuristicsBestGuess);
@@ -88,13 +236,13 @@ function getPluginFile(projectPath) {
88
236
  return null;
89
237
  }
90
238
 
91
- // src/wp-playground-wordpress/is-plugin-directory.ts
239
+ // src/detect/is-plugin-directory.ts
92
240
  function isPluginDirectory(projectPath) {
93
241
  const pluginFile = getPluginFile(projectPath);
94
242
  return pluginFile !== null;
95
243
  }
96
244
 
97
- // src/wp-playground-wordpress/is-theme-directory.ts
245
+ // src/detect/is-theme-directory.ts
98
246
  import fs4 from "fs";
99
247
  import path3 from "path";
100
248
  function isThemeDirectory(projectPath) {
@@ -107,7 +255,7 @@ function isThemeDirectory(projectPath) {
107
255
  return themeNameRegex.test(styleCSS);
108
256
  }
109
257
 
110
- // src/wp-playground-wordpress/is-wp-content-directory.ts
258
+ // src/detect/is-wp-content-directory.ts
111
259
  import fs5 from "fs";
112
260
  import path4 from "path";
113
261
  function isWpContentDirectory(projectPath) {
@@ -120,14 +268,14 @@ function isWpContentDirectory(projectPath) {
120
268
  return false;
121
269
  }
122
270
 
123
- // src/wp-playground-wordpress/is-wordpress-directory.ts
271
+ // src/detect/is-wordpress-directory.ts
124
272
  import fs6 from "fs";
125
273
  import path5 from "path";
126
274
  function isWordPressDirectory(projectPath) {
127
275
  return fs6.existsSync(path5.join(projectPath, "wp-content")) && fs6.existsSync(path5.join(projectPath, "wp-includes")) && fs6.existsSync(path5.join(projectPath, "wp-load.php"));
128
276
  }
129
277
 
130
- // src/wp-playground-wordpress/is-wordpress-develop-directory.ts
278
+ // src/detect/is-wordpress-develop-directory.ts
131
279
  import fs7 from "fs";
132
280
  import path6 from "path";
133
281
  function isWordPressDevelopDirectory(projectPath) {
@@ -343,31 +491,38 @@ async function startWPApp(options) {
343
491
  output?.log(`wp: ${options.wordPressVersion}`);
344
492
  output?.log(`site data: ${hostDocroot}`);
345
493
  }
346
- const requestHandler = await bootWordPressAndRequestHandler({
347
- siteUrl: options.absoluteUrl,
348
- documentRoot: DOCROOT,
349
- phpVersion: options.phpVersion,
350
- maxPhpInstances: 1,
351
- sapiName: "cli",
352
- cookieStore: false,
353
- createPhpRuntime: () => loadNodeRuntime(options.phpVersion, {
354
- emscriptenOptions: { processId: process.pid }
355
- }),
356
- hooks: {
357
- beforeWordPressFiles: async (php2) => {
358
- php2.mkdir(DOCROOT);
359
- await php2.mount(
360
- DOCROOT,
361
- createNodeFsMountHandler(hostDocroot)
362
- );
363
- }
364
- },
365
- wordPressZip: freshSite ? await getCoreZip(options.wordPressVersion) : void 0,
366
- sqliteIntegrationPluginZip: isWordPressBacked ? await getSqliteIntegrationZip() : void 0,
367
- dataSqlPath: isWordPressBacked ? `${DOCROOT}/wp-content/database/.ht.sqlite` : void 0,
368
- wordpressInstallMode: isWordPressBacked ? "install-from-existing-files-if-needed" : "do-not-attempt-installing"
369
- });
370
- const php = await requestHandler.getPrimaryPhp();
494
+ const php = await createPhp(options.phpVersion);
495
+ php.mkdir(DOCROOT);
496
+ await php.mount(DOCROOT, createNodeFsMountHandler(hostDocroot));
497
+ const requestHandler = createRequestHandler(
498
+ php,
499
+ DOCROOT,
500
+ options.absoluteUrl
501
+ );
502
+ if (freshSite) {
503
+ await installCore(
504
+ php,
505
+ await getCoreZip(options.wordPressVersion),
506
+ DOCROOT
507
+ );
508
+ }
509
+ if (isWordPressBacked) {
510
+ if (!php.fileExists(`${DOCROOT}/wp-content/db.php`)) {
511
+ await installSqliteIntegration(
512
+ php,
513
+ await getSqliteIntegrationZip(),
514
+ DOCROOT
515
+ );
516
+ }
517
+ defineSiteConstants(
518
+ php,
519
+ options.absoluteUrl,
520
+ `${DOCROOT}/wp-content/database`
521
+ );
522
+ }
523
+ if (freshSite) {
524
+ await runInstaller(requestHandler);
525
+ }
371
526
  await applyProjectMounts(php, mode, projectPath);
372
527
  if (freshSite) {
373
528
  await activateProject(php, mode, projectPath);
@@ -442,15 +597,17 @@ async function activateProject(php, mode, projectPath) {
442
597
  }
443
598
  }
444
599
  async function runWordPressCode(php, code) {
445
- const response = await php.run({
446
- code: `<?php
600
+ try {
601
+ await runPhp(
602
+ php,
603
+ `
447
604
  require ${phpString(`${DOCROOT}/wp-load.php`)};
448
605
  require_once ${phpString(`${DOCROOT}/wp-admin/includes/plugin.php`)};
449
606
  ${code}
450
- `
451
- });
452
- if (response.exitCode !== 0) {
453
- output?.error(`Activation step failed: ${response.errors}`);
607
+ `
608
+ );
609
+ } catch (error) {
610
+ output?.error(`Activation step failed: ${error.message}`);
454
611
  }
455
612
  }
456
613
  function phpString(value) {
@@ -562,7 +719,7 @@ var parseHeaders = (req) => {
562
719
  import fs12 from "fs";
563
720
  import path10 from "path";
564
721
  import { loadNodeRuntime as loadNodeRuntime2 } from "@php-wasm/node";
565
- import { PHP } from "@php-wasm/universal";
722
+ import { PHP as PHP2 } from "@php-wasm/universal";
566
723
  async function executePHP(phpArgs, options) {
567
724
  const args = phpArgs.filter((arg) => arg !== "php" && arg !== "--");
568
725
  let code;
@@ -575,7 +732,7 @@ async function executePHP(phpArgs, options) {
575
732
  'Usage: wp-app php -- -r "<code>" | wp-app php -- <script.php>'
576
733
  );
577
734
  }
578
- const php = new PHP(
735
+ const php = new PHP2(
579
736
  await loadNodeRuntime2(options.phpVersion, {
580
737
  emscriptenOptions: { processId: process.pid }
581
738
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wp-operations/wp-app",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "The wp-now revival. Zero-config local WordPress dev server on @php-wasm/node.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -41,8 +41,6 @@
41
41
  "dependencies": {
42
42
  "@php-wasm/node": "3.1.44",
43
43
  "@php-wasm/universal": "3.1.44",
44
- "@wp-playground/common": "3.1.44",
45
- "@wp-playground/wordpress": "3.1.44",
46
44
  "express": "4.21.2",
47
45
  "yargs": "17.7.2"
48
46
  },