@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/dist/main.js
CHANGED
|
@@ -1,19 +1,324 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res, err) => function __init() {
|
|
4
|
+
if (err) throw err[0];
|
|
5
|
+
try {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
+
} catch (e) {
|
|
8
|
+
throw err = [e], e;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// src/exec-command.ts
|
|
17
|
+
import { execFileSync } from "child_process";
|
|
18
|
+
function runCommand(command, args, options) {
|
|
19
|
+
return execFileSync(command, args, {
|
|
20
|
+
...options,
|
|
21
|
+
shell: process.platform === "win32"
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
var init_exec_command = __esm({
|
|
25
|
+
"src/exec-command.ts"() {
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// src/create.ts
|
|
30
|
+
var create_exports = {};
|
|
31
|
+
__export(create_exports, {
|
|
32
|
+
runCreateCommand: () => runCreateCommand
|
|
33
|
+
});
|
|
34
|
+
import fs14 from "fs";
|
|
35
|
+
import path12 from "path";
|
|
36
|
+
import readline from "readline/promises";
|
|
37
|
+
async function ask(question, defaultValue = "") {
|
|
38
|
+
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
39
|
+
const answer = (await rl.question(`? ${question}${suffix}: `)).trim();
|
|
40
|
+
return answer || defaultValue;
|
|
41
|
+
}
|
|
42
|
+
async function askChoice(question, choices, defaultIndex = 0) {
|
|
43
|
+
console.log(`? ${question}`);
|
|
44
|
+
choices.forEach((choice, i) => {
|
|
45
|
+
const marker = i === defaultIndex ? ">" : " ";
|
|
46
|
+
console.log(` ${marker} ${i + 1}) ${choice}`);
|
|
47
|
+
});
|
|
48
|
+
const answer = (await rl.question(` Choice (${defaultIndex + 1}): `)).trim();
|
|
49
|
+
const index = answer ? parseInt(answer, 10) - 1 : defaultIndex;
|
|
50
|
+
return choices[index] ?? choices[defaultIndex];
|
|
51
|
+
}
|
|
52
|
+
async function askPassword(question, defaultValue) {
|
|
53
|
+
if (!process.stdin.isTTY) {
|
|
54
|
+
return defaultValue;
|
|
55
|
+
}
|
|
56
|
+
process.stdout.write(`? ${question} (${defaultValue}): `);
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
let value = "";
|
|
59
|
+
process.stdin.setRawMode(true);
|
|
60
|
+
process.stdin.resume();
|
|
61
|
+
process.stdin.setEncoding("utf8");
|
|
62
|
+
const onData = (char) => {
|
|
63
|
+
if (char === "\r" || char === "\n") {
|
|
64
|
+
process.stdin.setRawMode(false);
|
|
65
|
+
process.stdin.pause();
|
|
66
|
+
process.stdin.removeListener("data", onData);
|
|
67
|
+
process.stdout.write("\n");
|
|
68
|
+
resolve(value || defaultValue);
|
|
69
|
+
} else if (char === CTRL_C) {
|
|
70
|
+
process.stdin.setRawMode(false);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
} else if (char === BACKSPACE_DEL || char === BACKSPACE_BS) {
|
|
73
|
+
if (value.length > 0) {
|
|
74
|
+
value = value.slice(0, -1);
|
|
75
|
+
process.stdout.write("\b \b");
|
|
76
|
+
}
|
|
77
|
+
} else if (char.charCodeAt(0) >= 32) {
|
|
78
|
+
value += char;
|
|
79
|
+
process.stdout.write("*");
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
process.stdin.on("data", onData);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function toPascalCase(value) {
|
|
86
|
+
return value.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((word) => word[0].toUpperCase() + word.slice(1)).join("");
|
|
87
|
+
}
|
|
88
|
+
async function collectAnswers() {
|
|
89
|
+
const projectName = await ask("Project name", "my-app");
|
|
90
|
+
const author = await ask("Author (composer vendor)", "my-name");
|
|
91
|
+
const cssTool = (await askChoice("CSS tooling", ["Tailwind", "SCSS"], 0)).toLowerCase().startsWith("tailwind") ? "tailwind" : "scss";
|
|
92
|
+
const npmPackagesRaw = await ask(
|
|
93
|
+
"npm packages (comma-separated, optional)"
|
|
94
|
+
);
|
|
95
|
+
const composerPackagesRaw = await ask(
|
|
96
|
+
"composer packages (comma-separated, optional)"
|
|
97
|
+
);
|
|
98
|
+
const adminUser = await ask("Admin username", "admin");
|
|
99
|
+
const adminPassword = await askPassword("Admin password", "password");
|
|
100
|
+
return {
|
|
101
|
+
projectName,
|
|
102
|
+
author,
|
|
103
|
+
cssTool,
|
|
104
|
+
npmPackages: splitList(npmPackagesRaw),
|
|
105
|
+
composerPackages: splitList(composerPackagesRaw),
|
|
106
|
+
adminUser,
|
|
107
|
+
adminPassword
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function splitList(value) {
|
|
111
|
+
return value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
112
|
+
}
|
|
113
|
+
function printPreview(answers) {
|
|
114
|
+
console.log("\nReview:");
|
|
115
|
+
console.log(` 1) Project name: ${answers.projectName}`);
|
|
116
|
+
console.log(` 2) Author: ${answers.author}`);
|
|
117
|
+
console.log(` 3) CSS tooling: ${answers.cssTool}`);
|
|
118
|
+
console.log(
|
|
119
|
+
` 4) npm packages: ${answers.npmPackages.join(", ") || "(none)"}`
|
|
120
|
+
);
|
|
121
|
+
console.log(
|
|
122
|
+
` 5) composer packages: ${answers.composerPackages.join(", ") || "(none)"}`
|
|
123
|
+
);
|
|
124
|
+
console.log(` 6) Admin username: ${answers.adminUser}`);
|
|
125
|
+
console.log(` 7) Admin password: ${"*".repeat(answers.adminPassword.length)}`);
|
|
126
|
+
}
|
|
127
|
+
async function editAnswers(answers) {
|
|
128
|
+
for (; ; ) {
|
|
129
|
+
printPreview(answers);
|
|
130
|
+
const choice = await ask(
|
|
131
|
+
"Edit a field (number), or press enter to confirm"
|
|
132
|
+
);
|
|
133
|
+
if (!choice) {
|
|
134
|
+
return answers;
|
|
135
|
+
}
|
|
136
|
+
switch (choice) {
|
|
137
|
+
case "1":
|
|
138
|
+
answers.projectName = await ask("Project name", answers.projectName);
|
|
139
|
+
break;
|
|
140
|
+
case "2":
|
|
141
|
+
answers.author = await ask("Author (composer vendor)", answers.author);
|
|
142
|
+
break;
|
|
143
|
+
case "3":
|
|
144
|
+
answers.cssTool = (await askChoice(
|
|
145
|
+
"CSS tooling",
|
|
146
|
+
["Tailwind", "SCSS"],
|
|
147
|
+
answers.cssTool === "tailwind" ? 0 : 1
|
|
148
|
+
)).toLowerCase().startsWith("tailwind") ? "tailwind" : "scss";
|
|
149
|
+
break;
|
|
150
|
+
case "4":
|
|
151
|
+
answers.npmPackages = splitList(
|
|
152
|
+
await ask(
|
|
153
|
+
"npm packages (comma-separated, optional)",
|
|
154
|
+
answers.npmPackages.join(", ")
|
|
155
|
+
)
|
|
156
|
+
);
|
|
157
|
+
break;
|
|
158
|
+
case "5":
|
|
159
|
+
answers.composerPackages = splitList(
|
|
160
|
+
await ask(
|
|
161
|
+
"composer packages (comma-separated, optional)",
|
|
162
|
+
answers.composerPackages.join(", ")
|
|
163
|
+
)
|
|
164
|
+
);
|
|
165
|
+
break;
|
|
166
|
+
case "6":
|
|
167
|
+
answers.adminUser = await ask("Admin username", answers.adminUser);
|
|
168
|
+
break;
|
|
169
|
+
case "7":
|
|
170
|
+
answers.adminPassword = await askPassword(
|
|
171
|
+
"Admin password",
|
|
172
|
+
answers.adminPassword
|
|
173
|
+
);
|
|
174
|
+
break;
|
|
175
|
+
default:
|
|
176
|
+
console.log("Not a valid field number.");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function scaffoldFiles(dir, answers) {
|
|
181
|
+
const namespace = toPascalCase(answers.author) + "\\" + toPascalCase(answers.projectName);
|
|
182
|
+
fs14.mkdirSync(path12.join(dir, "src", "js"), { recursive: true });
|
|
183
|
+
fs14.mkdirSync(path12.join(dir, "assets", "css"), { recursive: true });
|
|
184
|
+
fs14.mkdirSync(path12.join(dir, "assets", "js"), { recursive: true });
|
|
185
|
+
writeJson(path12.join(dir, "composer.json"), {
|
|
186
|
+
name: `${answers.author}/${answers.projectName}`,
|
|
187
|
+
type: "wp-app",
|
|
188
|
+
license: "proprietary",
|
|
189
|
+
require: { php: ">=8.0" },
|
|
190
|
+
autoload: { "psr-4": { [`${namespace}\\`]: "src/php/" } }
|
|
191
|
+
});
|
|
192
|
+
const cssDeps = answers.cssTool === "tailwind" ? { tailwindcss: "^4.3.0", "@tailwindcss/cli": "^4.3.0" } : { sass: "^1.103.0" };
|
|
193
|
+
const cssBuild = answers.cssTool === "tailwind" ? "tailwindcss -i src/css/main.css -o assets/css/main.css --minify" : "sass src/css/main.scss assets/css/main.css --style=compressed";
|
|
194
|
+
const cssWatch = answers.cssTool === "tailwind" ? "tailwindcss -i src/css/main.css -o assets/css/main.css --watch" : "sass src/css/main.scss assets/css/main.css --watch";
|
|
195
|
+
writeJson(path12.join(dir, "package.json"), {
|
|
196
|
+
name: answers.projectName,
|
|
197
|
+
private: true,
|
|
198
|
+
scripts: {
|
|
199
|
+
"build:css": cssBuild,
|
|
200
|
+
"watch:css": cssWatch,
|
|
201
|
+
"build:js": "esbuild src/js/main.js --bundle --minify --outfile=assets/js/main.js",
|
|
202
|
+
"watch:js": "esbuild src/js/main.js --bundle --outfile=assets/js/main.js --watch",
|
|
203
|
+
build: "npm run build:css && npm run build:js"
|
|
204
|
+
},
|
|
205
|
+
devDependencies: { esbuild: "^0.28.0", ...cssDeps }
|
|
206
|
+
});
|
|
207
|
+
fs14.writeFileSync(
|
|
208
|
+
path12.join(dir, ".env"),
|
|
209
|
+
`WP_ADMIN_USER=${answers.adminUser}
|
|
210
|
+
WP_ADMIN_PASSWORD=${answers.adminPassword}
|
|
211
|
+
`
|
|
212
|
+
);
|
|
213
|
+
fs14.writeFileSync(
|
|
214
|
+
path12.join(dir, "style.css"),
|
|
215
|
+
`/*
|
|
216
|
+
Theme Name: ${answers.projectName}
|
|
217
|
+
Author: ${answers.author}
|
|
218
|
+
Version: 0.1.0
|
|
219
|
+
*/
|
|
220
|
+
`
|
|
221
|
+
);
|
|
222
|
+
fs14.writeFileSync(
|
|
223
|
+
path12.join(dir, "functions.php"),
|
|
224
|
+
`<?php
|
|
225
|
+
declare(strict_types=1);
|
|
226
|
+
|
|
227
|
+
$autoload = __DIR__ . '/vendor/autoload.php';
|
|
228
|
+
if (file_exists($autoload)) {
|
|
229
|
+
require_once $autoload;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
add_action('wp_enqueue_scripts', function (): void {
|
|
233
|
+
wp_enqueue_style('${answers.projectName}', get_stylesheet_directory_uri() . '/assets/css/main.css');
|
|
234
|
+
wp_enqueue_script('${answers.projectName}', get_stylesheet_directory_uri() . '/assets/js/main.js', [], false, true);
|
|
235
|
+
});
|
|
236
|
+
`
|
|
237
|
+
);
|
|
238
|
+
fs14.writeFileSync(
|
|
239
|
+
path12.join(dir, "index.php"),
|
|
240
|
+
`<?php
|
|
241
|
+
declare(strict_types=1);
|
|
242
|
+
get_header();
|
|
243
|
+
while (have_posts()) : the_post();
|
|
244
|
+
the_content();
|
|
245
|
+
endwhile;
|
|
246
|
+
get_footer();
|
|
247
|
+
`
|
|
248
|
+
);
|
|
249
|
+
const cssEntry = answers.cssTool === "tailwind" ? '@import "tailwindcss";\n' : "// entry stylesheet\n";
|
|
250
|
+
fs14.mkdirSync(path12.join(dir, "src", "css"), { recursive: true });
|
|
251
|
+
fs14.writeFileSync(
|
|
252
|
+
path12.join(
|
|
253
|
+
dir,
|
|
254
|
+
"src",
|
|
255
|
+
"css",
|
|
256
|
+
answers.cssTool === "tailwind" ? "main.css" : "main.scss"
|
|
257
|
+
),
|
|
258
|
+
cssEntry
|
|
259
|
+
);
|
|
260
|
+
fs14.writeFileSync(path12.join(dir, "src", "js", "main.js"), "// entry script\n");
|
|
261
|
+
fs14.mkdirSync(path12.join(dir, "src", "php"), { recursive: true });
|
|
262
|
+
}
|
|
263
|
+
function writeJson(filePath, data) {
|
|
264
|
+
fs14.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
265
|
+
}
|
|
266
|
+
async function runCreateCommand(targetPath) {
|
|
267
|
+
const dir = path12.resolve(targetPath);
|
|
268
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
269
|
+
let answers = await collectAnswers();
|
|
270
|
+
answers = await editAnswers(answers);
|
|
271
|
+
rl.close();
|
|
272
|
+
scaffoldFiles(dir, answers);
|
|
273
|
+
const run = (cmd, args) => runCommand(cmd, args, { cwd: dir, stdio: "inherit" });
|
|
274
|
+
if (answers.npmPackages.length) {
|
|
275
|
+
console.log("Installing additional npm packages...");
|
|
276
|
+
run("npm", ["install", "--save", ...answers.npmPackages]);
|
|
277
|
+
} else {
|
|
278
|
+
console.log("Installing npm dependencies...");
|
|
279
|
+
run("npm", ["install"]);
|
|
280
|
+
}
|
|
281
|
+
if (answers.composerPackages.length) {
|
|
282
|
+
console.log("Requiring additional composer packages...");
|
|
283
|
+
run("composer", ["require", ...answers.composerPackages]);
|
|
284
|
+
} else {
|
|
285
|
+
console.log("Installing composer dependencies...");
|
|
286
|
+
try {
|
|
287
|
+
run("composer", ["install"]);
|
|
288
|
+
} catch {
|
|
289
|
+
console.log("(composer not available or nothing to install yet \u2014 skipped)");
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
console.log(`
|
|
293
|
+
Done. cd ${targetPath} && wp-app start`);
|
|
294
|
+
}
|
|
295
|
+
var rl, CTRL_C, BACKSPACE_DEL, BACKSPACE_BS;
|
|
296
|
+
var init_create = __esm({
|
|
297
|
+
"src/create.ts"() {
|
|
298
|
+
init_exec_command();
|
|
299
|
+
rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
300
|
+
CTRL_C = "";
|
|
301
|
+
BACKSPACE_DEL = "\x7F";
|
|
302
|
+
BACKSPACE_BS = "\b";
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
1
306
|
// src/run-cli.ts
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
307
|
+
init_exec_command();
|
|
308
|
+
import fs15 from "fs";
|
|
309
|
+
import path13 from "path";
|
|
310
|
+
import { parseArgs } from "util";
|
|
6
311
|
import { spawn, execSync } from "child_process";
|
|
7
312
|
|
|
8
313
|
// src/start-server.ts
|
|
9
|
-
import
|
|
314
|
+
import fs12 from "fs";
|
|
10
315
|
import { Readable } from "stream";
|
|
11
316
|
import { pipeline } from "stream/promises";
|
|
12
317
|
import express from "express";
|
|
13
318
|
|
|
14
319
|
// src/engine.ts
|
|
15
|
-
import
|
|
16
|
-
import
|
|
320
|
+
import fs11 from "fs";
|
|
321
|
+
import path10 from "path";
|
|
17
322
|
import { createNodeFsMountHandler } from "@php-wasm/node";
|
|
18
323
|
|
|
19
324
|
// src/boot.ts
|
|
@@ -39,7 +344,18 @@ async function createPhp(phpVersion) {
|
|
|
39
344
|
return php;
|
|
40
345
|
}
|
|
41
346
|
function createRequestHandler(php, documentRoot, absoluteUrl) {
|
|
42
|
-
return new PHPRequestHandler({
|
|
347
|
+
return new PHPRequestHandler({
|
|
348
|
+
php,
|
|
349
|
+
documentRoot,
|
|
350
|
+
absoluteUrl,
|
|
351
|
+
// Route unmatched URLs (permalinks, admin-ajax) to index.php, not 404.
|
|
352
|
+
getFileNotFoundAction: () => ({
|
|
353
|
+
type: "internal-redirect",
|
|
354
|
+
uri: "/index.php"
|
|
355
|
+
}),
|
|
356
|
+
// Let Set-Cookie pass through to the real HTTP server (express).
|
|
357
|
+
cookieStore: false
|
|
358
|
+
});
|
|
43
359
|
}
|
|
44
360
|
async function runPhp(php, code) {
|
|
45
361
|
const response = await php.run({ code: `<?php ${code}` });
|
|
@@ -142,18 +458,22 @@ function defineSiteConstants(php, siteUrl, databaseDir) {
|
|
|
142
458
|
php.defineConstant("DB_DIR", databaseDir);
|
|
143
459
|
php.defineConstant("DB_FILE", ".ht.sqlite");
|
|
144
460
|
php.defineConstant("WP_SQLITE_AST_DRIVER", true);
|
|
461
|
+
php.defineConstant("DISABLE_WP_CRON", true);
|
|
145
462
|
}
|
|
146
463
|
async function runInstaller(requestHandler) {
|
|
464
|
+
const adminUser = process.env.WP_ADMIN_USER || "admin";
|
|
465
|
+
const adminPassword = process.env.WP_ADMIN_PASSWORD || "password";
|
|
466
|
+
const adminEmail = process.env.WP_ADMIN_EMAIL || "admin@localhost.com";
|
|
147
467
|
const fields = new URLSearchParams({
|
|
148
468
|
language: "en",
|
|
149
469
|
prefix: "wp_",
|
|
150
470
|
weblog_title: "My WordPress Website",
|
|
151
|
-
user_name:
|
|
152
|
-
admin_password:
|
|
153
|
-
admin_password2:
|
|
471
|
+
user_name: adminUser,
|
|
472
|
+
admin_password: adminPassword,
|
|
473
|
+
admin_password2: adminPassword,
|
|
154
474
|
Submit: "Install WordPress",
|
|
155
475
|
pw_weak: "1",
|
|
156
|
-
admin_email:
|
|
476
|
+
admin_email: adminEmail
|
|
157
477
|
});
|
|
158
478
|
return requestHandler.request({
|
|
159
479
|
url: "/wp-admin/install.php?step=2",
|
|
@@ -177,8 +497,8 @@ var DEFAULT_WORDPRESS_VERSION = "latest";
|
|
|
177
497
|
var DOCROOT = "/wordpress";
|
|
178
498
|
|
|
179
499
|
// src/download.ts
|
|
180
|
-
import
|
|
181
|
-
import
|
|
500
|
+
import fs10 from "fs";
|
|
501
|
+
import path9 from "path";
|
|
182
502
|
|
|
183
503
|
// src/detect/has-index-file.ts
|
|
184
504
|
import fs from "fs";
|
|
@@ -190,7 +510,7 @@ function hasIndexFile(projectPath) {
|
|
|
190
510
|
// src/detect/is-valid-wordpress-version.ts
|
|
191
511
|
function isValidWordPressVersion(version) {
|
|
192
512
|
const versionPattern = /^latest$|^(?:(\d+)\.(\d+)(?:\.(\d+))?)((?:-beta(?:\d+)?)|(?:-RC(?:\d+)?))?$/;
|
|
193
|
-
const classicPressPattern = /^classicpress(?:-(\d+)\.(\d+)(?:\.(\d+))?)?$/;
|
|
513
|
+
const classicPressPattern = /^classicpress(?:-(\d+)\.(\d+)(?:\.(\d+))?(?:-rc(?:\d+)?)?)?$/;
|
|
194
514
|
return versionPattern.test(version) || classicPressPattern.test(version);
|
|
195
515
|
}
|
|
196
516
|
function isClassicPressVersion(version) {
|
|
@@ -255,13 +575,29 @@ function isThemeDirectory(projectPath) {
|
|
|
255
575
|
return themeNameRegex.test(styleCSS);
|
|
256
576
|
}
|
|
257
577
|
|
|
258
|
-
// src/detect/is-wp-
|
|
578
|
+
// src/detect/is-wp-app-project.ts
|
|
259
579
|
import fs5 from "fs";
|
|
260
580
|
import path4 from "path";
|
|
581
|
+
function isWpAppProject(projectPath) {
|
|
582
|
+
const composerJsonPath = path4.join(projectPath, "composer.json");
|
|
583
|
+
if (!fs5.existsSync(composerJsonPath)) {
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
const composerJson = JSON.parse(fs5.readFileSync(composerJsonPath, "utf8"));
|
|
588
|
+
return composerJson.type === "wp-app";
|
|
589
|
+
} catch {
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// src/detect/is-wp-content-directory.ts
|
|
595
|
+
import fs6 from "fs";
|
|
596
|
+
import path5 from "path";
|
|
261
597
|
function isWpContentDirectory(projectPath) {
|
|
262
|
-
const muPluginsExists =
|
|
263
|
-
const pluginsExists =
|
|
264
|
-
const themesExists =
|
|
598
|
+
const muPluginsExists = fs6.existsSync(path5.join(projectPath, "mu-plugins"));
|
|
599
|
+
const pluginsExists = fs6.existsSync(path5.join(projectPath, "plugins"));
|
|
600
|
+
const themesExists = fs6.existsSync(path5.join(projectPath, "themes"));
|
|
265
601
|
if (muPluginsExists || pluginsExists || themesExists) {
|
|
266
602
|
return true;
|
|
267
603
|
}
|
|
@@ -269,29 +605,24 @@ function isWpContentDirectory(projectPath) {
|
|
|
269
605
|
}
|
|
270
606
|
|
|
271
607
|
// src/detect/is-wordpress-directory.ts
|
|
272
|
-
import
|
|
273
|
-
import
|
|
608
|
+
import fs7 from "fs";
|
|
609
|
+
import path6 from "path";
|
|
274
610
|
function isWordPressDirectory(projectPath) {
|
|
275
|
-
return
|
|
611
|
+
return fs7.existsSync(path6.join(projectPath, "wp-content")) && fs7.existsSync(path6.join(projectPath, "wp-includes")) && fs7.existsSync(path6.join(projectPath, "wp-load.php"));
|
|
276
612
|
}
|
|
277
613
|
|
|
278
|
-
// src/detect/is-
|
|
279
|
-
import
|
|
280
|
-
import
|
|
281
|
-
function
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
"build/wp-load.php"
|
|
291
|
-
];
|
|
292
|
-
return requiredFiles.every(
|
|
293
|
-
(file) => fs7.existsSync(path6.join(projectPath, file))
|
|
294
|
-
);
|
|
614
|
+
// src/detect/is-classicpress-directory.ts
|
|
615
|
+
import fs8 from "fs";
|
|
616
|
+
import path7 from "path";
|
|
617
|
+
function isClassicPressDirectory(projectPath) {
|
|
618
|
+
if (!isWordPressDirectory(projectPath)) {
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
const versionFile = path7.join(projectPath, "wp-includes", "version.php");
|
|
622
|
+
if (!fs8.existsSync(versionFile)) {
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
return fs8.readFileSync(versionFile, "utf8").includes("classicpress_version");
|
|
295
626
|
}
|
|
296
627
|
|
|
297
628
|
// src/output.ts
|
|
@@ -302,24 +633,54 @@ var output = shouldOutput() ? console : null;
|
|
|
302
633
|
|
|
303
634
|
// src/paths.ts
|
|
304
635
|
import crypto from "crypto";
|
|
305
|
-
import
|
|
636
|
+
import fs9 from "fs";
|
|
306
637
|
import os from "os";
|
|
307
|
-
import
|
|
638
|
+
import path8 from "path";
|
|
308
639
|
function getWpAppHome() {
|
|
309
|
-
|
|
640
|
+
const home = process.env[WP_APP_HOME_ENV] || path8.join(os.homedir(), WP_APP_HIDDEN_FOLDER);
|
|
641
|
+
cleanupLegacyHome(home);
|
|
642
|
+
return home;
|
|
310
643
|
}
|
|
311
644
|
function getCachePath() {
|
|
312
|
-
return ensureDir(
|
|
645
|
+
return ensureDir(path8.join(getWpAppHome(), "cache"));
|
|
646
|
+
}
|
|
647
|
+
function getCorePath(wordPressVersion) {
|
|
648
|
+
const key = wordPressVersion.replace(/[^a-zA-Z0-9.-]/g, "_");
|
|
649
|
+
return path8.join(getWpAppHome(), "core", key);
|
|
313
650
|
}
|
|
314
651
|
function getSitePath(projectPath) {
|
|
315
|
-
const
|
|
316
|
-
const
|
|
317
|
-
|
|
652
|
+
const resolved = path8.resolve(projectPath);
|
|
653
|
+
const identity = process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
654
|
+
const projectName = path8.basename(resolved);
|
|
655
|
+
const hash = crypto.createHash("sha1").update(identity).digest("hex");
|
|
656
|
+
return path8.join(getWpAppHome(), "sites", `${projectName}-${hash}`);
|
|
657
|
+
}
|
|
658
|
+
function getProjectLocalPath(projectPath) {
|
|
659
|
+
return path8.join(path8.resolve(projectPath), ".local", "wp-app");
|
|
318
660
|
}
|
|
319
661
|
function ensureDir(dir) {
|
|
320
|
-
|
|
662
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
321
663
|
return dir;
|
|
322
664
|
}
|
|
665
|
+
var LEGACY_TOP_LEVEL_DIRS = [
|
|
666
|
+
"wordpress-versions",
|
|
667
|
+
"wp-content",
|
|
668
|
+
"mu-plugins",
|
|
669
|
+
"sqlite-database-integration-main"
|
|
670
|
+
];
|
|
671
|
+
var legacyCleanupDone = false;
|
|
672
|
+
function cleanupLegacyHome(home) {
|
|
673
|
+
if (legacyCleanupDone) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
legacyCleanupDone = true;
|
|
677
|
+
for (const name of LEGACY_TOP_LEVEL_DIRS) {
|
|
678
|
+
const dir = path8.join(home, name);
|
|
679
|
+
if (fs9.existsSync(dir)) {
|
|
680
|
+
fs9.rmSync(dir, { recursive: true, force: true });
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
323
684
|
|
|
324
685
|
// src/download.ts
|
|
325
686
|
function getWordPressVersionUrl(version) {
|
|
@@ -338,8 +699,8 @@ function getClassicPressVersionUrl(version) {
|
|
|
338
699
|
return `https://github.com/ClassicPress/ClassicPress-release/archive/refs/tags/${tag}.zip`;
|
|
339
700
|
}
|
|
340
701
|
async function cachedZip(url, cacheKey) {
|
|
341
|
-
const cacheFile =
|
|
342
|
-
if (!
|
|
702
|
+
const cacheFile = path9.join(getCachePath(), cacheKey);
|
|
703
|
+
if (!fs10.existsSync(cacheFile)) {
|
|
343
704
|
output?.log(`Downloading ${cacheKey}...`);
|
|
344
705
|
const response = await fetch(url, { redirect: "follow" });
|
|
345
706
|
if (!response.ok) {
|
|
@@ -348,11 +709,11 @@ async function cachedZip(url, cacheKey) {
|
|
|
348
709
|
);
|
|
349
710
|
}
|
|
350
711
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
351
|
-
|
|
352
|
-
|
|
712
|
+
fs10.writeFileSync(`${cacheFile}.partial`, bytes);
|
|
713
|
+
fs10.renameSync(`${cacheFile}.partial`, cacheFile);
|
|
353
714
|
output?.log(`Cached ${cacheKey} (${bytes.length} bytes).`);
|
|
354
715
|
}
|
|
355
|
-
return new File([
|
|
716
|
+
return new File([fs10.readFileSync(cacheFile)], cacheKey);
|
|
356
717
|
}
|
|
357
718
|
async function getCoreZip(version) {
|
|
358
719
|
if (isClassicPressVersion(version)) {
|
|
@@ -403,11 +764,6 @@ var PortFinder = class _PortFinder {
|
|
|
403
764
|
});
|
|
404
765
|
});
|
|
405
766
|
}
|
|
406
|
-
/**
|
|
407
|
-
* Returns the first available open port, caching and reusing it for subsequent calls.
|
|
408
|
-
*
|
|
409
|
-
* @returns {Promise<number>} A promise that resolves to the open port number.
|
|
410
|
-
*/
|
|
411
767
|
async getOpenPort() {
|
|
412
768
|
if (this.#openPort) {
|
|
413
769
|
return this.#openPort;
|
|
@@ -459,7 +815,11 @@ async function getWpAppConfig(args) {
|
|
|
459
815
|
}
|
|
460
816
|
});
|
|
461
817
|
if (!options.mode || options.mode === "auto") {
|
|
462
|
-
|
|
818
|
+
const inferred = inferMode(options.projectPath);
|
|
819
|
+
if (!inferred) {
|
|
820
|
+
process.exit(1);
|
|
821
|
+
}
|
|
822
|
+
options.mode = inferred;
|
|
463
823
|
}
|
|
464
824
|
if (!options.absoluteUrl) {
|
|
465
825
|
options.absoluteUrl = await getAbsoluteURL();
|
|
@@ -479,17 +839,22 @@ async function getWpAppConfig(args) {
|
|
|
479
839
|
|
|
480
840
|
// src/engine.ts
|
|
481
841
|
async function startWPApp(options) {
|
|
482
|
-
const projectPath =
|
|
842
|
+
const projectPath = path10.resolve(options.projectPath);
|
|
483
843
|
const mode = options.mode;
|
|
484
844
|
output?.log(`directory: ${options.projectPath}`);
|
|
485
845
|
output?.log(`mode: ${mode}`);
|
|
486
846
|
output?.log(`php: ${options.phpVersion}`);
|
|
487
|
-
const
|
|
847
|
+
const sharesCore = mode === "wp-app" /* WP_APP */ || mode === "theme" /* THEME */ || mode === "plugin" /* PLUGIN */ || mode === "wp-content" /* WP_CONTENT */;
|
|
848
|
+
const hostDocroot = mode === "index" /* INDEX */ || mode === "wordpress" /* WORDPRESS */ || mode === "classicpress" /* CLASSICPRESS */ ? projectPath : ensureDir(getCorePath(options.wordPressVersion));
|
|
849
|
+
const projectDataRoot = sharesCore ? ensureDir(
|
|
850
|
+
mode === "wp-app" /* WP_APP */ ? getProjectLocalPath(projectPath) : getSitePath(projectPath)
|
|
851
|
+
) : null;
|
|
488
852
|
const isWordPressBacked = mode !== "index" /* INDEX */;
|
|
489
|
-
const
|
|
853
|
+
const freshCore = isWordPressBacked && !fs11.existsSync(path10.join(hostDocroot, "wp-load.php"));
|
|
854
|
+
const freshSite = sharesCore ? !fs11.existsSync(path10.join(projectDataRoot, "database", ".ht.sqlite")) : freshCore;
|
|
490
855
|
if (isWordPressBacked) {
|
|
491
856
|
output?.log(`wp: ${options.wordPressVersion}`);
|
|
492
|
-
output?.log(`site data: ${hostDocroot}`);
|
|
857
|
+
output?.log(`site data: ${projectDataRoot ?? hostDocroot}`);
|
|
493
858
|
}
|
|
494
859
|
const php = await createPhp(options.phpVersion);
|
|
495
860
|
php.mkdir(DOCROOT);
|
|
@@ -499,7 +864,7 @@ async function startWPApp(options) {
|
|
|
499
864
|
DOCROOT,
|
|
500
865
|
options.absoluteUrl
|
|
501
866
|
);
|
|
502
|
-
if (
|
|
867
|
+
if (freshCore) {
|
|
503
868
|
await installCore(
|
|
504
869
|
php,
|
|
505
870
|
await getCoreZip(options.wordPressVersion),
|
|
@@ -514,6 +879,22 @@ async function startWPApp(options) {
|
|
|
514
879
|
DOCROOT
|
|
515
880
|
);
|
|
516
881
|
}
|
|
882
|
+
}
|
|
883
|
+
if (projectDataRoot) {
|
|
884
|
+
const databaseDir = ensureDir(path10.join(projectDataRoot, "database"));
|
|
885
|
+
const uploadsDir = ensureDir(path10.join(projectDataRoot, "uploads"));
|
|
886
|
+
php.mkdir(`${DOCROOT}/wp-content/database`);
|
|
887
|
+
await php.mount(
|
|
888
|
+
`${DOCROOT}/wp-content/database`,
|
|
889
|
+
createNodeFsMountHandler(databaseDir)
|
|
890
|
+
);
|
|
891
|
+
php.mkdir(`${DOCROOT}/wp-content/uploads`);
|
|
892
|
+
await php.mount(
|
|
893
|
+
`${DOCROOT}/wp-content/uploads`,
|
|
894
|
+
createNodeFsMountHandler(uploadsDir)
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
if (isWordPressBacked) {
|
|
517
898
|
defineSiteConstants(
|
|
518
899
|
php,
|
|
519
900
|
options.absoluteUrl,
|
|
@@ -530,8 +911,9 @@ async function startWPApp(options) {
|
|
|
530
911
|
return { requestHandler, php, options, freshSite };
|
|
531
912
|
}
|
|
532
913
|
async function applyProjectMounts(php, mode, projectPath) {
|
|
533
|
-
const projectName =
|
|
914
|
+
const projectName = path10.basename(projectPath);
|
|
534
915
|
switch (mode) {
|
|
916
|
+
case "wp-app" /* WP_APP */:
|
|
535
917
|
case "theme" /* THEME */:
|
|
536
918
|
await mountAt(
|
|
537
919
|
php,
|
|
@@ -547,13 +929,13 @@ async function applyProjectMounts(php, mode, projectPath) {
|
|
|
547
929
|
);
|
|
548
930
|
break;
|
|
549
931
|
case "wp-content" /* WP_CONTENT */:
|
|
550
|
-
for (const entry of
|
|
932
|
+
for (const entry of fs11.readdirSync(projectPath)) {
|
|
551
933
|
if (entry === "index.php") {
|
|
552
934
|
continue;
|
|
553
935
|
}
|
|
554
936
|
await mountAt(
|
|
555
937
|
php,
|
|
556
|
-
|
|
938
|
+
path10.join(projectPath, entry),
|
|
557
939
|
`${DOCROOT}/wp-content/${entry}`
|
|
558
940
|
);
|
|
559
941
|
}
|
|
@@ -563,14 +945,14 @@ async function applyProjectMounts(php, mode, projectPath) {
|
|
|
563
945
|
}
|
|
564
946
|
}
|
|
565
947
|
async function mountAt(php, hostPath, vfsPath) {
|
|
566
|
-
if (
|
|
948
|
+
if (fs11.statSync(hostPath).isDirectory()) {
|
|
567
949
|
php.mkdir(vfsPath);
|
|
568
950
|
}
|
|
569
951
|
await php.mount(vfsPath, createNodeFsMountHandler(hostPath));
|
|
570
952
|
}
|
|
571
953
|
async function activateProject(php, mode, projectPath) {
|
|
572
|
-
const projectName =
|
|
573
|
-
if (mode === "theme" /* THEME */) {
|
|
954
|
+
const projectName = path10.basename(projectPath);
|
|
955
|
+
if (mode === "wp-app" /* WP_APP */ || mode === "theme" /* THEME */) {
|
|
574
956
|
await runWordPressCode(
|
|
575
957
|
php,
|
|
576
958
|
`switch_theme(${phpString(projectName)});`
|
|
@@ -615,11 +997,11 @@ function phpString(value) {
|
|
|
615
997
|
}
|
|
616
998
|
function findPluginFile(projectPath) {
|
|
617
999
|
const pluginNameRegex = /^(?:[ \t]*<\?php)?[ \t/*#@]*Plugin Name:(.*)$/im;
|
|
618
|
-
for (const file of
|
|
1000
|
+
for (const file of fs11.readdirSync(projectPath)) {
|
|
619
1001
|
if (!file.endsWith(".php")) {
|
|
620
1002
|
continue;
|
|
621
1003
|
}
|
|
622
|
-
const content =
|
|
1004
|
+
const content = fs11.readFileSync(path10.join(projectPath, file), "utf8");
|
|
623
1005
|
if (pluginNameRegex.test(content)) {
|
|
624
1006
|
return file;
|
|
625
1007
|
}
|
|
@@ -627,8 +1009,10 @@ function findPluginFile(projectPath) {
|
|
|
627
1009
|
return null;
|
|
628
1010
|
}
|
|
629
1011
|
function inferMode(projectPath) {
|
|
630
|
-
if (
|
|
631
|
-
return "
|
|
1012
|
+
if (isWpAppProject(projectPath)) {
|
|
1013
|
+
return "wp-app" /* WP_APP */;
|
|
1014
|
+
} else if (isClassicPressDirectory(projectPath)) {
|
|
1015
|
+
return "classicpress" /* CLASSICPRESS */;
|
|
632
1016
|
} else if (isWordPressDirectory(projectPath)) {
|
|
633
1017
|
return "wordpress" /* WORDPRESS */;
|
|
634
1018
|
} else if (isWpContentDirectory(projectPath)) {
|
|
@@ -640,12 +1024,12 @@ function inferMode(projectPath) {
|
|
|
640
1024
|
} else if (hasIndexFile(projectPath)) {
|
|
641
1025
|
return "index" /* INDEX */;
|
|
642
1026
|
}
|
|
643
|
-
return
|
|
1027
|
+
return null;
|
|
644
1028
|
}
|
|
645
1029
|
|
|
646
1030
|
// src/start-server.ts
|
|
647
1031
|
async function startServer(options = {}) {
|
|
648
|
-
if (!
|
|
1032
|
+
if (!fs12.existsSync(options.projectPath)) {
|
|
649
1033
|
throw new Error(
|
|
650
1034
|
`The given path "${options.projectPath}" does not exist.`
|
|
651
1035
|
);
|
|
@@ -716,8 +1100,8 @@ var parseHeaders = (req) => {
|
|
|
716
1100
|
};
|
|
717
1101
|
|
|
718
1102
|
// src/execute-php.ts
|
|
719
|
-
import
|
|
720
|
-
import
|
|
1103
|
+
import fs13 from "fs";
|
|
1104
|
+
import path11 from "path";
|
|
721
1105
|
import { loadNodeRuntime as loadNodeRuntime2 } from "@php-wasm/node";
|
|
722
1106
|
import { PHP as PHP2 } from "@php-wasm/universal";
|
|
723
1107
|
async function executePHP(phpArgs, options) {
|
|
@@ -725,8 +1109,8 @@ async function executePHP(phpArgs, options) {
|
|
|
725
1109
|
let code;
|
|
726
1110
|
if (args[0] === "-r" && typeof args[1] === "string") {
|
|
727
1111
|
code = `<?php ${args[1]}`;
|
|
728
|
-
} else if (args[0] &&
|
|
729
|
-
code =
|
|
1112
|
+
} else if (args[0] && fs13.existsSync(path11.resolve(args[0]))) {
|
|
1113
|
+
code = fs13.readFileSync(path11.resolve(args[0]), "utf8");
|
|
730
1114
|
} else {
|
|
731
1115
|
throw new Error(
|
|
732
1116
|
'Usage: wp-app php -- -r "<code>" | wp-app php -- <script.php>'
|
|
@@ -748,14 +1132,42 @@ async function executePHP(phpArgs, options) {
|
|
|
748
1132
|
// src/run-cli.ts
|
|
749
1133
|
var MODE_CHOICES = [
|
|
750
1134
|
"auto",
|
|
1135
|
+
"wp-app",
|
|
751
1136
|
"plugin",
|
|
752
1137
|
"theme",
|
|
753
1138
|
"wordpress",
|
|
754
|
-
"
|
|
1139
|
+
"classicpress",
|
|
755
1140
|
"wp-content",
|
|
756
|
-
"index"
|
|
757
|
-
"playground"
|
|
1141
|
+
"index"
|
|
758
1142
|
];
|
|
1143
|
+
var SERVER_OPTIONS = {
|
|
1144
|
+
path: { type: "string" },
|
|
1145
|
+
php: { type: "string" },
|
|
1146
|
+
wp: { type: "string" },
|
|
1147
|
+
port: { type: "string" },
|
|
1148
|
+
mode: { type: "string" }
|
|
1149
|
+
};
|
|
1150
|
+
var HELP = `wp-app <command> [args]
|
|
1151
|
+
|
|
1152
|
+
Commands:
|
|
1153
|
+
wp-app start Start the server
|
|
1154
|
+
wp-app php Run the php command passing the arguments to php cli
|
|
1155
|
+
wp-app build Regenerate the composer autoloader (if vendor/ exists) and run the npm build script, when present
|
|
1156
|
+
wp-app dev Start the server; watch CSS/JS source files and rebuild + refresh on change
|
|
1157
|
+
wp-app install Run npm install and composer install
|
|
1158
|
+
wp-app update Run composer update (optionally scoped to specific packages)
|
|
1159
|
+
wp-app create Scaffold a new wp-app project
|
|
1160
|
+
|
|
1161
|
+
Options:
|
|
1162
|
+
--path=<dir> Path to the PHP or WordPress project. Defaults to the current working directory.
|
|
1163
|
+
--php=<ver> PHP version to use.
|
|
1164
|
+
--wp=<ver> WordPress version to use, e.g. '--wp=6.4'. Use '--wp=classicpress' for ClassicPress.
|
|
1165
|
+
--port=<n> Server port
|
|
1166
|
+
--mode=<mode> Project mode (${MODE_CHOICES.join("|")}). Defaults to auto-detection.
|
|
1167
|
+
--reset (start) Create a new site environment, destroying the old one.
|
|
1168
|
+
--no-open (start/dev) Don't open the site in the default browser.
|
|
1169
|
+
-h, --help Show this help
|
|
1170
|
+
`;
|
|
759
1171
|
function startSpinner(message) {
|
|
760
1172
|
process.stdout.write(`${message}...
|
|
761
1173
|
`);
|
|
@@ -768,218 +1180,219 @@ function startSpinner(message) {
|
|
|
768
1180
|
}
|
|
769
1181
|
};
|
|
770
1182
|
}
|
|
771
|
-
function
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
describe: "PHP version to use.",
|
|
777
|
-
type: "string"
|
|
778
|
-
});
|
|
1183
|
+
function fail(message) {
|
|
1184
|
+
console.error(`wp-app: ${message}
|
|
1185
|
+
`);
|
|
1186
|
+
console.error(HELP);
|
|
1187
|
+
process.exit(1);
|
|
779
1188
|
}
|
|
780
|
-
function
|
|
781
|
-
|
|
782
|
-
yargs2.option("wp", {
|
|
783
|
-
describe: "WordPress version to use, e.g. '--wp=6.4'. Use '--wp=classicpress' for ClassicPress.",
|
|
784
|
-
type: "string"
|
|
785
|
-
});
|
|
786
|
-
yargs2.option("port", {
|
|
787
|
-
describe: "Server port",
|
|
788
|
-
type: "number"
|
|
789
|
-
});
|
|
790
|
-
yargs2.option("mode", {
|
|
791
|
-
describe: "Project mode. Defaults to auto-detection from the project directory.",
|
|
792
|
-
type: "string",
|
|
793
|
-
choices: MODE_CHOICES
|
|
794
|
-
});
|
|
1189
|
+
function hasNegatedFlag(args, flag) {
|
|
1190
|
+
return args.includes(`--no-${flag}`);
|
|
795
1191
|
}
|
|
796
1192
|
async function runCli() {
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
1193
|
+
const rawArgs = process.argv.slice(2);
|
|
1194
|
+
if (rawArgs.includes("-h") || rawArgs.includes("--help")) {
|
|
1195
|
+
console.log(HELP);
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
const command = rawArgs[0];
|
|
1199
|
+
if (!command || command.startsWith("-")) {
|
|
1200
|
+
fail("You must provide a valid command");
|
|
1201
|
+
}
|
|
1202
|
+
if (command === "php") {
|
|
1203
|
+
return runPhpCommand(rawArgs);
|
|
1204
|
+
}
|
|
1205
|
+
if (command === "create") {
|
|
1206
|
+
const { runCreateCommand: runCreateCommand2 } = await Promise.resolve().then(() => (init_create(), create_exports));
|
|
1207
|
+
const target = rawArgs[1] || ".";
|
|
1208
|
+
return runCreateCommand2(target);
|
|
1209
|
+
}
|
|
1210
|
+
const { values } = parseArgs({
|
|
1211
|
+
args: rawArgs.slice(1),
|
|
1212
|
+
options: { ...SERVER_OPTIONS, reset: { type: "boolean" } },
|
|
1213
|
+
allowPositionals: true,
|
|
1214
|
+
strict: false
|
|
1215
|
+
});
|
|
1216
|
+
if (command === "install") {
|
|
1217
|
+
return runInstallCommand(values);
|
|
1218
|
+
}
|
|
1219
|
+
if (command === "update") {
|
|
1220
|
+
return runUpdateCommand(values, rawArgs);
|
|
1221
|
+
}
|
|
1222
|
+
if (values.mode && !MODE_CHOICES.includes(values.mode)) {
|
|
1223
|
+
fail(
|
|
1224
|
+
`Invalid mode "${values.mode}". Choices: ${MODE_CHOICES.join(", ")}`
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
const config = {
|
|
1228
|
+
php: values.php,
|
|
1229
|
+
path: values.path
|
|
1230
|
+
};
|
|
1231
|
+
config.wp = values.wp;
|
|
1232
|
+
config.port = values.port ? Number(values.port) : void 0;
|
|
1233
|
+
config.mode = values.mode;
|
|
1234
|
+
try {
|
|
1235
|
+
await getWpAppConfig(config);
|
|
1236
|
+
} catch (error) {
|
|
1237
|
+
fail(error.message);
|
|
1238
|
+
}
|
|
1239
|
+
switch (command) {
|
|
1240
|
+
case "start":
|
|
1241
|
+
return runStartCommand(values, rawArgs);
|
|
1242
|
+
case "build":
|
|
1243
|
+
return runBuildCommand(values);
|
|
1244
|
+
case "dev":
|
|
1245
|
+
return runDevCommand(values, rawArgs);
|
|
1246
|
+
default:
|
|
1247
|
+
fail(`Unknown command "${command}"`);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
async function runStartCommand(values, rawArgs) {
|
|
1251
|
+
const spinner = startSpinner("Starting the server...");
|
|
1252
|
+
try {
|
|
1253
|
+
const options = await getWpAppConfig({
|
|
1254
|
+
path: values.path,
|
|
1255
|
+
php: values.php,
|
|
1256
|
+
wp: values.wp,
|
|
1257
|
+
port: values.port ? Number(values.port) : void 0,
|
|
1258
|
+
mode: values.mode
|
|
1259
|
+
});
|
|
1260
|
+
portFinder.setPort(options.port);
|
|
1261
|
+
if (values.reset) {
|
|
1262
|
+
if (["wordpress", "classicpress", "index"].includes(
|
|
1263
|
+
options.mode
|
|
1264
|
+
)) {
|
|
1265
|
+
output?.log(
|
|
1266
|
+
"--reset only applies to wp-app managed sites; your project files are never touched."
|
|
1267
|
+
);
|
|
1268
|
+
} else {
|
|
1269
|
+
fs15.rmSync(getSitePath(options.projectPath), {
|
|
1270
|
+
recursive: true,
|
|
1271
|
+
force: true,
|
|
1272
|
+
maxRetries: 10,
|
|
1273
|
+
retryDelay: 100
|
|
1274
|
+
});
|
|
1275
|
+
output?.log("Site environment reset.");
|
|
1276
|
+
}
|
|
800
1277
|
}
|
|
801
|
-
const
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
};
|
|
805
|
-
if (argv._[0] !== "php") {
|
|
806
|
-
config.wp = argv.wp;
|
|
807
|
-
config.port = argv.port;
|
|
808
|
-
config.mode = argv.mode;
|
|
1278
|
+
const { url } = await startServer(options);
|
|
1279
|
+
if (!hasNegatedFlag(rawArgs, "open")) {
|
|
1280
|
+
openInDefaultBrowser(url);
|
|
809
1281
|
}
|
|
1282
|
+
} catch (error) {
|
|
1283
|
+
output?.error(error);
|
|
1284
|
+
spinner.fail(`Failed to start the server: ${error.message}`);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
async function runPhpCommand(rawArgs) {
|
|
1288
|
+
const args = rawArgs.slice(1);
|
|
1289
|
+
const pathIndex = args.findIndex((arg) => arg.startsWith("--path="));
|
|
1290
|
+
const phpIndex = args.findIndex((arg) => arg.startsWith("--php="));
|
|
1291
|
+
try {
|
|
1292
|
+
const options = await getWpAppConfig({
|
|
1293
|
+
path: pathIndex >= 0 ? args[pathIndex].split("=")[1] : void 0,
|
|
1294
|
+
php: phpIndex >= 0 ? args[phpIndex].split("=")[1] : void 0
|
|
1295
|
+
});
|
|
1296
|
+
const phpArgs = args.filter(
|
|
1297
|
+
(arg, i) => i !== pathIndex && i !== phpIndex
|
|
1298
|
+
);
|
|
1299
|
+
await executePHP(phpArgs, options);
|
|
1300
|
+
process.exit(0);
|
|
1301
|
+
} catch (error) {
|
|
1302
|
+
console.error(error);
|
|
1303
|
+
process.exit(error.status || -1);
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
async function runBuildCommand(values) {
|
|
1307
|
+
const projectPath = values.path ? path13.resolve(values.path) : process.cwd();
|
|
1308
|
+
if (fs15.existsSync(path13.join(projectPath, "vendor"))) {
|
|
810
1309
|
try {
|
|
811
|
-
|
|
1310
|
+
execSync("composer dump-autoload --optimize --classmap-authoritative", {
|
|
1311
|
+
cwd: projectPath,
|
|
1312
|
+
stdio: "inherit"
|
|
1313
|
+
});
|
|
812
1314
|
} catch (error) {
|
|
813
|
-
|
|
1315
|
+
console.error(
|
|
1316
|
+
`composer dump-autoload failed: ${error.message}`
|
|
1317
|
+
);
|
|
814
1318
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
(
|
|
820
|
-
|
|
821
|
-
yargs2.option("reset", {
|
|
822
|
-
describe: "Create a new site environment, destroying the old one (WordPress files and database).",
|
|
823
|
-
type: "boolean",
|
|
824
|
-
default: false
|
|
825
|
-
});
|
|
826
|
-
yargs2.option("open", {
|
|
827
|
-
describe: "Open the site in the default browser.",
|
|
828
|
-
type: "boolean",
|
|
829
|
-
default: true
|
|
830
|
-
});
|
|
831
|
-
},
|
|
832
|
-
async (argv) => {
|
|
833
|
-
const spinner = startSpinner("Starting the server...");
|
|
834
|
-
try {
|
|
835
|
-
const options = await getWpAppConfig({
|
|
836
|
-
path: argv.path,
|
|
837
|
-
php: argv.php,
|
|
838
|
-
wp: argv.wp,
|
|
839
|
-
port: argv.port,
|
|
840
|
-
mode: argv.mode
|
|
841
|
-
});
|
|
842
|
-
portFinder.setPort(options.port);
|
|
843
|
-
if (argv.reset) {
|
|
844
|
-
if (["wordpress", "wordpress-develop", "index"].includes(
|
|
845
|
-
options.mode
|
|
846
|
-
)) {
|
|
847
|
-
output?.log(
|
|
848
|
-
"--reset only applies to wp-app managed sites; your project files are never touched."
|
|
849
|
-
);
|
|
850
|
-
} else {
|
|
851
|
-
fs13.rmSync(getSitePath(options.projectPath), {
|
|
852
|
-
recursive: true,
|
|
853
|
-
force: true,
|
|
854
|
-
maxRetries: 10,
|
|
855
|
-
retryDelay: 100
|
|
856
|
-
});
|
|
857
|
-
output?.log("Site environment reset.");
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
const { url } = await startServer(options);
|
|
861
|
-
if (argv.open) {
|
|
862
|
-
openInDefaultBrowser(url);
|
|
863
|
-
}
|
|
864
|
-
} catch (error) {
|
|
865
|
-
output?.error(error);
|
|
866
|
-
spinner.fail(
|
|
867
|
-
`Failed to start the server: ${error.message}`
|
|
868
|
-
);
|
|
869
|
-
}
|
|
1319
|
+
}
|
|
1320
|
+
const packageJsonPath = path13.join(projectPath, "package.json");
|
|
1321
|
+
if (fs15.existsSync(packageJsonPath)) {
|
|
1322
|
+
const pkg = JSON.parse(fs15.readFileSync(packageJsonPath, "utf8"));
|
|
1323
|
+
if (pkg.scripts?.build) {
|
|
1324
|
+
execSync("npm run build", { cwd: projectPath, stdio: "inherit" });
|
|
870
1325
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1326
|
+
}
|
|
1327
|
+
console.log("Build complete.");
|
|
1328
|
+
}
|
|
1329
|
+
async function runInstallCommand(values) {
|
|
1330
|
+
const projectPath = values.path ? path13.resolve(values.path) : process.cwd();
|
|
1331
|
+
if (fs15.existsSync(path13.join(projectPath, "package.json"))) {
|
|
1332
|
+
execSync("npm install", { cwd: projectPath, stdio: "inherit" });
|
|
1333
|
+
}
|
|
1334
|
+
if (fs15.existsSync(path13.join(projectPath, "composer.json"))) {
|
|
1335
|
+
execSync("composer install", { cwd: projectPath, stdio: "inherit" });
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
async function runUpdateCommand(values, rawArgs) {
|
|
1339
|
+
const projectPath = values.path ? path13.resolve(values.path) : process.cwd();
|
|
1340
|
+
const packages = rawArgs.slice(1).filter((arg) => !arg.startsWith("--"));
|
|
1341
|
+
const args = ["update", ...packages];
|
|
1342
|
+
runCommand("composer", args, { cwd: projectPath, stdio: "inherit" });
|
|
1343
|
+
}
|
|
1344
|
+
var WATCHED_EXTENSIONS = [".css", ".scss", ".js", ".ts", ".jsx", ".tsx"];
|
|
1345
|
+
var IGNORED_DIRS = ["node_modules", "vendor", ".git", "dist", "assets"];
|
|
1346
|
+
async function runDevCommand(values, rawArgs) {
|
|
1347
|
+
const projectPath = path13.resolve(values.path || process.cwd());
|
|
1348
|
+
const spinner = startSpinner("Starting the server...");
|
|
1349
|
+
try {
|
|
1350
|
+
const options = await getWpAppConfig({
|
|
1351
|
+
path: values.path,
|
|
1352
|
+
php: values.php,
|
|
1353
|
+
wp: values.wp,
|
|
1354
|
+
port: values.port ? Number(values.port) : void 0,
|
|
1355
|
+
mode: values.mode
|
|
1356
|
+
});
|
|
1357
|
+
portFinder.setPort(options.port);
|
|
1358
|
+
const { url } = await startServer(options);
|
|
1359
|
+
if (!hasNegatedFlag(rawArgs, "open")) {
|
|
1360
|
+
openInDefaultBrowser(url);
|
|
892
1361
|
}
|
|
893
|
-
)
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
);
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
const packageJsonPath = path11.join(projectPath, "package.json");
|
|
916
|
-
if (fs13.existsSync(packageJsonPath)) {
|
|
917
|
-
const pkg = JSON.parse(
|
|
918
|
-
fs13.readFileSync(packageJsonPath, "utf8")
|
|
919
|
-
);
|
|
920
|
-
if (pkg.scripts?.build) {
|
|
921
|
-
console.log("Running npm build...");
|
|
922
|
-
execSync("npm run build", {
|
|
923
|
-
cwd: projectPath,
|
|
924
|
-
stdio: "inherit"
|
|
925
|
-
});
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
console.log("Build complete.");
|
|
1362
|
+
} catch (error) {
|
|
1363
|
+
output?.error(error);
|
|
1364
|
+
spinner.fail(`Failed to start the server: ${error.message}`);
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
const packageJsonPath = path13.join(projectPath, "package.json");
|
|
1368
|
+
const hasBuildScript = fs15.existsSync(packageJsonPath) && JSON.parse(fs15.readFileSync(packageJsonPath, "utf8")).scripts?.build;
|
|
1369
|
+
if (!hasBuildScript) {
|
|
1370
|
+
console.log("Dev mode: no npm build script found, nothing to watch.");
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
let rebuildTimer = null;
|
|
1374
|
+
const rebuild = (filename) => {
|
|
1375
|
+
console.log(`Change detected: ${filename}. Rebuilding...`);
|
|
1376
|
+
try {
|
|
1377
|
+
execSync("npm run build", { cwd: projectPath, stdio: "inherit" });
|
|
1378
|
+
console.log("Rebuild complete. Refresh your browser to see changes.");
|
|
1379
|
+
} catch (error) {
|
|
1380
|
+
console.error(`Rebuild failed: ${error.message}`);
|
|
929
1381
|
}
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
(
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
argv.path || process.cwd()
|
|
940
|
-
);
|
|
941
|
-
const IGNORED = ["node_modules", "vendor", ".git", "dist"];
|
|
942
|
-
let child = null;
|
|
943
|
-
let restartTimer = null;
|
|
944
|
-
let firstRun = true;
|
|
945
|
-
const startChild = () => {
|
|
946
|
-
const args = [selfScript, "start"];
|
|
947
|
-
if (argv.path) args.push(`--path=${argv.path}`);
|
|
948
|
-
if (argv.php) args.push(`--php=${argv.php}`);
|
|
949
|
-
if (argv.wp) args.push(`--wp=${argv.wp}`);
|
|
950
|
-
if (argv.port) args.push(`--port=${argv.port}`);
|
|
951
|
-
if (argv.mode) args.push(`--mode=${argv.mode}`);
|
|
952
|
-
if (!firstRun) args.push("--no-open");
|
|
953
|
-
firstRun = false;
|
|
954
|
-
child = spawn(process.execPath, args, {
|
|
955
|
-
stdio: "inherit"
|
|
956
|
-
});
|
|
957
|
-
};
|
|
958
|
-
startChild();
|
|
959
|
-
fs13.watch(
|
|
960
|
-
watchDir,
|
|
961
|
-
{ recursive: true },
|
|
962
|
-
(_eventType, filename) => {
|
|
963
|
-
if (!filename) return;
|
|
964
|
-
const parts = filename.split(/[\\/]/);
|
|
965
|
-
if (parts.some(
|
|
966
|
-
(part) => IGNORED.includes(part) || part.startsWith(".")
|
|
967
|
-
)) {
|
|
968
|
-
return;
|
|
969
|
-
}
|
|
970
|
-
clearTimeout(restartTimer);
|
|
971
|
-
restartTimer = setTimeout(() => {
|
|
972
|
-
console.log(
|
|
973
|
-
`Change detected: ${filename}. Restarting...`
|
|
974
|
-
);
|
|
975
|
-
child?.kill();
|
|
976
|
-
startChild();
|
|
977
|
-
}, 500);
|
|
978
|
-
}
|
|
979
|
-
);
|
|
980
|
-
console.log("Dev mode: watching for file changes...");
|
|
1382
|
+
};
|
|
1383
|
+
fs15.watch(projectPath, { recursive: true }, (_eventType, filename) => {
|
|
1384
|
+
if (!filename) return;
|
|
1385
|
+
const parts = filename.split(/[\\/]/);
|
|
1386
|
+
if (parts.some((part) => IGNORED_DIRS.includes(part) || part.startsWith("."))) {
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
if (!WATCHED_EXTENSIONS.includes(path13.extname(filename))) {
|
|
1390
|
+
return;
|
|
981
1391
|
}
|
|
982
|
-
|
|
1392
|
+
if (rebuildTimer) clearTimeout(rebuildTimer);
|
|
1393
|
+
rebuildTimer = setTimeout(() => rebuild(filename), 300);
|
|
1394
|
+
});
|
|
1395
|
+
console.log("Dev mode: watching CSS/JS source files for changes...");
|
|
983
1396
|
}
|
|
984
1397
|
function openInDefaultBrowser(url) {
|
|
985
1398
|
if (isGitHubCodespace) {
|
|
@@ -1016,4 +1429,8 @@ if (currentNodeVersion < requiredMajorVersion) {
|
|
|
1016
1429
|
`You are running Node.js version ${currentNodeVersion}, but this application requires at least Node.js ${requiredMajorVersion}. Please upgrade your Node.js version.`
|
|
1017
1430
|
);
|
|
1018
1431
|
}
|
|
1432
|
+
try {
|
|
1433
|
+
process.loadEnvFile();
|
|
1434
|
+
} catch {
|
|
1435
|
+
}
|
|
1019
1436
|
runCli();
|