@wp-operations/wp-app 0.3.0 → 0.4.1
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 +20 -33
- package/dist/index.js +150 -71
- package/dist/main.js +699 -282
- package/package.json +5 -6
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# @wp-operations/wp-app
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
When `wp-now` was deprecated in favor of the blueprint-centric Playground CLI, the simple `wp-now start` workflow went with it. `wp-app` brings it back — same one-command experience, running on the **current** php-wasm engine (PHP 7.4 – 8.5).
|
|
3
|
+
A zero-config local WordPress dev server on `@php-wasm/node`: no Docker, no MySQL, no XAMPP. Point it at a theme, a plugin, a full core checkout, or a wp-app project, and it starts.
|
|
6
4
|
|
|
7
5
|
## Install
|
|
8
6
|
|
|
@@ -15,48 +13,37 @@ Requires Node.js >= 20.10.
|
|
|
15
13
|
## Usage
|
|
16
14
|
|
|
17
15
|
```bash
|
|
18
|
-
wp-app start
|
|
19
|
-
wp-app start --path=my-
|
|
20
|
-
wp-app start --php=8.3 --wp=6.8
|
|
21
|
-
wp-app start --wp=classicpress
|
|
22
|
-
wp-app start --port=8881 --reset
|
|
23
|
-
wp-app dev
|
|
24
|
-
wp-app build
|
|
25
|
-
wp-app
|
|
26
|
-
wp-app
|
|
16
|
+
wp-app start
|
|
17
|
+
wp-app start --path=my-app
|
|
18
|
+
wp-app start --php=8.3 --wp=6.8
|
|
19
|
+
wp-app start --wp=classicpress
|
|
20
|
+
wp-app start --port=8881 --reset
|
|
21
|
+
wp-app dev
|
|
22
|
+
wp-app build
|
|
23
|
+
wp-app install
|
|
24
|
+
wp-app update
|
|
25
|
+
wp-app create my-app
|
|
26
|
+
wp-app php -- -r 'echo PHP_VERSION;'
|
|
27
|
+
wp-app php -- script.php
|
|
27
28
|
```
|
|
28
29
|
|
|
29
30
|
Login: `admin` / `password`.
|
|
30
31
|
|
|
31
|
-
## How data is stored
|
|
32
|
-
|
|
33
|
-
- Core downloads are cached once in `~/.wp-app/cache/`
|
|
34
|
-
- Each project gets a full persistent WordPress site in `~/.wp-app/sites/<project>-<hash>/` — files and SQLite database survive restarts; `--reset` deletes exactly this directory and never your project
|
|
35
|
-
- Your project directory is mounted *into* that site (as a theme, plugin, or wp-content overlay) — edits are live without restarts
|
|
36
|
-
- Set the `WP_APP_HOME` environment variable to relocate all of the above (CI, portable drives, or the `.local` repo convention via `@wp-operations/wp-ide`)
|
|
37
|
-
|
|
38
32
|
## Modes
|
|
39
33
|
|
|
40
|
-
Auto-detected from the project directory, or forced with `--mode
|
|
34
|
+
Auto-detected from the project directory, or forced with `--mode`. Checked in this order — first match wins:
|
|
41
35
|
|
|
42
36
|
| Mode | Detected when | What runs |
|
|
43
37
|
|------|--------------|-----------|
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
38
|
+
| `wp-app` | `composer.json` with `"type": "wp-app"` | `wp-app` |
|
|
39
|
+
| `wordpress` | Full WordPress core checkout | Your core served directly, SQLite-backed |
|
|
40
|
+
| `classicpress` | Full ClassicPress core checkout | Your core served directly, SQLite-backed |
|
|
46
41
|
| `wp-content` | `plugins/` + `themes/` dirs | Managed WP + your entries overlaid on `wp-content` |
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
42
|
+
| `plugin` | PHP file with a Plugin Name header | Managed WP + your directory mounted as a plugin, auto-activated |
|
|
43
|
+
| `theme` | `style.css` with a Theme Name header | Managed WP + your directory mounted as a theme, auto-activated |
|
|
49
44
|
| `index` | An `index.php` | Plain PHP server, no WordPress |
|
|
50
|
-
| `playground` | None of the above (e.g. empty dir) | Fresh managed install |
|
|
51
|
-
|
|
52
|
-
## ClassicPress
|
|
53
|
-
|
|
54
|
-
`--wp=classicpress` fetches the latest ClassicPress release; `--wp=classicpress-2.4.1` pins a version. Everything else (modes, SQLite, mounts) works identically.
|
|
55
|
-
|
|
56
|
-
## Part of wp-operations
|
|
57
45
|
|
|
58
|
-
`wp-app` is the foundation of [wp-operations](https://github.com/wordpress-operations/wordpress-operations) — an open-source operations toolchain for classic WordPress development: PSR-4 vendor packages as plugins, an AI skills library with security guidelines, monorepo tooling (`@wp-operations/wp-ide`), and a unified operations CLI (`@wp-operations/wp-ops`).
|
|
59
46
|
|
|
60
|
-
##
|
|
47
|
+
## License
|
|
61
48
|
|
|
62
49
|
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
|
@@ -10,8 +10,8 @@ function getCodeSpaceURL(port) {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
// src/engine.ts
|
|
13
|
-
import
|
|
14
|
-
import
|
|
13
|
+
import fs11 from "fs";
|
|
14
|
+
import path10 from "path";
|
|
15
15
|
import { createNodeFsMountHandler } from "@php-wasm/node";
|
|
16
16
|
|
|
17
17
|
// src/boot.ts
|
|
@@ -37,7 +37,18 @@ async function createPhp(phpVersion) {
|
|
|
37
37
|
return php;
|
|
38
38
|
}
|
|
39
39
|
function createRequestHandler(php, documentRoot, absoluteUrl) {
|
|
40
|
-
return new PHPRequestHandler({
|
|
40
|
+
return new PHPRequestHandler({
|
|
41
|
+
php,
|
|
42
|
+
documentRoot,
|
|
43
|
+
absoluteUrl,
|
|
44
|
+
// Route unmatched URLs (permalinks, admin-ajax) to index.php, not 404.
|
|
45
|
+
getFileNotFoundAction: () => ({
|
|
46
|
+
type: "internal-redirect",
|
|
47
|
+
uri: "/index.php"
|
|
48
|
+
}),
|
|
49
|
+
// Let Set-Cookie pass through to the real HTTP server (express).
|
|
50
|
+
cookieStore: false
|
|
51
|
+
});
|
|
41
52
|
}
|
|
42
53
|
async function runPhp(php, code) {
|
|
43
54
|
const response = await php.run({ code: `<?php ${code}` });
|
|
@@ -140,18 +151,22 @@ function defineSiteConstants(php, siteUrl, databaseDir) {
|
|
|
140
151
|
php.defineConstant("DB_DIR", databaseDir);
|
|
141
152
|
php.defineConstant("DB_FILE", ".ht.sqlite");
|
|
142
153
|
php.defineConstant("WP_SQLITE_AST_DRIVER", true);
|
|
154
|
+
php.defineConstant("DISABLE_WP_CRON", true);
|
|
143
155
|
}
|
|
144
156
|
async function runInstaller(requestHandler) {
|
|
157
|
+
const adminUser = process.env.WP_ADMIN_USER || "admin";
|
|
158
|
+
const adminPassword = process.env.WP_ADMIN_PASSWORD || "password";
|
|
159
|
+
const adminEmail = process.env.WP_ADMIN_EMAIL || "admin@localhost.com";
|
|
145
160
|
const fields = new URLSearchParams({
|
|
146
161
|
language: "en",
|
|
147
162
|
prefix: "wp_",
|
|
148
163
|
weblog_title: "My WordPress Website",
|
|
149
|
-
user_name:
|
|
150
|
-
admin_password:
|
|
151
|
-
admin_password2:
|
|
164
|
+
user_name: adminUser,
|
|
165
|
+
admin_password: adminPassword,
|
|
166
|
+
admin_password2: adminPassword,
|
|
152
167
|
Submit: "Install WordPress",
|
|
153
168
|
pw_weak: "1",
|
|
154
|
-
admin_email:
|
|
169
|
+
admin_email: adminEmail
|
|
155
170
|
});
|
|
156
171
|
return requestHandler.request({
|
|
157
172
|
url: "/wp-admin/install.php?step=2",
|
|
@@ -175,8 +190,8 @@ var DEFAULT_WORDPRESS_VERSION = "latest";
|
|
|
175
190
|
var DOCROOT = "/wordpress";
|
|
176
191
|
|
|
177
192
|
// src/download.ts
|
|
178
|
-
import
|
|
179
|
-
import
|
|
193
|
+
import fs10 from "fs";
|
|
194
|
+
import path9 from "path";
|
|
180
195
|
|
|
181
196
|
// src/detect/has-index-file.ts
|
|
182
197
|
import fs from "fs";
|
|
@@ -188,7 +203,7 @@ function hasIndexFile(projectPath) {
|
|
|
188
203
|
// src/detect/is-valid-wordpress-version.ts
|
|
189
204
|
function isValidWordPressVersion(version) {
|
|
190
205
|
const versionPattern = /^latest$|^(?:(\d+)\.(\d+)(?:\.(\d+))?)((?:-beta(?:\d+)?)|(?:-RC(?:\d+)?))?$/;
|
|
191
|
-
const classicPressPattern = /^classicpress(?:-(\d+)\.(\d+)(?:\.(\d+))?)?$/;
|
|
206
|
+
const classicPressPattern = /^classicpress(?:-(\d+)\.(\d+)(?:\.(\d+))?(?:-rc(?:\d+)?)?)?$/;
|
|
192
207
|
return versionPattern.test(version) || classicPressPattern.test(version);
|
|
193
208
|
}
|
|
194
209
|
function isClassicPressVersion(version) {
|
|
@@ -253,13 +268,29 @@ function isThemeDirectory(projectPath) {
|
|
|
253
268
|
return themeNameRegex.test(styleCSS);
|
|
254
269
|
}
|
|
255
270
|
|
|
256
|
-
// src/detect/is-wp-
|
|
271
|
+
// src/detect/is-wp-app-project.ts
|
|
257
272
|
import fs5 from "fs";
|
|
258
273
|
import path4 from "path";
|
|
274
|
+
function isWpAppProject(projectPath) {
|
|
275
|
+
const composerJsonPath = path4.join(projectPath, "composer.json");
|
|
276
|
+
if (!fs5.existsSync(composerJsonPath)) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
const composerJson = JSON.parse(fs5.readFileSync(composerJsonPath, "utf8"));
|
|
281
|
+
return composerJson.type === "wp-app";
|
|
282
|
+
} catch {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// src/detect/is-wp-content-directory.ts
|
|
288
|
+
import fs6 from "fs";
|
|
289
|
+
import path5 from "path";
|
|
259
290
|
function isWpContentDirectory(projectPath) {
|
|
260
|
-
const muPluginsExists =
|
|
261
|
-
const pluginsExists =
|
|
262
|
-
const themesExists =
|
|
291
|
+
const muPluginsExists = fs6.existsSync(path5.join(projectPath, "mu-plugins"));
|
|
292
|
+
const pluginsExists = fs6.existsSync(path5.join(projectPath, "plugins"));
|
|
293
|
+
const themesExists = fs6.existsSync(path5.join(projectPath, "themes"));
|
|
263
294
|
if (muPluginsExists || pluginsExists || themesExists) {
|
|
264
295
|
return true;
|
|
265
296
|
}
|
|
@@ -267,29 +298,24 @@ function isWpContentDirectory(projectPath) {
|
|
|
267
298
|
}
|
|
268
299
|
|
|
269
300
|
// src/detect/is-wordpress-directory.ts
|
|
270
|
-
import
|
|
271
|
-
import
|
|
301
|
+
import fs7 from "fs";
|
|
302
|
+
import path6 from "path";
|
|
272
303
|
function isWordPressDirectory(projectPath) {
|
|
273
|
-
return
|
|
304
|
+
return fs7.existsSync(path6.join(projectPath, "wp-content")) && fs7.existsSync(path6.join(projectPath, "wp-includes")) && fs7.existsSync(path6.join(projectPath, "wp-load.php"));
|
|
274
305
|
}
|
|
275
306
|
|
|
276
|
-
// src/detect/is-
|
|
277
|
-
import
|
|
278
|
-
import
|
|
279
|
-
function
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
"build/wp-load.php"
|
|
289
|
-
];
|
|
290
|
-
return requiredFiles.every(
|
|
291
|
-
(file) => fs7.existsSync(path6.join(projectPath, file))
|
|
292
|
-
);
|
|
307
|
+
// src/detect/is-classicpress-directory.ts
|
|
308
|
+
import fs8 from "fs";
|
|
309
|
+
import path7 from "path";
|
|
310
|
+
function isClassicPressDirectory(projectPath) {
|
|
311
|
+
if (!isWordPressDirectory(projectPath)) {
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
const versionFile = path7.join(projectPath, "wp-includes", "version.php");
|
|
315
|
+
if (!fs8.existsSync(versionFile)) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
return fs8.readFileSync(versionFile, "utf8").includes("classicpress_version");
|
|
293
319
|
}
|
|
294
320
|
|
|
295
321
|
// src/output.ts
|
|
@@ -300,24 +326,54 @@ var output = shouldOutput() ? console : null;
|
|
|
300
326
|
|
|
301
327
|
// src/paths.ts
|
|
302
328
|
import crypto from "crypto";
|
|
303
|
-
import
|
|
329
|
+
import fs9 from "fs";
|
|
304
330
|
import os from "os";
|
|
305
|
-
import
|
|
331
|
+
import path8 from "path";
|
|
306
332
|
function getWpAppHome() {
|
|
307
|
-
|
|
333
|
+
const home = process.env[WP_APP_HOME_ENV] || path8.join(os.homedir(), WP_APP_HIDDEN_FOLDER);
|
|
334
|
+
cleanupLegacyHome(home);
|
|
335
|
+
return home;
|
|
308
336
|
}
|
|
309
337
|
function getCachePath() {
|
|
310
|
-
return ensureDir(
|
|
338
|
+
return ensureDir(path8.join(getWpAppHome(), "cache"));
|
|
339
|
+
}
|
|
340
|
+
function getCorePath(wordPressVersion) {
|
|
341
|
+
const key = wordPressVersion.replace(/[^a-zA-Z0-9.-]/g, "_");
|
|
342
|
+
return path8.join(getWpAppHome(), "core", key);
|
|
311
343
|
}
|
|
312
344
|
function getSitePath(projectPath) {
|
|
313
|
-
const
|
|
314
|
-
const
|
|
315
|
-
|
|
345
|
+
const resolved = path8.resolve(projectPath);
|
|
346
|
+
const identity = process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
347
|
+
const projectName = path8.basename(resolved);
|
|
348
|
+
const hash = crypto.createHash("sha1").update(identity).digest("hex");
|
|
349
|
+
return path8.join(getWpAppHome(), "sites", `${projectName}-${hash}`);
|
|
350
|
+
}
|
|
351
|
+
function getProjectLocalPath(projectPath) {
|
|
352
|
+
return path8.join(path8.resolve(projectPath), ".local", "wp-app");
|
|
316
353
|
}
|
|
317
354
|
function ensureDir(dir) {
|
|
318
|
-
|
|
355
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
319
356
|
return dir;
|
|
320
357
|
}
|
|
358
|
+
var LEGACY_TOP_LEVEL_DIRS = [
|
|
359
|
+
"wordpress-versions",
|
|
360
|
+
"wp-content",
|
|
361
|
+
"mu-plugins",
|
|
362
|
+
"sqlite-database-integration-main"
|
|
363
|
+
];
|
|
364
|
+
var legacyCleanupDone = false;
|
|
365
|
+
function cleanupLegacyHome(home) {
|
|
366
|
+
if (legacyCleanupDone) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
legacyCleanupDone = true;
|
|
370
|
+
for (const name of LEGACY_TOP_LEVEL_DIRS) {
|
|
371
|
+
const dir = path8.join(home, name);
|
|
372
|
+
if (fs9.existsSync(dir)) {
|
|
373
|
+
fs9.rmSync(dir, { recursive: true, force: true });
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
321
377
|
|
|
322
378
|
// src/download.ts
|
|
323
379
|
function getWordPressVersionUrl(version) {
|
|
@@ -336,8 +392,8 @@ function getClassicPressVersionUrl(version) {
|
|
|
336
392
|
return `https://github.com/ClassicPress/ClassicPress-release/archive/refs/tags/${tag}.zip`;
|
|
337
393
|
}
|
|
338
394
|
async function cachedZip(url, cacheKey) {
|
|
339
|
-
const cacheFile =
|
|
340
|
-
if (!
|
|
395
|
+
const cacheFile = path9.join(getCachePath(), cacheKey);
|
|
396
|
+
if (!fs10.existsSync(cacheFile)) {
|
|
341
397
|
output?.log(`Downloading ${cacheKey}...`);
|
|
342
398
|
const response = await fetch(url, { redirect: "follow" });
|
|
343
399
|
if (!response.ok) {
|
|
@@ -346,11 +402,11 @@ async function cachedZip(url, cacheKey) {
|
|
|
346
402
|
);
|
|
347
403
|
}
|
|
348
404
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
349
|
-
|
|
350
|
-
|
|
405
|
+
fs10.writeFileSync(`${cacheFile}.partial`, bytes);
|
|
406
|
+
fs10.renameSync(`${cacheFile}.partial`, cacheFile);
|
|
351
407
|
output?.log(`Cached ${cacheKey} (${bytes.length} bytes).`);
|
|
352
408
|
}
|
|
353
|
-
return new File([
|
|
409
|
+
return new File([fs10.readFileSync(cacheFile)], cacheKey);
|
|
354
410
|
}
|
|
355
411
|
async function getCoreZip(version) {
|
|
356
412
|
if (isClassicPressVersion(version)) {
|
|
@@ -364,17 +420,22 @@ async function getSqliteIntegrationZip() {
|
|
|
364
420
|
|
|
365
421
|
// src/engine.ts
|
|
366
422
|
async function startWPApp(options) {
|
|
367
|
-
const projectPath =
|
|
423
|
+
const projectPath = path10.resolve(options.projectPath);
|
|
368
424
|
const mode = options.mode;
|
|
369
425
|
output?.log(`directory: ${options.projectPath}`);
|
|
370
426
|
output?.log(`mode: ${mode}`);
|
|
371
427
|
output?.log(`php: ${options.phpVersion}`);
|
|
372
|
-
const
|
|
428
|
+
const sharesCore = mode === "wp-app" /* WP_APP */ || mode === "theme" /* THEME */ || mode === "plugin" /* PLUGIN */ || mode === "wp-content" /* WP_CONTENT */;
|
|
429
|
+
const hostDocroot = mode === "index" /* INDEX */ || mode === "wordpress" /* WORDPRESS */ || mode === "classicpress" /* CLASSICPRESS */ ? projectPath : ensureDir(getCorePath(options.wordPressVersion));
|
|
430
|
+
const projectDataRoot = sharesCore ? ensureDir(
|
|
431
|
+
mode === "wp-app" /* WP_APP */ ? getProjectLocalPath(projectPath) : getSitePath(projectPath)
|
|
432
|
+
) : null;
|
|
373
433
|
const isWordPressBacked = mode !== "index" /* INDEX */;
|
|
374
|
-
const
|
|
434
|
+
const freshCore = isWordPressBacked && !fs11.existsSync(path10.join(hostDocroot, "wp-load.php"));
|
|
435
|
+
const freshSite = sharesCore ? !fs11.existsSync(path10.join(projectDataRoot, "database", ".ht.sqlite")) : freshCore;
|
|
375
436
|
if (isWordPressBacked) {
|
|
376
437
|
output?.log(`wp: ${options.wordPressVersion}`);
|
|
377
|
-
output?.log(`site data: ${hostDocroot}`);
|
|
438
|
+
output?.log(`site data: ${projectDataRoot ?? hostDocroot}`);
|
|
378
439
|
}
|
|
379
440
|
const php = await createPhp(options.phpVersion);
|
|
380
441
|
php.mkdir(DOCROOT);
|
|
@@ -384,7 +445,7 @@ async function startWPApp(options) {
|
|
|
384
445
|
DOCROOT,
|
|
385
446
|
options.absoluteUrl
|
|
386
447
|
);
|
|
387
|
-
if (
|
|
448
|
+
if (freshCore) {
|
|
388
449
|
await installCore(
|
|
389
450
|
php,
|
|
390
451
|
await getCoreZip(options.wordPressVersion),
|
|
@@ -399,6 +460,22 @@ async function startWPApp(options) {
|
|
|
399
460
|
DOCROOT
|
|
400
461
|
);
|
|
401
462
|
}
|
|
463
|
+
}
|
|
464
|
+
if (projectDataRoot) {
|
|
465
|
+
const databaseDir = ensureDir(path10.join(projectDataRoot, "database"));
|
|
466
|
+
const uploadsDir = ensureDir(path10.join(projectDataRoot, "uploads"));
|
|
467
|
+
php.mkdir(`${DOCROOT}/wp-content/database`);
|
|
468
|
+
await php.mount(
|
|
469
|
+
`${DOCROOT}/wp-content/database`,
|
|
470
|
+
createNodeFsMountHandler(databaseDir)
|
|
471
|
+
);
|
|
472
|
+
php.mkdir(`${DOCROOT}/wp-content/uploads`);
|
|
473
|
+
await php.mount(
|
|
474
|
+
`${DOCROOT}/wp-content/uploads`,
|
|
475
|
+
createNodeFsMountHandler(uploadsDir)
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
if (isWordPressBacked) {
|
|
402
479
|
defineSiteConstants(
|
|
403
480
|
php,
|
|
404
481
|
options.absoluteUrl,
|
|
@@ -415,8 +492,9 @@ async function startWPApp(options) {
|
|
|
415
492
|
return { requestHandler, php, options, freshSite };
|
|
416
493
|
}
|
|
417
494
|
async function applyProjectMounts(php, mode, projectPath) {
|
|
418
|
-
const projectName =
|
|
495
|
+
const projectName = path10.basename(projectPath);
|
|
419
496
|
switch (mode) {
|
|
497
|
+
case "wp-app" /* WP_APP */:
|
|
420
498
|
case "theme" /* THEME */:
|
|
421
499
|
await mountAt(
|
|
422
500
|
php,
|
|
@@ -432,13 +510,13 @@ async function applyProjectMounts(php, mode, projectPath) {
|
|
|
432
510
|
);
|
|
433
511
|
break;
|
|
434
512
|
case "wp-content" /* WP_CONTENT */:
|
|
435
|
-
for (const entry of
|
|
513
|
+
for (const entry of fs11.readdirSync(projectPath)) {
|
|
436
514
|
if (entry === "index.php") {
|
|
437
515
|
continue;
|
|
438
516
|
}
|
|
439
517
|
await mountAt(
|
|
440
518
|
php,
|
|
441
|
-
|
|
519
|
+
path10.join(projectPath, entry),
|
|
442
520
|
`${DOCROOT}/wp-content/${entry}`
|
|
443
521
|
);
|
|
444
522
|
}
|
|
@@ -448,14 +526,14 @@ async function applyProjectMounts(php, mode, projectPath) {
|
|
|
448
526
|
}
|
|
449
527
|
}
|
|
450
528
|
async function mountAt(php, hostPath, vfsPath) {
|
|
451
|
-
if (
|
|
529
|
+
if (fs11.statSync(hostPath).isDirectory()) {
|
|
452
530
|
php.mkdir(vfsPath);
|
|
453
531
|
}
|
|
454
532
|
await php.mount(vfsPath, createNodeFsMountHandler(hostPath));
|
|
455
533
|
}
|
|
456
534
|
async function activateProject(php, mode, projectPath) {
|
|
457
|
-
const projectName =
|
|
458
|
-
if (mode === "theme" /* THEME */) {
|
|
535
|
+
const projectName = path10.basename(projectPath);
|
|
536
|
+
if (mode === "wp-app" /* WP_APP */ || mode === "theme" /* THEME */) {
|
|
459
537
|
await runWordPressCode(
|
|
460
538
|
php,
|
|
461
539
|
`switch_theme(${phpString(projectName)});`
|
|
@@ -500,11 +578,11 @@ function phpString(value) {
|
|
|
500
578
|
}
|
|
501
579
|
function findPluginFile(projectPath) {
|
|
502
580
|
const pluginNameRegex = /^(?:[ \t]*<\?php)?[ \t/*#@]*Plugin Name:(.*)$/im;
|
|
503
|
-
for (const file of
|
|
581
|
+
for (const file of fs11.readdirSync(projectPath)) {
|
|
504
582
|
if (!file.endsWith(".php")) {
|
|
505
583
|
continue;
|
|
506
584
|
}
|
|
507
|
-
const content =
|
|
585
|
+
const content = fs11.readFileSync(path10.join(projectPath, file), "utf8");
|
|
508
586
|
if (pluginNameRegex.test(content)) {
|
|
509
587
|
return file;
|
|
510
588
|
}
|
|
@@ -512,8 +590,10 @@ function findPluginFile(projectPath) {
|
|
|
512
590
|
return null;
|
|
513
591
|
}
|
|
514
592
|
function inferMode(projectPath) {
|
|
515
|
-
if (
|
|
516
|
-
return "
|
|
593
|
+
if (isWpAppProject(projectPath)) {
|
|
594
|
+
return "wp-app" /* WP_APP */;
|
|
595
|
+
} else if (isClassicPressDirectory(projectPath)) {
|
|
596
|
+
return "classicpress" /* CLASSICPRESS */;
|
|
517
597
|
} else if (isWordPressDirectory(projectPath)) {
|
|
518
598
|
return "wordpress" /* WORDPRESS */;
|
|
519
599
|
} else if (isWpContentDirectory(projectPath)) {
|
|
@@ -525,7 +605,7 @@ function inferMode(projectPath) {
|
|
|
525
605
|
} else if (hasIndexFile(projectPath)) {
|
|
526
606
|
return "index" /* INDEX */;
|
|
527
607
|
}
|
|
528
|
-
return
|
|
608
|
+
return null;
|
|
529
609
|
}
|
|
530
610
|
|
|
531
611
|
// src/port-finder.ts
|
|
@@ -556,11 +636,6 @@ var PortFinder = class _PortFinder {
|
|
|
556
636
|
});
|
|
557
637
|
});
|
|
558
638
|
}
|
|
559
|
-
/**
|
|
560
|
-
* Returns the first available open port, caching and reusing it for subsequent calls.
|
|
561
|
-
*
|
|
562
|
-
* @returns {Promise<number>} A promise that resolves to the open port number.
|
|
563
|
-
*/
|
|
564
639
|
async getOpenPort() {
|
|
565
640
|
if (this.#openPort) {
|
|
566
641
|
return this.#openPort;
|
|
@@ -612,7 +687,11 @@ async function getWpAppConfig(args) {
|
|
|
612
687
|
}
|
|
613
688
|
});
|
|
614
689
|
if (!options.mode || options.mode === "auto") {
|
|
615
|
-
|
|
690
|
+
const inferred = inferMode(options.projectPath);
|
|
691
|
+
if (!inferred) {
|
|
692
|
+
process.exit(1);
|
|
693
|
+
}
|
|
694
|
+
options.mode = inferred;
|
|
616
695
|
}
|
|
617
696
|
if (!options.absoluteUrl) {
|
|
618
697
|
options.absoluteUrl = await getAbsoluteURL();
|
|
@@ -631,12 +710,12 @@ async function getWpAppConfig(args) {
|
|
|
631
710
|
}
|
|
632
711
|
|
|
633
712
|
// src/start-server.ts
|
|
634
|
-
import
|
|
713
|
+
import fs12 from "fs";
|
|
635
714
|
import { Readable } from "stream";
|
|
636
715
|
import { pipeline } from "stream/promises";
|
|
637
716
|
import express from "express";
|
|
638
717
|
async function startServer(options = {}) {
|
|
639
|
-
if (!
|
|
718
|
+
if (!fs12.existsSync(options.projectPath)) {
|
|
640
719
|
throw new Error(
|
|
641
720
|
`The given path "${options.projectPath}" does not exist.`
|
|
642
721
|
);
|