wp-studio 1.7.10 → 1.7.11-beta1
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/dist/cli/{_events-BcapW3eh.mjs → _events-D_POEBYs.mjs} +4 -5
- package/dist/cli/appdata-D-luHxJU.mjs +19 -0
- package/dist/cli/{certificate-manager-SVYcCL_i.mjs → certificate-manager-v-yNLDFJ.mjs} +134 -15
- package/dist/cli/{delete-D1924O3o.mjs → delete-C4R7DV_E.mjs} +3 -3
- package/dist/cli/{helpers-oQuItT8n.mjs → helpers-CBl4GQzX.mjs} +2 -2
- package/dist/cli/{index-4lan3TI_.mjs → index-Bej4fL6n.mjs} +31 -4
- package/dist/cli/{index-DRQnCQvM.mjs → index-DYlrlauo.mjs} +1308 -1109
- package/dist/cli/{index-BjzOJKPi.mjs → index-jpyzVGrl.mjs} +90 -52
- package/dist/cli/{list-DOFyyV1f.mjs → list-BRllR-8H.mjs} +3 -3
- package/dist/cli/{login-BtPZeZ4G.mjs → login-CDVrFvfk.mjs} +3 -3
- package/dist/cli/{logout-Cr631QzG.mjs → logout-DLAXkbrZ.mjs} +2 -3
- package/dist/cli/main.mjs +2 -2
- package/dist/cli/{paths-CqXGLB7R.mjs → paths-D7DniT1Q.mjs} +7 -6
- package/dist/cli/plugin/skills/rank-me-up/SKILL.md +166 -0
- package/dist/cli/plugin/skills/site-spec/SKILL.md +4 -0
- package/dist/cli/process-manager-daemon.mjs +29 -5
- package/dist/cli/{process-manager-ipc-BisO0qtU.mjs → process-manager-ipc-GCdebuBH.mjs} +4 -1
- package/dist/cli/proxy-daemon.mjs +1 -1
- package/dist/cli/{prune-pm-logs-COryxqeo.mjs → prune-pm-logs-Dm_Bwi7l.mjs} +1 -1
- package/dist/cli/{resume-BwDwdJtq.mjs → resume-B8M2e-Ii.mjs} +4 -15
- package/dist/cli/{rewrite-wp-cli-post-content-2zlfFnKT.mjs → rewrite-wp-cli-post-content-Beo5_Ojo.mjs} +32 -531
- package/dist/cli/{set-D5eeqHbp.mjs → set-CizYT2gE.mjs} +2 -3
- package/dist/cli/{set-DYnzUz_G.mjs → set-_5ABvjxt.mjs} +4 -5
- package/dist/cli/{status-DNvMZBqD.mjs → status-D6Huwi6x.mjs} +2 -2
- package/dist/cli/well-known-paths-QcSJNi_l.mjs +95 -0
- package/dist/cli/wordpress-server-child.mjs +5 -3
- package/dist/cli/{wp-DD2-QiiP.mjs → wp-BevvZcM1.mjs} +2 -2
- package/dist/cli/wp-files/latest/available-site-translations.json +1 -1
- package/dist/cli/wp-files/sqlite-database-integration/admin-page.php +1 -2
- package/dist/cli/wp-files/sqlite-database-integration/constants.php +0 -5
- package/dist/cli/wp-files/sqlite-database-integration/load.php +1 -1
- package/dist/cli/wp-files/sqlite-database-integration/readme.txt +6 -3
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/database/sqlite/class-wp-pdo-mysql-on-sqlite.php +22 -3
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/database/version.php +1 -1
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-db.php +41 -89
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/db.php +2 -24
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/install-functions.php +7 -16
- package/package.json +2 -1
- package/dist/cli/well-known-paths-BYA1Bw5o.mjs +0 -214
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-lexer.php +0 -2575
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-pdo-user-defined-functions.php +0 -899
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-query-rewriter.php +0 -343
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-token.php +0 -327
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/class-wp-sqlite-translator.php +0 -4543
- package/dist/cli/wp-files/sqlite-database-integration/wp-includes/sqlite/php-polyfills.php +0 -68
|
@@ -49,17 +49,18 @@ import semver from "semver";
|
|
|
49
49
|
import yargs from "yargs";
|
|
50
50
|
import * as path from "path";
|
|
51
51
|
import path__default from "path";
|
|
52
|
-
import { L as
|
|
52
|
+
import { L as LOCKFILE_STALE_TIME, a as LOCKFILE_WAIT_TIME, b as LatestSupportedPHPVersion, D as DEFAULT_PHP_VERSION, A as ARCHIVER_OPTIONS, S as SupportedPHPVersionsList, P as PLAYGROUND_CLI_MAX_TIMEOUT, c as PLAYGROUND_CLI_INACTIVITY_TIMEOUT, d as PLAYGROUND_CLI_ACTIVITY_CHECK_INTERVAL, g as getServerFilesPath, e as getSharedConfigPath, f as getConfigDirectory, h as SHARED_CONFIG_LOCKFILE_NAME, i as DEMO_SITE_EXPIRATION_DAYS, H as HOUR_MS, j as DAY_MS, k as DEMO_SITE_SIZE_LIMIT_BYTES, l as DEMO_SITE_SIZE_LIMIT_GB, M as MINIMUM_WORDPRESS_VERSION, m as DEFAULT_WORDPRESS_VERSION$1, n as getCliConfigPath } from "./well-known-paths-QcSJNi_l.mjs";
|
|
53
|
+
import * as fs from "fs";
|
|
54
|
+
import fs__default, { promises, constants, existsSync } from "fs";
|
|
55
|
+
import ignore from "ignore";
|
|
56
|
+
import { D as DEPLOY_IGNORE_DEFAULTS, a as arePathsEqual, i as isWordPressDirectory, g as getWordPressVersionPath, r as recursiveCopyDirectory, b as getWordPressVersionUrl, I as IS_JSPI_AVAILABLE, c as cleanupLegacyMuPlugins, d as getMuPlugins, e as getWpCliPharPath, f as getSqliteCommandPath, k as keepSqliteIntegrationUpdated, h as calculateDirectorySizeForArchive, p as pathExists, j as isWordPressBetaVersion, l as isWordPressDevVersion, m as getWpFilesPath, n as getLanguagePacksPath, o as isEmptyDir, q as decodePassword, s as getAiInstructionsPath, v as validateAdminUsername, t as validateAdminEmail, u as encodePassword, w as createPassword, x as isValidWordPressVersion, y as isWordPressVersionAtLeast, z as getUnsupportedWpCliPostContentMessage } from "./rewrite-wp-cli-post-content-Beo5_Ojo.mjs";
|
|
57
|
+
import { i as isErrnoException } from "./wordpress-server-ipc-D-8mLjOF.mjs";
|
|
53
58
|
import { z } from "zod";
|
|
54
|
-
import {
|
|
55
|
-
import
|
|
56
|
-
import require$$0$7, { spawn } from "child_process";
|
|
59
|
+
import { l as lockCliConfig, r as readCliConfig, L as LoggerError, s as saveCliConfig, u as unlockCliConfig, a as lockFileAsync, b as unlockFileAsync, c as Logger, S as SITE_EVENTS, g as getDefaultExportFromCjs, d as commonjsGlobal, e as authTokenSchema, h as hideDirectoryOnWindows, f as SNAPSHOT_EVENTS, i as requireSignalExit, j as StatsMetric, k as StatsGroup, m as updateCliConfigWithPartial, n as generateSiteCertificate, o as deleteSiteCertificate, p as emitProgress, q as setProgressCallback, t as getProgressCallback, v as updateCheckSchema } from "./certificate-manager-v-yNLDFJ.mjs";
|
|
60
|
+
import require$$0$7, { spawn, execFile } from "child_process";
|
|
57
61
|
import crypto$1 from "crypto";
|
|
58
62
|
import { EventEmitter } from "events";
|
|
59
|
-
import
|
|
60
|
-
import fs__default, { constants } from "fs";
|
|
61
|
-
import { i as isErrnoException } from "./wordpress-server-ipc-D-8mLjOF.mjs";
|
|
62
|
-
import { P as PROCESS_MANAGER_HOME, p as processDescriptionSchema, S as SocketRequestClient, a as PROCESS_MANAGER_EVENTS_SOCKET_PATH, b as PROCESS_MANAGER_CONTROL_SOCKET_PATH, d as daemonResponseSchema, c as SocketMessageDecoder, e as SocketStreamClient, f as daemonEventSchema } from "./process-manager-ipc-BisO0qtU.mjs";
|
|
63
|
+
import { P as PROCESS_MANAGER_HOME, p as processDescriptionSchema, S as SocketRequestClient, a as PROCESS_MANAGER_EVENTS_SOCKET_PATH, b as PROCESS_MANAGER_CONTROL_SOCKET_PATH, d as daemonResponseSchema, c as SocketMessageDecoder, e as SocketStreamClient, f as daemonEventSchema } from "./process-manager-ipc-GCdebuBH.mjs";
|
|
63
64
|
import fs$1, { readFile as readFile$2, cp } from "fs/promises";
|
|
64
65
|
import os, { tmpdir, platform } from "os";
|
|
65
66
|
import archiver from "archiver";
|
|
@@ -81,12 +82,12 @@ import trash from "trash";
|
|
|
81
82
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
82
83
|
import { tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";
|
|
83
84
|
import { z as z$1 } from "zod/v4";
|
|
85
|
+
import require$$2, { fileURLToPath } from "url";
|
|
84
86
|
import require$$0$1 from "tty";
|
|
85
87
|
import require$$1$1 from "constants";
|
|
86
88
|
import require$$0$2 from "assert";
|
|
87
89
|
import require$$0$3 from "http";
|
|
88
90
|
import require$$1$3 from "https";
|
|
89
|
-
import require$$2 from "url";
|
|
90
91
|
import fs$2 from "node:fs";
|
|
91
92
|
import crypto$2 from "node:crypto";
|
|
92
93
|
import require$$2$1 from "node:events";
|
|
@@ -122,6 +123,20 @@ function suppressPunycodeWarning() {
|
|
|
122
123
|
}
|
|
123
124
|
});
|
|
124
125
|
}
|
|
126
|
+
const DEPLOY_IGNORE_FILENAME = ".deployignore";
|
|
127
|
+
async function createDeployIgnoreFilter(rootPath, basePatterns = DEPLOY_IGNORE_DEFAULTS) {
|
|
128
|
+
const ig = ignore().add(basePatterns);
|
|
129
|
+
const deployIgnorePath = path__default.join(rootPath, DEPLOY_IGNORE_FILENAME);
|
|
130
|
+
try {
|
|
131
|
+
const content = await promises.readFile(deployIgnorePath, "utf-8");
|
|
132
|
+
ig.add(content);
|
|
133
|
+
} catch (error2) {
|
|
134
|
+
if (!isErrnoException(error2) || error2.code !== "ENOENT") {
|
|
135
|
+
console.warn(`Failed to read ${deployIgnorePath}:`, error2);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return ig;
|
|
139
|
+
}
|
|
125
140
|
const BackupExtractEvents = {
|
|
126
141
|
BACKUP_EXTRACT_START: "backup_extract_start",
|
|
127
142
|
BACKUP_EXTRACT_PROGRESS: "backup_extract_progress",
|
|
@@ -153,6 +168,13 @@ const ImportEvents = {
|
|
|
153
168
|
...ValidatorEvents,
|
|
154
169
|
...ImporterEvents
|
|
155
170
|
};
|
|
171
|
+
const importerTypeSchema = z.union([
|
|
172
|
+
z.literal("jetpack"),
|
|
173
|
+
z.literal("local"),
|
|
174
|
+
z.literal("playground"),
|
|
175
|
+
z.literal("sql"),
|
|
176
|
+
z.literal("wpress")
|
|
177
|
+
]);
|
|
156
178
|
const backupExtractProgressEventDataSchema = z.object({
|
|
157
179
|
progress: z.number().optional(),
|
|
158
180
|
processedFiles: z.number().optional(),
|
|
@@ -177,10 +199,11 @@ const importWpContentProgressEventDataSchema = z.object({
|
|
|
177
199
|
processedBytes: z.number().optional(),
|
|
178
200
|
totalBytes: z.number().optional()
|
|
179
201
|
});
|
|
180
|
-
z.union([
|
|
202
|
+
const nullOrUndefined = z.union([z.undefined(), z.null()]);
|
|
203
|
+
const importEventTupleSchema = z.union([
|
|
181
204
|
z.tuple([
|
|
182
205
|
z.literal(BackupExtractEvents.BACKUP_EXTRACT_START),
|
|
183
|
-
backupExtractProgressEventDataSchema.or(
|
|
206
|
+
backupExtractProgressEventDataSchema.or(nullOrUndefined)
|
|
184
207
|
]),
|
|
185
208
|
z.tuple([
|
|
186
209
|
z.literal(BackupExtractEvents.BACKUP_EXTRACT_PROGRESS),
|
|
@@ -192,31 +215,32 @@ z.union([
|
|
|
192
215
|
]),
|
|
193
216
|
z.tuple([
|
|
194
217
|
z.literal(BackupExtractEvents.BACKUP_EXTRACT_COMPLETE),
|
|
195
|
-
backupExtractProgressEventDataSchema.or(
|
|
218
|
+
backupExtractProgressEventDataSchema.or(nullOrUndefined)
|
|
196
219
|
]),
|
|
197
220
|
z.tuple([z.literal(BackupExtractEvents.BACKUP_EXTRACT_WARNING), z.string()]),
|
|
198
221
|
z.tuple([z.literal(BackupExtractEvents.BACKUP_EXTRACT_ERROR), z.unknown()]),
|
|
199
|
-
z.tuple([z.literal(ValidatorEvents.IMPORT_VALIDATION_START),
|
|
200
|
-
z.tuple([z.literal(ValidatorEvents.IMPORT_VALIDATION_COMPLETE),
|
|
222
|
+
z.tuple([z.literal(ValidatorEvents.IMPORT_VALIDATION_START), nullOrUndefined]),
|
|
223
|
+
z.tuple([z.literal(ValidatorEvents.IMPORT_VALIDATION_COMPLETE), nullOrUndefined]),
|
|
201
224
|
z.tuple([z.literal(ValidatorEvents.IMPORT_VALIDATION_ERROR), z.unknown()]),
|
|
202
|
-
z.tuple([z.literal(ImporterEvents.IMPORT_START),
|
|
203
|
-
z.tuple([z.literal(ImporterEvents.IMPORT_DATABASE_START),
|
|
225
|
+
z.tuple([z.literal(ImporterEvents.IMPORT_START), importerTypeSchema]),
|
|
226
|
+
z.tuple([z.literal(ImporterEvents.IMPORT_DATABASE_START), nullOrUndefined]),
|
|
204
227
|
z.tuple([
|
|
205
228
|
z.literal(ImporterEvents.IMPORT_DATABASE_PROGRESS),
|
|
206
229
|
importDatabaseProgressEventDataSchema
|
|
207
230
|
]),
|
|
208
|
-
z.tuple([z.literal(ImporterEvents.IMPORT_DATABASE_COMPLETE),
|
|
209
|
-
z.tuple([z.literal(ImporterEvents.IMPORT_WP_CONTENT_START),
|
|
231
|
+
z.tuple([z.literal(ImporterEvents.IMPORT_DATABASE_COMPLETE), nullOrUndefined]),
|
|
232
|
+
z.tuple([z.literal(ImporterEvents.IMPORT_WP_CONTENT_START), nullOrUndefined]),
|
|
210
233
|
z.tuple([
|
|
211
234
|
z.literal(ImporterEvents.IMPORT_WP_CONTENT_PROGRESS),
|
|
212
235
|
importWpContentProgressEventDataSchema
|
|
213
236
|
]),
|
|
214
|
-
z.tuple([z.literal(ImporterEvents.IMPORT_WP_CONTENT_COMPLETE),
|
|
215
|
-
z.tuple([z.literal(ImporterEvents.IMPORT_META_START),
|
|
216
|
-
z.tuple([z.literal(ImporterEvents.IMPORT_META_COMPLETE),
|
|
217
|
-
z.tuple([z.literal(ImporterEvents.IMPORT_COMPLETE),
|
|
218
|
-
z.tuple([z.literal(ImporterEvents.IMPORT_ERROR), z.
|
|
237
|
+
z.tuple([z.literal(ImporterEvents.IMPORT_WP_CONTENT_COMPLETE), nullOrUndefined]),
|
|
238
|
+
z.tuple([z.literal(ImporterEvents.IMPORT_META_START), nullOrUndefined]),
|
|
239
|
+
z.tuple([z.literal(ImporterEvents.IMPORT_META_COMPLETE), nullOrUndefined]),
|
|
240
|
+
z.tuple([z.literal(ImporterEvents.IMPORT_COMPLETE), importerTypeSchema]),
|
|
241
|
+
z.tuple([z.literal(ImporterEvents.IMPORT_ERROR), z.string()])
|
|
219
242
|
]);
|
|
243
|
+
z.object({ event: importEventTupleSchema });
|
|
220
244
|
const ExportEvents = {
|
|
221
245
|
EXPORT_START: "export_start",
|
|
222
246
|
EXPORT_COMPLETE: "export_complete",
|
|
@@ -246,25 +270,39 @@ const backupCreateProgressEventDataSchema = z.object({
|
|
|
246
270
|
})
|
|
247
271
|
})
|
|
248
272
|
});
|
|
249
|
-
z.union([
|
|
250
|
-
z.tuple([z.literal(ExportEvents.EXPORT_START),
|
|
251
|
-
z.tuple([z.literal(ExportEvents.EXPORT_COMPLETE),
|
|
273
|
+
const exportEventTupleSchema = z.union([
|
|
274
|
+
z.tuple([z.literal(ExportEvents.EXPORT_START), nullOrUndefined]),
|
|
275
|
+
z.tuple([z.literal(ExportEvents.EXPORT_COMPLETE), nullOrUndefined]),
|
|
252
276
|
z.tuple([z.literal(ExportEvents.EXPORT_ERROR), z.unknown().nullable()]),
|
|
253
|
-
z.tuple([z.literal(ExportEvents.BACKUP_CREATE_START),
|
|
277
|
+
z.tuple([z.literal(ExportEvents.BACKUP_CREATE_START), nullOrUndefined]),
|
|
254
278
|
z.tuple([
|
|
255
279
|
z.literal(ExportEvents.BACKUP_CREATE_PROGRESS),
|
|
256
280
|
backupCreateProgressEventDataSchema
|
|
257
281
|
]),
|
|
258
|
-
z.tuple([z.literal(ExportEvents.BACKUP_CREATE_COMPLETE),
|
|
259
|
-
z.tuple([z.literal(ExportEvents.WP_CONTENT_EXPORT_START),
|
|
260
|
-
z.tuple([z.literal(ExportEvents.WP_CONTENT_EXPORT_PROGRESS),
|
|
261
|
-
z.tuple([z.literal(ExportEvents.WP_CONTENT_EXPORT_COMPLETE),
|
|
262
|
-
z.tuple([z.literal(ExportEvents.DATABASE_EXPORT_START),
|
|
263
|
-
z.tuple([z.literal(ExportEvents.DATABASE_EXPORT_PROGRESS),
|
|
264
|
-
z.tuple([z.literal(ExportEvents.DATABASE_EXPORT_COMPLETE),
|
|
265
|
-
z.tuple([z.literal(ExportEvents.CONFIG_EXPORT_START),
|
|
266
|
-
z.tuple([z.literal(ExportEvents.CONFIG_EXPORT_COMPLETE),
|
|
282
|
+
z.tuple([z.literal(ExportEvents.BACKUP_CREATE_COMPLETE), nullOrUndefined]),
|
|
283
|
+
z.tuple([z.literal(ExportEvents.WP_CONTENT_EXPORT_START), nullOrUndefined]),
|
|
284
|
+
z.tuple([z.literal(ExportEvents.WP_CONTENT_EXPORT_PROGRESS), nullOrUndefined]),
|
|
285
|
+
z.tuple([z.literal(ExportEvents.WP_CONTENT_EXPORT_COMPLETE), nullOrUndefined]),
|
|
286
|
+
z.tuple([z.literal(ExportEvents.DATABASE_EXPORT_START), nullOrUndefined]),
|
|
287
|
+
z.tuple([z.literal(ExportEvents.DATABASE_EXPORT_PROGRESS), nullOrUndefined]),
|
|
288
|
+
z.tuple([z.literal(ExportEvents.DATABASE_EXPORT_COMPLETE), nullOrUndefined]),
|
|
289
|
+
z.tuple([z.literal(ExportEvents.CONFIG_EXPORT_START), nullOrUndefined]),
|
|
290
|
+
z.tuple([z.literal(ExportEvents.CONFIG_EXPORT_COMPLETE), nullOrUndefined])
|
|
267
291
|
]);
|
|
292
|
+
z.object({ event: exportEventTupleSchema });
|
|
293
|
+
const SYNC_POLL_INTERVAL_MS = 3e3;
|
|
294
|
+
const SYNC_MAX_STALLED_ATTEMPTS = 200;
|
|
295
|
+
const SYNC_PUSH_SIZE_LIMIT_GB = 5;
|
|
296
|
+
const SYNC_PUSH_SIZE_LIMIT_BYTES = SYNC_PUSH_SIZE_LIMIT_GB * 1024 * 1024 * 1024;
|
|
297
|
+
const SYNC_IGNORE_DEFAULTS = [
|
|
298
|
+
...DEPLOY_IGNORE_DEFAULTS,
|
|
299
|
+
// Studio-internal sync exclusions
|
|
300
|
+
"database",
|
|
301
|
+
"db.php",
|
|
302
|
+
"debug.log",
|
|
303
|
+
"sqlite-database-integration",
|
|
304
|
+
"cache"
|
|
305
|
+
];
|
|
268
306
|
var AuthCommandLoggerAction = /* @__PURE__ */ ((AuthCommandLoggerAction2) => {
|
|
269
307
|
AuthCommandLoggerAction2["LOGIN"] = "login";
|
|
270
308
|
AuthCommandLoggerAction2["LOGOUT"] = "logout";
|
|
@@ -400,6 +438,20 @@ async function updateSiteAutoStart(siteId, autoStart) {
|
|
|
400
438
|
await unlockCliConfig();
|
|
401
439
|
}
|
|
402
440
|
}
|
|
441
|
+
async function updateSitePhpVersion(siteId, phpVersion) {
|
|
442
|
+
try {
|
|
443
|
+
await lockCliConfig();
|
|
444
|
+
const config = await readCliConfig();
|
|
445
|
+
const site = config.sites.find((s) => s.id === siteId);
|
|
446
|
+
if (!site) {
|
|
447
|
+
throw new LoggerError(__("Site not found"));
|
|
448
|
+
}
|
|
449
|
+
site.phpVersion = phpVersion;
|
|
450
|
+
await saveCliConfig(config);
|
|
451
|
+
} finally {
|
|
452
|
+
await unlockCliConfig();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
403
455
|
async function removeSiteFromConfig(siteId) {
|
|
404
456
|
try {
|
|
405
457
|
await lockCliConfig();
|
|
@@ -649,767 +701,6 @@ async function emitCliEvent(payload) {
|
|
|
649
701
|
} catch {
|
|
650
702
|
}
|
|
651
703
|
}
|
|
652
|
-
function parseJsonFromPhpOutput(output) {
|
|
653
|
-
try {
|
|
654
|
-
return JSON.parse(output);
|
|
655
|
-
} catch {
|
|
656
|
-
}
|
|
657
|
-
const objectStart = output.indexOf("{");
|
|
658
|
-
const arrayStart = output.indexOf("[");
|
|
659
|
-
let startIndex;
|
|
660
|
-
if (objectStart === -1 && arrayStart === -1) {
|
|
661
|
-
throw new SyntaxError(`No JSON found in output: ${output.substring(0, 100)}`);
|
|
662
|
-
} else if (objectStart === -1) {
|
|
663
|
-
startIndex = arrayStart;
|
|
664
|
-
} else if (arrayStart === -1) {
|
|
665
|
-
startIndex = objectStart;
|
|
666
|
-
} else {
|
|
667
|
-
startIndex = Math.min(objectStart, arrayStart);
|
|
668
|
-
}
|
|
669
|
-
return JSON.parse(output.substring(startIndex));
|
|
670
|
-
}
|
|
671
|
-
const DB_DEFAULTS = [
|
|
672
|
-
["DB_NAME", "database_name_here"],
|
|
673
|
-
["DB_USER", "username_here"],
|
|
674
|
-
["DB_PASSWORD", "password_here"],
|
|
675
|
-
["DB_HOST", "localhost"],
|
|
676
|
-
["DB_CHARSET", "utf8mb4"],
|
|
677
|
-
["DB_COLLATE", ""]
|
|
678
|
-
];
|
|
679
|
-
function buildDefaultDbBlockRegex() {
|
|
680
|
-
const commentLine = "[ \\t]*(?:\\/\\/[^\\r\\n]*|\\/\\*\\*[^*]*(?:\\*(?!\\/)[^*]*)*\\*\\/)[ \\t]*\\r?\\n";
|
|
681
|
-
const optionalComments = `(?:${commentLine})*`;
|
|
682
|
-
const blankLines = "(?:[ \\t]*\\r?\\n)*";
|
|
683
|
-
const definePatterns = DB_DEFAULTS.map(([name, value]) => {
|
|
684
|
-
const escapedValue = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
685
|
-
return `${optionalComments}[ \\t]*define\\([ \\t]*'${name}'[ \\t]*,[ \\t]*'${escapedValue}'[ \\t]*\\)[ \\t]*;[ \\t]*\\r?\\n`;
|
|
686
|
-
});
|
|
687
|
-
return new RegExp(definePatterns.join(blankLines));
|
|
688
|
-
}
|
|
689
|
-
const DB_SETTINGS_BLOCK_REGEX = buildDefaultDbBlockRegex();
|
|
690
|
-
const REPLACEMENT_COMMENT = normalizeLineEndings(`/**
|
|
691
|
-
* Database connection information is automatically provided.
|
|
692
|
-
* There is no need to set or change the following database configuration
|
|
693
|
-
* values:
|
|
694
|
-
* DB_HOST
|
|
695
|
-
* DB_NAME
|
|
696
|
-
* DB_USER
|
|
697
|
-
* DB_PASSWORD
|
|
698
|
-
* DB_CHARSET
|
|
699
|
-
* DB_COLLATE
|
|
700
|
-
*/`);
|
|
701
|
-
function normalizeLineEndings(content) {
|
|
702
|
-
return content.replace(/\n/g, "\r\n");
|
|
703
|
-
}
|
|
704
|
-
function hasDefaultDbBlock(content) {
|
|
705
|
-
return DB_SETTINGS_BLOCK_REGEX.test(content);
|
|
706
|
-
}
|
|
707
|
-
function removeDbConstants(content) {
|
|
708
|
-
return content.replace(DB_SETTINGS_BLOCK_REGEX, REPLACEMENT_COMMENT + "\n");
|
|
709
|
-
}
|
|
710
|
-
const openZip$1 = promisify(yauzl.open);
|
|
711
|
-
async function extractZip(zipPath, destinationFolder) {
|
|
712
|
-
const zipFile = await openZip$1(zipPath, { lazyEntries: true });
|
|
713
|
-
const openReadStream = promisify(zipFile.openReadStream.bind(zipFile));
|
|
714
|
-
const resolvedDestination = path__default.resolve(destinationFolder);
|
|
715
|
-
return new Promise((resolve, reject) => {
|
|
716
|
-
zipFile.on("entry", async (entry) => {
|
|
717
|
-
if (entry.fileName.endsWith("/")) {
|
|
718
|
-
zipFile.readEntry();
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
const normalizedPath = path__default.normalize(entry.fileName);
|
|
722
|
-
const fullPath = path__default.join(resolvedDestination, normalizedPath);
|
|
723
|
-
if (!fullPath.startsWith(resolvedDestination + path__default.sep)) {
|
|
724
|
-
console.warn(`Skipping invalid path: ${entry.fileName}`);
|
|
725
|
-
zipFile.readEntry();
|
|
726
|
-
return;
|
|
727
|
-
}
|
|
728
|
-
try {
|
|
729
|
-
let onError = function(error2) {
|
|
730
|
-
if (!readStream.destroyed) {
|
|
731
|
-
readStream.destroy();
|
|
732
|
-
}
|
|
733
|
-
if (!writeStream.destroyed) {
|
|
734
|
-
writeStream.destroy();
|
|
735
|
-
}
|
|
736
|
-
reject(error2);
|
|
737
|
-
};
|
|
738
|
-
await fs__default.promises.mkdir(path__default.dirname(fullPath), { recursive: true });
|
|
739
|
-
const readStream = await openReadStream(entry);
|
|
740
|
-
const writeStream = fs__default.createWriteStream(fullPath);
|
|
741
|
-
readStream.once("error", onError);
|
|
742
|
-
writeStream.once("error", onError);
|
|
743
|
-
writeStream.once("finish", () => {
|
|
744
|
-
zipFile.readEntry();
|
|
745
|
-
});
|
|
746
|
-
readStream.pipe(writeStream);
|
|
747
|
-
} catch (error2) {
|
|
748
|
-
reject(error2);
|
|
749
|
-
}
|
|
750
|
-
});
|
|
751
|
-
zipFile.on("end", () => {
|
|
752
|
-
resolve();
|
|
753
|
-
});
|
|
754
|
-
zipFile.on("error", reject);
|
|
755
|
-
zipFile.readEntry();
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
|
-
var ignore$1;
|
|
759
|
-
var hasRequiredIgnore;
|
|
760
|
-
function requireIgnore() {
|
|
761
|
-
if (hasRequiredIgnore) return ignore$1;
|
|
762
|
-
hasRequiredIgnore = 1;
|
|
763
|
-
function makeArray(subject) {
|
|
764
|
-
return Array.isArray(subject) ? subject : [subject];
|
|
765
|
-
}
|
|
766
|
-
const EMPTY = "";
|
|
767
|
-
const SPACE = " ";
|
|
768
|
-
const ESCAPE = "\\";
|
|
769
|
-
const REGEX_TEST_BLANK_LINE = /^\s+$/;
|
|
770
|
-
const REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
|
|
771
|
-
const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
|
|
772
|
-
const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
|
|
773
|
-
const REGEX_SPLITALL_CRLF = /\r?\n/g;
|
|
774
|
-
const REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/;
|
|
775
|
-
const SLASH = "/";
|
|
776
|
-
let TMP_KEY_IGNORE = "node-ignore";
|
|
777
|
-
if (typeof Symbol !== "undefined") {
|
|
778
|
-
TMP_KEY_IGNORE = /* @__PURE__ */ Symbol.for("node-ignore");
|
|
779
|
-
}
|
|
780
|
-
const KEY_IGNORE = TMP_KEY_IGNORE;
|
|
781
|
-
const define = (object, key, value) => Object.defineProperty(object, key, { value });
|
|
782
|
-
const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
|
|
783
|
-
const RETURN_FALSE = () => false;
|
|
784
|
-
const sanitizeRange = (range2) => range2.replace(
|
|
785
|
-
REGEX_REGEXP_RANGE,
|
|
786
|
-
(match2, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) ? match2 : EMPTY
|
|
787
|
-
);
|
|
788
|
-
const cleanRangeBackSlash = (slashes) => {
|
|
789
|
-
const { length } = slashes;
|
|
790
|
-
return slashes.slice(0, length - length % 2);
|
|
791
|
-
};
|
|
792
|
-
const REPLACERS = [
|
|
793
|
-
[
|
|
794
|
-
// remove BOM
|
|
795
|
-
// TODO:
|
|
796
|
-
// Other similar zero-width characters?
|
|
797
|
-
/^\uFEFF/,
|
|
798
|
-
() => EMPTY
|
|
799
|
-
],
|
|
800
|
-
// > Trailing spaces are ignored unless they are quoted with backslash ("\")
|
|
801
|
-
[
|
|
802
|
-
// (a\ ) -> (a )
|
|
803
|
-
// (a ) -> (a)
|
|
804
|
-
// (a ) -> (a)
|
|
805
|
-
// (a \ ) -> (a )
|
|
806
|
-
/((?:\\\\)*?)(\\?\s+)$/,
|
|
807
|
-
(_, m1, m2) => m1 + (m2.indexOf("\\") === 0 ? SPACE : EMPTY)
|
|
808
|
-
],
|
|
809
|
-
// replace (\ ) with ' '
|
|
810
|
-
// (\ ) -> ' '
|
|
811
|
-
// (\\ ) -> '\\ '
|
|
812
|
-
// (\\\ ) -> '\\ '
|
|
813
|
-
[
|
|
814
|
-
/(\\+?)\s/g,
|
|
815
|
-
(_, m1) => {
|
|
816
|
-
const { length } = m1;
|
|
817
|
-
return m1.slice(0, length - length % 2) + SPACE;
|
|
818
|
-
}
|
|
819
|
-
],
|
|
820
|
-
// Escape metacharacters
|
|
821
|
-
// which is written down by users but means special for regular expressions.
|
|
822
|
-
// > There are 12 characters with special meanings:
|
|
823
|
-
// > - the backslash \,
|
|
824
|
-
// > - the caret ^,
|
|
825
|
-
// > - the dollar sign $,
|
|
826
|
-
// > - the period or dot .,
|
|
827
|
-
// > - the vertical bar or pipe symbol |,
|
|
828
|
-
// > - the question mark ?,
|
|
829
|
-
// > - the asterisk or star *,
|
|
830
|
-
// > - the plus sign +,
|
|
831
|
-
// > - the opening parenthesis (,
|
|
832
|
-
// > - the closing parenthesis ),
|
|
833
|
-
// > - and the opening square bracket [,
|
|
834
|
-
// > - the opening curly brace {,
|
|
835
|
-
// > These special characters are often called "metacharacters".
|
|
836
|
-
[
|
|
837
|
-
/[\\$.|*+(){^]/g,
|
|
838
|
-
(match2) => `\\${match2}`
|
|
839
|
-
],
|
|
840
|
-
[
|
|
841
|
-
// > a question mark (?) matches a single character
|
|
842
|
-
/(?!\\)\?/g,
|
|
843
|
-
() => "[^/]"
|
|
844
|
-
],
|
|
845
|
-
// leading slash
|
|
846
|
-
[
|
|
847
|
-
// > A leading slash matches the beginning of the pathname.
|
|
848
|
-
// > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
|
|
849
|
-
// A leading slash matches the beginning of the pathname
|
|
850
|
-
/^\//,
|
|
851
|
-
() => "^"
|
|
852
|
-
],
|
|
853
|
-
// replace special metacharacter slash after the leading slash
|
|
854
|
-
[
|
|
855
|
-
/\//g,
|
|
856
|
-
() => "\\/"
|
|
857
|
-
],
|
|
858
|
-
[
|
|
859
|
-
// > A leading "**" followed by a slash means match in all directories.
|
|
860
|
-
// > For example, "**/foo" matches file or directory "foo" anywhere,
|
|
861
|
-
// > the same as pattern "foo".
|
|
862
|
-
// > "**/foo/bar" matches file or directory "bar" anywhere that is directly
|
|
863
|
-
// > under directory "foo".
|
|
864
|
-
// Notice that the '*'s have been replaced as '\\*'
|
|
865
|
-
/^\^*\\\*\\\*\\\//,
|
|
866
|
-
// '**/foo' <-> 'foo'
|
|
867
|
-
() => "^(?:.*\\/)?"
|
|
868
|
-
],
|
|
869
|
-
// starting
|
|
870
|
-
[
|
|
871
|
-
// there will be no leading '/'
|
|
872
|
-
// (which has been replaced by section "leading slash")
|
|
873
|
-
// If starts with '**', adding a '^' to the regular expression also works
|
|
874
|
-
/^(?=[^^])/,
|
|
875
|
-
function startingReplacer() {
|
|
876
|
-
return !/\/(?!$)/.test(this) ? "(?:^|\\/)" : "^";
|
|
877
|
-
}
|
|
878
|
-
],
|
|
879
|
-
// two globstars
|
|
880
|
-
[
|
|
881
|
-
// Use lookahead assertions so that we could match more than one `'/**'`
|
|
882
|
-
/\\\/\\\*\\\*(?=\\\/|$)/g,
|
|
883
|
-
// Zero, one or several directories
|
|
884
|
-
// should not use '*', or it will be replaced by the next replacer
|
|
885
|
-
// Check if it is not the last `'/**'`
|
|
886
|
-
(_, index, str) => index + 6 < str.length ? "(?:\\/[^\\/]+)*" : "\\/.+"
|
|
887
|
-
],
|
|
888
|
-
// normal intermediate wildcards
|
|
889
|
-
[
|
|
890
|
-
// Never replace escaped '*'
|
|
891
|
-
// ignore rule '\*' will match the path '*'
|
|
892
|
-
// 'abc.*/' -> go
|
|
893
|
-
// 'abc.*' -> skip this rule,
|
|
894
|
-
// coz trailing single wildcard will be handed by [trailing wildcard]
|
|
895
|
-
/(^|[^\\]+)(\\\*)+(?=.+)/g,
|
|
896
|
-
// '*.js' matches '.js'
|
|
897
|
-
// '*.js' doesn't match 'abc'
|
|
898
|
-
(_, p1, p2) => {
|
|
899
|
-
const unescaped = p2.replace(/\\\*/g, "[^\\/]*");
|
|
900
|
-
return p1 + unescaped;
|
|
901
|
-
}
|
|
902
|
-
],
|
|
903
|
-
[
|
|
904
|
-
// unescape, revert step 3 except for back slash
|
|
905
|
-
// For example, if a user escape a '\\*',
|
|
906
|
-
// after step 3, the result will be '\\\\\\*'
|
|
907
|
-
/\\\\\\(?=[$.|*+(){^])/g,
|
|
908
|
-
() => ESCAPE
|
|
909
|
-
],
|
|
910
|
-
[
|
|
911
|
-
// '\\\\' -> '\\'
|
|
912
|
-
/\\\\/g,
|
|
913
|
-
() => ESCAPE
|
|
914
|
-
],
|
|
915
|
-
[
|
|
916
|
-
// > The range notation, e.g. [a-zA-Z],
|
|
917
|
-
// > can be used to match one of the characters in a range.
|
|
918
|
-
// `\` is escaped by step 3
|
|
919
|
-
/(\\)?\[([^\]/]*?)(\\*)($|\])/g,
|
|
920
|
-
(match2, leadEscape, range2, endEscape, close) => leadEscape === ESCAPE ? `\\[${range2}${cleanRangeBackSlash(endEscape)}${close}` : close === "]" ? endEscape.length % 2 === 0 ? `[${sanitizeRange(range2)}${endEscape}]` : "[]" : "[]"
|
|
921
|
-
],
|
|
922
|
-
// ending
|
|
923
|
-
[
|
|
924
|
-
// 'js' will not match 'js.'
|
|
925
|
-
// 'ab' will not match 'abc'
|
|
926
|
-
/(?:[^*])$/,
|
|
927
|
-
// WTF!
|
|
928
|
-
// https://git-scm.com/docs/gitignore
|
|
929
|
-
// changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
|
|
930
|
-
// which re-fixes #24, #38
|
|
931
|
-
// > If there is a separator at the end of the pattern then the pattern
|
|
932
|
-
// > will only match directories, otherwise the pattern can match both
|
|
933
|
-
// > files and directories.
|
|
934
|
-
// 'js*' will not match 'a.js'
|
|
935
|
-
// 'js/' will not match 'a.js'
|
|
936
|
-
// 'js' will match 'a.js' and 'a.js/'
|
|
937
|
-
(match2) => /\/$/.test(match2) ? `${match2}$` : `${match2}(?=$|\\/$)`
|
|
938
|
-
],
|
|
939
|
-
// trailing wildcard
|
|
940
|
-
[
|
|
941
|
-
/(\^|\\\/)?\\\*$/,
|
|
942
|
-
(_, p1) => {
|
|
943
|
-
const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
|
|
944
|
-
return `${prefix}(?=$|\\/$)`;
|
|
945
|
-
}
|
|
946
|
-
]
|
|
947
|
-
];
|
|
948
|
-
const regexCache = /* @__PURE__ */ Object.create(null);
|
|
949
|
-
const makeRegex = (pattern, ignoreCase) => {
|
|
950
|
-
let source2 = regexCache[pattern];
|
|
951
|
-
if (!source2) {
|
|
952
|
-
source2 = REPLACERS.reduce(
|
|
953
|
-
(prev, [matcher, replacer]) => prev.replace(matcher, replacer.bind(pattern)),
|
|
954
|
-
pattern
|
|
955
|
-
);
|
|
956
|
-
regexCache[pattern] = source2;
|
|
957
|
-
}
|
|
958
|
-
return ignoreCase ? new RegExp(source2, "i") : new RegExp(source2);
|
|
959
|
-
};
|
|
960
|
-
const isString = (subject) => typeof subject === "string";
|
|
961
|
-
const checkPattern = (pattern) => pattern && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
|
|
962
|
-
const splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF);
|
|
963
|
-
class IgnoreRule {
|
|
964
|
-
constructor(origin, pattern, negative, regex) {
|
|
965
|
-
this.origin = origin;
|
|
966
|
-
this.pattern = pattern;
|
|
967
|
-
this.negative = negative;
|
|
968
|
-
this.regex = regex;
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
const createRule = (pattern, ignoreCase) => {
|
|
972
|
-
const origin = pattern;
|
|
973
|
-
let negative = false;
|
|
974
|
-
if (pattern.indexOf("!") === 0) {
|
|
975
|
-
negative = true;
|
|
976
|
-
pattern = pattern.substr(1);
|
|
977
|
-
}
|
|
978
|
-
pattern = pattern.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#");
|
|
979
|
-
const regex = makeRegex(pattern, ignoreCase);
|
|
980
|
-
return new IgnoreRule(
|
|
981
|
-
origin,
|
|
982
|
-
pattern,
|
|
983
|
-
negative,
|
|
984
|
-
regex
|
|
985
|
-
);
|
|
986
|
-
};
|
|
987
|
-
const throwError = (message2, Ctor) => {
|
|
988
|
-
throw new Ctor(message2);
|
|
989
|
-
};
|
|
990
|
-
const checkPath = (path2, originalPath, doThrow) => {
|
|
991
|
-
if (!isString(path2)) {
|
|
992
|
-
return doThrow(
|
|
993
|
-
`path must be a string, but got \`${originalPath}\``,
|
|
994
|
-
TypeError
|
|
995
|
-
);
|
|
996
|
-
}
|
|
997
|
-
if (!path2) {
|
|
998
|
-
return doThrow(`path must not be empty`, TypeError);
|
|
999
|
-
}
|
|
1000
|
-
if (checkPath.isNotRelative(path2)) {
|
|
1001
|
-
const r = "`path.relative()`d";
|
|
1002
|
-
return doThrow(
|
|
1003
|
-
`path should be a ${r} string, but got "${originalPath}"`,
|
|
1004
|
-
RangeError
|
|
1005
|
-
);
|
|
1006
|
-
}
|
|
1007
|
-
return true;
|
|
1008
|
-
};
|
|
1009
|
-
const isNotRelative = (path2) => REGEX_TEST_INVALID_PATH.test(path2);
|
|
1010
|
-
checkPath.isNotRelative = isNotRelative;
|
|
1011
|
-
checkPath.convert = (p) => p;
|
|
1012
|
-
class Ignore {
|
|
1013
|
-
constructor({
|
|
1014
|
-
ignorecase = true,
|
|
1015
|
-
ignoreCase = ignorecase,
|
|
1016
|
-
allowRelativePaths = false
|
|
1017
|
-
} = {}) {
|
|
1018
|
-
define(this, KEY_IGNORE, true);
|
|
1019
|
-
this._rules = [];
|
|
1020
|
-
this._ignoreCase = ignoreCase;
|
|
1021
|
-
this._allowRelativePaths = allowRelativePaths;
|
|
1022
|
-
this._initCache();
|
|
1023
|
-
}
|
|
1024
|
-
_initCache() {
|
|
1025
|
-
this._ignoreCache = /* @__PURE__ */ Object.create(null);
|
|
1026
|
-
this._testCache = /* @__PURE__ */ Object.create(null);
|
|
1027
|
-
}
|
|
1028
|
-
_addPattern(pattern) {
|
|
1029
|
-
if (pattern && pattern[KEY_IGNORE]) {
|
|
1030
|
-
this._rules = this._rules.concat(pattern._rules);
|
|
1031
|
-
this._added = true;
|
|
1032
|
-
return;
|
|
1033
|
-
}
|
|
1034
|
-
if (checkPattern(pattern)) {
|
|
1035
|
-
const rule = createRule(pattern, this._ignoreCase);
|
|
1036
|
-
this._added = true;
|
|
1037
|
-
this._rules.push(rule);
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
// @param {Array<string> | string | Ignore} pattern
|
|
1041
|
-
add(pattern) {
|
|
1042
|
-
this._added = false;
|
|
1043
|
-
makeArray(
|
|
1044
|
-
isString(pattern) ? splitPattern(pattern) : pattern
|
|
1045
|
-
).forEach(this._addPattern, this);
|
|
1046
|
-
if (this._added) {
|
|
1047
|
-
this._initCache();
|
|
1048
|
-
}
|
|
1049
|
-
return this;
|
|
1050
|
-
}
|
|
1051
|
-
// legacy
|
|
1052
|
-
addPattern(pattern) {
|
|
1053
|
-
return this.add(pattern);
|
|
1054
|
-
}
|
|
1055
|
-
// | ignored : unignored
|
|
1056
|
-
// negative | 0:0 | 0:1 | 1:0 | 1:1
|
|
1057
|
-
// -------- | ------- | ------- | ------- | --------
|
|
1058
|
-
// 0 | TEST | TEST | SKIP | X
|
|
1059
|
-
// 1 | TESTIF | SKIP | TEST | X
|
|
1060
|
-
// - SKIP: always skip
|
|
1061
|
-
// - TEST: always test
|
|
1062
|
-
// - TESTIF: only test if checkUnignored
|
|
1063
|
-
// - X: that never happen
|
|
1064
|
-
// @param {boolean} whether should check if the path is unignored,
|
|
1065
|
-
// setting `checkUnignored` to `false` could reduce additional
|
|
1066
|
-
// path matching.
|
|
1067
|
-
// @returns {TestResult} true if a file is ignored
|
|
1068
|
-
_testOne(path2, checkUnignored) {
|
|
1069
|
-
let ignored = false;
|
|
1070
|
-
let unignored = false;
|
|
1071
|
-
this._rules.forEach((rule) => {
|
|
1072
|
-
const { negative } = rule;
|
|
1073
|
-
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
1074
|
-
return;
|
|
1075
|
-
}
|
|
1076
|
-
const matched = rule.regex.test(path2);
|
|
1077
|
-
if (matched) {
|
|
1078
|
-
ignored = !negative;
|
|
1079
|
-
unignored = negative;
|
|
1080
|
-
}
|
|
1081
|
-
});
|
|
1082
|
-
return {
|
|
1083
|
-
ignored,
|
|
1084
|
-
unignored
|
|
1085
|
-
};
|
|
1086
|
-
}
|
|
1087
|
-
// @returns {TestResult}
|
|
1088
|
-
_test(originalPath, cache, checkUnignored, slices) {
|
|
1089
|
-
const path2 = originalPath && checkPath.convert(originalPath);
|
|
1090
|
-
checkPath(
|
|
1091
|
-
path2,
|
|
1092
|
-
originalPath,
|
|
1093
|
-
this._allowRelativePaths ? RETURN_FALSE : throwError
|
|
1094
|
-
);
|
|
1095
|
-
return this._t(path2, cache, checkUnignored, slices);
|
|
1096
|
-
}
|
|
1097
|
-
_t(path2, cache, checkUnignored, slices) {
|
|
1098
|
-
if (path2 in cache) {
|
|
1099
|
-
return cache[path2];
|
|
1100
|
-
}
|
|
1101
|
-
if (!slices) {
|
|
1102
|
-
slices = path2.split(SLASH);
|
|
1103
|
-
}
|
|
1104
|
-
slices.pop();
|
|
1105
|
-
if (!slices.length) {
|
|
1106
|
-
return cache[path2] = this._testOne(path2, checkUnignored);
|
|
1107
|
-
}
|
|
1108
|
-
const parent = this._t(
|
|
1109
|
-
slices.join(SLASH) + SLASH,
|
|
1110
|
-
cache,
|
|
1111
|
-
checkUnignored,
|
|
1112
|
-
slices
|
|
1113
|
-
);
|
|
1114
|
-
return cache[path2] = parent.ignored ? parent : this._testOne(path2, checkUnignored);
|
|
1115
|
-
}
|
|
1116
|
-
ignores(path2) {
|
|
1117
|
-
return this._test(path2, this._ignoreCache, false).ignored;
|
|
1118
|
-
}
|
|
1119
|
-
createFilter() {
|
|
1120
|
-
return (path2) => !this.ignores(path2);
|
|
1121
|
-
}
|
|
1122
|
-
filter(paths) {
|
|
1123
|
-
return makeArray(paths).filter(this.createFilter());
|
|
1124
|
-
}
|
|
1125
|
-
// @returns {TestResult}
|
|
1126
|
-
test(path2) {
|
|
1127
|
-
return this._test(path2, this._testCache, true);
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
const factory = (options) => new Ignore(options);
|
|
1131
|
-
const isPathValid = (path2) => checkPath(path2 && checkPath.convert(path2), path2, RETURN_FALSE);
|
|
1132
|
-
factory.isPathValid = isPathValid;
|
|
1133
|
-
factory.default = factory;
|
|
1134
|
-
ignore$1 = factory;
|
|
1135
|
-
if (
|
|
1136
|
-
// Detect `process` so that it can run in browsers.
|
|
1137
|
-
typeof process !== "undefined" && (process.env && process.env.IGNORE_TEST_WIN32 || process.platform === "win32")
|
|
1138
|
-
) {
|
|
1139
|
-
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
1140
|
-
checkPath.convert = makePosix;
|
|
1141
|
-
const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
1142
|
-
checkPath.isNotRelative = (path2) => REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path2) || isNotRelative(path2);
|
|
1143
|
-
}
|
|
1144
|
-
return ignore$1;
|
|
1145
|
-
}
|
|
1146
|
-
var ignoreExports = requireIgnore();
|
|
1147
|
-
const ignore = /* @__PURE__ */ getDefaultExportFromCjs(ignoreExports);
|
|
1148
|
-
async function downloadFile(url, destinationPath) {
|
|
1149
|
-
try {
|
|
1150
|
-
await fs__default.promises.mkdir(path__default.dirname(destinationPath), { recursive: true });
|
|
1151
|
-
} catch (error2) {
|
|
1152
|
-
if (isErrnoException(error2) && error2.code !== "EEXIST") {
|
|
1153
|
-
throw error2;
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
const response2 = await fetch(url);
|
|
1157
|
-
if (!response2.ok) {
|
|
1158
|
-
throw new Error(`Request failed with status code: ${response2.status}`);
|
|
1159
|
-
}
|
|
1160
|
-
if (!response2.body) {
|
|
1161
|
-
throw new Error("Download response did not include a readable body.");
|
|
1162
|
-
}
|
|
1163
|
-
await response2.body.pipeTo(Writable.toWeb(fs__default.createWriteStream(destinationPath)));
|
|
1164
|
-
}
|
|
1165
|
-
const IGNORE_PATTERNS = [".DS_Store", "Thumbs.db"];
|
|
1166
|
-
const IGNORE_INSTANCE = ignore().add(IGNORE_PATTERNS);
|
|
1167
|
-
async function collectDirectoryMetadata(directoryPath, basePath = directoryPath) {
|
|
1168
|
-
const files = /* @__PURE__ */ new Map();
|
|
1169
|
-
const entries = await fs__default.promises.readdir(directoryPath, { withFileTypes: true });
|
|
1170
|
-
for (const entry of entries) {
|
|
1171
|
-
const fullPath = path__default.join(directoryPath, entry.name);
|
|
1172
|
-
const relativePath = path__default.relative(basePath, fullPath);
|
|
1173
|
-
if (IGNORE_INSTANCE.ignores(relativePath)) {
|
|
1174
|
-
continue;
|
|
1175
|
-
}
|
|
1176
|
-
if (entry.isDirectory()) {
|
|
1177
|
-
const nestedFiles = await collectDirectoryMetadata(fullPath, basePath);
|
|
1178
|
-
for (const [key, value] of nestedFiles) {
|
|
1179
|
-
files.set(key, value);
|
|
1180
|
-
}
|
|
1181
|
-
continue;
|
|
1182
|
-
}
|
|
1183
|
-
if (!entry.isFile()) {
|
|
1184
|
-
continue;
|
|
1185
|
-
}
|
|
1186
|
-
const stats = await fs__default.promises.lstat(fullPath);
|
|
1187
|
-
files.set(relativePath, { size: stats.size, mtimeMs: Math.floor(stats.mtimeMs) });
|
|
1188
|
-
}
|
|
1189
|
-
return files;
|
|
1190
|
-
}
|
|
1191
|
-
async function areDirectoriesDifferentBySizeAndMtime(sourceDirectoryPath, targetDirectoryPath) {
|
|
1192
|
-
if (!fs__default.existsSync(sourceDirectoryPath) || !fs__default.existsSync(targetDirectoryPath)) {
|
|
1193
|
-
return true;
|
|
1194
|
-
}
|
|
1195
|
-
const [sourceFiles, targetFiles] = await Promise.all([
|
|
1196
|
-
collectDirectoryMetadata(sourceDirectoryPath),
|
|
1197
|
-
collectDirectoryMetadata(targetDirectoryPath)
|
|
1198
|
-
]);
|
|
1199
|
-
if (sourceFiles.size !== targetFiles.size) {
|
|
1200
|
-
return true;
|
|
1201
|
-
}
|
|
1202
|
-
for (const [relativePath, sourceMetadata] of sourceFiles) {
|
|
1203
|
-
const targetMetadata = targetFiles.get(relativePath);
|
|
1204
|
-
if (!targetMetadata) {
|
|
1205
|
-
return true;
|
|
1206
|
-
}
|
|
1207
|
-
if (sourceMetadata.size !== targetMetadata.size || sourceMetadata.mtimeMs !== targetMetadata.mtimeMs) {
|
|
1208
|
-
return true;
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
return false;
|
|
1212
|
-
}
|
|
1213
|
-
const MINIMUM_SUPPORTED_WP_VERSION = 6;
|
|
1214
|
-
const DEFAULT_WORDPRESS_VERSION = "latest";
|
|
1215
|
-
async function downloadWordPress(wordPressVersion = DEFAULT_WORDPRESS_VERSION, overwrite = false) {
|
|
1216
|
-
const finalFolder = getWordPressVersionPath(wordPressVersion);
|
|
1217
|
-
if (!overwrite && fs__default.existsSync(finalFolder)) {
|
|
1218
|
-
return;
|
|
1219
|
-
}
|
|
1220
|
-
const tempDir = path__default.join(os.tmpdir(), "wordpress-download-", crypto$1.randomUUID());
|
|
1221
|
-
await fs__default.promises.mkdir(tempDir, { recursive: true });
|
|
1222
|
-
try {
|
|
1223
|
-
const tmpDownloadPath = path__default.join(os.tmpdir(), `wordpress-${crypto$1.randomUUID()}.zip`);
|
|
1224
|
-
try {
|
|
1225
|
-
await downloadFile(getWordPressVersionUrl(wordPressVersion), tmpDownloadPath);
|
|
1226
|
-
await extractZip(tmpDownloadPath, tempDir);
|
|
1227
|
-
} finally {
|
|
1228
|
-
await fs__default.promises.rm(tmpDownloadPath, { force: true });
|
|
1229
|
-
}
|
|
1230
|
-
const wpSourcePath = path__default.join(tempDir, "wordpress");
|
|
1231
|
-
await fs__default.promises.mkdir(path__default.dirname(finalFolder), { recursive: true });
|
|
1232
|
-
await fs__default.promises.cp(wpSourcePath, finalFolder, {
|
|
1233
|
-
recursive: true,
|
|
1234
|
-
verbatimSymlinks: true
|
|
1235
|
-
});
|
|
1236
|
-
} finally {
|
|
1237
|
-
try {
|
|
1238
|
-
await fs__default.promises.rm(tempDir, { force: true, recursive: true });
|
|
1239
|
-
} catch {
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
async function fetchWordPressVersions$1() {
|
|
1244
|
-
try {
|
|
1245
|
-
const response2 = await fetch("https://api.wordpress.org/core/stable-check/1.0/");
|
|
1246
|
-
const versionsStatus = await response2.json();
|
|
1247
|
-
const versions = Object.keys(versionsStatus).filter((item) => {
|
|
1248
|
-
const version2 = semver.coerce(item);
|
|
1249
|
-
const minVersion = semver.coerce(MINIMUM_SUPPORTED_WP_VERSION);
|
|
1250
|
-
return version2 && minVersion && semver.gte(version2, minVersion);
|
|
1251
|
-
}).sort((a, b) => {
|
|
1252
|
-
const versionA = semver.coerce(a);
|
|
1253
|
-
const versionB = semver.coerce(b);
|
|
1254
|
-
if (!versionA || !versionB) {
|
|
1255
|
-
return 0;
|
|
1256
|
-
}
|
|
1257
|
-
return semver.compare(versionA, versionB);
|
|
1258
|
-
}).reverse();
|
|
1259
|
-
const latestVersion = Object.keys(versionsStatus).find(
|
|
1260
|
-
(index) => versionsStatus[index] === "latest"
|
|
1261
|
-
);
|
|
1262
|
-
return { versions, latest: latestVersion };
|
|
1263
|
-
} catch (exception) {
|
|
1264
|
-
return { versions: [], latest: DEFAULT_WORDPRESS_VERSION };
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
async function getWordPressVersionFromInstallation(installationPath) {
|
|
1268
|
-
try {
|
|
1269
|
-
const versionFileContent = await fs__default.promises.readFile(
|
|
1270
|
-
path__default.join(installationPath, "wp-includes", "version.php"),
|
|
1271
|
-
"utf8"
|
|
1272
|
-
);
|
|
1273
|
-
const matches = versionFileContent.match(/\$wp_version\s*=\s*'([0-9a-zA-Z.-]+)'/);
|
|
1274
|
-
return matches?.[1];
|
|
1275
|
-
} catch (err) {
|
|
1276
|
-
return null;
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
async function updateLatestWordPressVersion() {
|
|
1280
|
-
let shouldOverwrite = false;
|
|
1281
|
-
const latestVersionPath = getWordPressVersionPath("latest");
|
|
1282
|
-
const latestVersionFiles = fs__default.existsSync(latestVersionPath) ? await fs__default.promises.readdir(latestVersionPath) : [];
|
|
1283
|
-
if (latestVersionFiles.length !== 0) {
|
|
1284
|
-
const installedVersion = await getWordPressVersionFromInstallation(latestVersionPath);
|
|
1285
|
-
const wordPressVersions = await fetchWordPressVersions$1();
|
|
1286
|
-
const latestVersion = wordPressVersions.latest ?? DEFAULT_WORDPRESS_VERSION;
|
|
1287
|
-
if (installedVersion && latestVersion !== "latest" && installedVersion !== latestVersion) {
|
|
1288
|
-
await recursiveCopyDirectory(
|
|
1289
|
-
latestVersionPath,
|
|
1290
|
-
getWordPressVersionPath(installedVersion)
|
|
1291
|
-
);
|
|
1292
|
-
shouldOverwrite = true;
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
await downloadWordPress("latest", shouldOverwrite);
|
|
1296
|
-
}
|
|
1297
|
-
const processIdAllocator = new ProcessIdAllocator();
|
|
1298
|
-
const PLAYGROUND_INTERNAL_SHARED_FOLDER = "/internal/shared";
|
|
1299
|
-
function createNoopSpawnHandler() {
|
|
1300
|
-
return createSpawnHandler(async (args, processApi) => {
|
|
1301
|
-
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
1302
|
-
processApi.exit(1);
|
|
1303
|
-
});
|
|
1304
|
-
}
|
|
1305
|
-
async function runWpCliCommand(siteFolder, phpVersion, args) {
|
|
1306
|
-
const id = await loadNodeRuntime(phpVersion, {
|
|
1307
|
-
followSymlinks: true,
|
|
1308
|
-
withRedis: IS_JSPI_AVAILABLE,
|
|
1309
|
-
withMemcached: IS_JSPI_AVAILABLE,
|
|
1310
|
-
emscriptenOptions: {
|
|
1311
|
-
processId: processIdAllocator.claim()
|
|
1312
|
-
}
|
|
1313
|
-
});
|
|
1314
|
-
const php = new PHP(id);
|
|
1315
|
-
try {
|
|
1316
|
-
await php.setSapiName("cli");
|
|
1317
|
-
php.defineConstant("WP_SQLITE_AST_DRIVER", true);
|
|
1318
|
-
php.mkdir("/wordpress");
|
|
1319
|
-
await php.mount("/wordpress", createNodeFsMountHandler(siteFolder));
|
|
1320
|
-
php.writeFile("/tmp/ca-bundle.crt", rootCertificates.join("\n"));
|
|
1321
|
-
await setPhpIniEntries(php, {
|
|
1322
|
-
"openssl.cafile": "/tmp/ca-bundle.crt",
|
|
1323
|
-
allow_url_fopen: 1
|
|
1324
|
-
});
|
|
1325
|
-
await php.setSpawnHandler(createNoopSpawnHandler());
|
|
1326
|
-
await cleanupLegacyMuPlugins(siteFolder);
|
|
1327
|
-
const [studioMuPluginsHostPath, loaderMuPluginHostPath] = await getMuPlugins({
|
|
1328
|
-
isWpAutoUpdating: false
|
|
1329
|
-
});
|
|
1330
|
-
await php.mount(
|
|
1331
|
-
"/internal/studio/mu-plugins",
|
|
1332
|
-
createNodeFsMountHandler(studioMuPluginsHostPath)
|
|
1333
|
-
);
|
|
1334
|
-
await php.mount(
|
|
1335
|
-
PLAYGROUND_INTERNAL_SHARED_FOLDER + "/mu-plugins/99-studio-loader.php",
|
|
1336
|
-
createNodeFsMountHandler(loaderMuPluginHostPath)
|
|
1337
|
-
);
|
|
1338
|
-
await php.mount("/tmp/wp-cli.phar", createNodeFsMountHandler(getWpCliPharPath()));
|
|
1339
|
-
await php.mount("/tmp/sqlite-command", createNodeFsMountHandler(getSqliteCommandPath()));
|
|
1340
|
-
await setupPlatformLevelMuPlugins(php);
|
|
1341
|
-
const response2 = await php.cli(["php", "/tmp/wp-cli.phar", "--path=/wordpress", ...args]);
|
|
1342
|
-
return {
|
|
1343
|
-
response: response2,
|
|
1344
|
-
[Symbol.dispose]() {
|
|
1345
|
-
php.exit();
|
|
1346
|
-
}
|
|
1347
|
-
};
|
|
1348
|
-
} catch (error2) {
|
|
1349
|
-
php.exit();
|
|
1350
|
-
throw new Error(__("An error occurred while running the WP-CLI command."));
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
async function runGlobalWpCliCommand(args) {
|
|
1354
|
-
const id = await loadNodeRuntime(LatestSupportedPHPVersion, {
|
|
1355
|
-
followSymlinks: true,
|
|
1356
|
-
withRedis: false,
|
|
1357
|
-
withMemcached: false,
|
|
1358
|
-
emscriptenOptions: {
|
|
1359
|
-
processId: processIdAllocator.claim()
|
|
1360
|
-
}
|
|
1361
|
-
});
|
|
1362
|
-
const php = new PHP(id);
|
|
1363
|
-
try {
|
|
1364
|
-
await php.setSapiName("cli");
|
|
1365
|
-
php.writeFile("/tmp/ca-bundle.crt", rootCertificates.join("\n"));
|
|
1366
|
-
await setPhpIniEntries(php, {
|
|
1367
|
-
"openssl.cafile": "/tmp/ca-bundle.crt",
|
|
1368
|
-
allow_url_fopen: 1
|
|
1369
|
-
});
|
|
1370
|
-
await php.setSpawnHandler(createNoopSpawnHandler());
|
|
1371
|
-
await php.mount("/tmp/wp-cli.phar", createNodeFsMountHandler(getWpCliPharPath()));
|
|
1372
|
-
const response2 = await php.cli(["php", "/tmp/wp-cli.phar", ...args]);
|
|
1373
|
-
return {
|
|
1374
|
-
response: response2,
|
|
1375
|
-
[Symbol.dispose]() {
|
|
1376
|
-
php.exit();
|
|
1377
|
-
}
|
|
1378
|
-
};
|
|
1379
|
-
} catch (error2) {
|
|
1380
|
-
php.exit();
|
|
1381
|
-
throw new Error(__("An error occurred while running the WP-CLI command."));
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
class ImportExportEventEmitter extends EventEmitter {
|
|
1385
|
-
on(eventName, listener) {
|
|
1386
|
-
return super.on(eventName, listener);
|
|
1387
|
-
}
|
|
1388
|
-
off(eventName, listener) {
|
|
1389
|
-
return super.off(eventName, listener);
|
|
1390
|
-
}
|
|
1391
|
-
emit(eventName, ...args) {
|
|
1392
|
-
return super.emit(eventName, ...args);
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
const LATIN = "a-z";
|
|
1396
|
-
const CYRILLIC = "а-яё";
|
|
1397
|
-
const ARABIC = "\\u0600-\\u06FF";
|
|
1398
|
-
const HEBREW = "\\u0590-\\u05FF";
|
|
1399
|
-
const CHINESE = "\\u4e00-\\u9fa5";
|
|
1400
|
-
const JAPANESE_HIRAGANA = "\\u3040-\\u309F";
|
|
1401
|
-
const JAPANESE_KATAKANA = "\\u30A0-\\u30FF";
|
|
1402
|
-
const KOREAN_HANGUL = "\\uAC00-\\uD7AF";
|
|
1403
|
-
const KOREAN_JAMO = "\\u1100-\\u11FF";
|
|
1404
|
-
const NUMBERS = "0-9";
|
|
1405
|
-
const WHITELISTED_SYMBOLS = "_\\- ";
|
|
1406
|
-
const ALLOWED_CHARS = new RegExp(
|
|
1407
|
-
`[^${LATIN}${NUMBERS}${CYRILLIC}${ARABIC}${HEBREW}${CHINESE}${JAPANESE_HIRAGANA}${JAPANESE_KATAKANA}${KOREAN_HANGUL}${KOREAN_JAMO}${WHITELISTED_SYMBOLS}]`,
|
|
1408
|
-
"gi"
|
|
1409
|
-
);
|
|
1410
|
-
const sanitizeFolderName = (filename) => {
|
|
1411
|
-
return String(filename).replace(/ł/g, "l").replace(/Ł/g, "L").normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(ALLOWED_CHARS, "").trim().replace(/\s+/g, "-").replace(/-+/g, "-");
|
|
1412
|
-
};
|
|
1413
704
|
function toDate(argument) {
|
|
1414
705
|
const argStr = Object.prototype.toString.call(argument);
|
|
1415
706
|
if (argument instanceof Date || typeof argument === "object" && argStr === "[object Date]") {
|
|
@@ -3138,10 +2429,381 @@ function isSameMonth(dateLeft, dateRight) {
|
|
|
3138
2429
|
const _dateRight = toDate(dateRight);
|
|
3139
2430
|
return _dateLeft.getFullYear() === _dateRight.getFullYear() && _dateLeft.getMonth() === _dateRight.getMonth();
|
|
3140
2431
|
}
|
|
2432
|
+
const LATIN = "a-z";
|
|
2433
|
+
const CYRILLIC = "а-яё";
|
|
2434
|
+
const ARABIC = "\\u0600-\\u06FF";
|
|
2435
|
+
const HEBREW = "\\u0590-\\u05FF";
|
|
2436
|
+
const CHINESE = "\\u4e00-\\u9fa5";
|
|
2437
|
+
const JAPANESE_HIRAGANA = "\\u3040-\\u309F";
|
|
2438
|
+
const JAPANESE_KATAKANA = "\\u30A0-\\u30FF";
|
|
2439
|
+
const KOREAN_HANGUL = "\\uAC00-\\uD7AF";
|
|
2440
|
+
const KOREAN_JAMO = "\\u1100-\\u11FF";
|
|
2441
|
+
const NUMBERS = "0-9";
|
|
2442
|
+
const WHITELISTED_SYMBOLS = "_\\- ";
|
|
2443
|
+
const ALLOWED_CHARS = new RegExp(
|
|
2444
|
+
`[^${LATIN}${NUMBERS}${CYRILLIC}${ARABIC}${HEBREW}${CHINESE}${JAPANESE_HIRAGANA}${JAPANESE_KATAKANA}${KOREAN_HANGUL}${KOREAN_JAMO}${WHITELISTED_SYMBOLS}]`,
|
|
2445
|
+
"gi"
|
|
2446
|
+
);
|
|
2447
|
+
const sanitizeFolderName = (filename) => {
|
|
2448
|
+
return String(filename).replace(/ł/g, "l").replace(/Ł/g, "L").normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(ALLOWED_CHARS, "").trim().replace(/\s+/g, "-").replace(/-+/g, "-");
|
|
2449
|
+
};
|
|
3141
2450
|
function generateBackupFilename(name) {
|
|
3142
2451
|
const timestamp = format(/* @__PURE__ */ new Date(), "yyyy-MM-dd-HH-mm-ss");
|
|
3143
2452
|
return sanitizeFolderName(`studio-backup-${name}-${timestamp}`);
|
|
3144
2453
|
}
|
|
2454
|
+
function parseJsonFromPhpOutput(output) {
|
|
2455
|
+
try {
|
|
2456
|
+
return JSON.parse(output);
|
|
2457
|
+
} catch {
|
|
2458
|
+
}
|
|
2459
|
+
const objectStart = output.indexOf("{");
|
|
2460
|
+
const arrayStart = output.indexOf("[");
|
|
2461
|
+
let startIndex;
|
|
2462
|
+
if (objectStart === -1 && arrayStart === -1) {
|
|
2463
|
+
throw new SyntaxError(`No JSON found in output: ${output.substring(0, 100)}`);
|
|
2464
|
+
} else if (objectStart === -1) {
|
|
2465
|
+
startIndex = arrayStart;
|
|
2466
|
+
} else if (arrayStart === -1) {
|
|
2467
|
+
startIndex = objectStart;
|
|
2468
|
+
} else {
|
|
2469
|
+
startIndex = Math.min(objectStart, arrayStart);
|
|
2470
|
+
}
|
|
2471
|
+
return JSON.parse(output.substring(startIndex));
|
|
2472
|
+
}
|
|
2473
|
+
const DB_DEFAULTS = [
|
|
2474
|
+
["DB_NAME", "database_name_here"],
|
|
2475
|
+
["DB_USER", "username_here"],
|
|
2476
|
+
["DB_PASSWORD", "password_here"],
|
|
2477
|
+
["DB_HOST", "localhost"],
|
|
2478
|
+
["DB_CHARSET", "utf8mb4"],
|
|
2479
|
+
["DB_COLLATE", ""]
|
|
2480
|
+
];
|
|
2481
|
+
function buildDefaultDbBlockRegex() {
|
|
2482
|
+
const commentLine = "[ \\t]*(?:\\/\\/[^\\r\\n]*|\\/\\*\\*[^*]*(?:\\*(?!\\/)[^*]*)*\\*\\/)[ \\t]*\\r?\\n";
|
|
2483
|
+
const optionalComments = `(?:${commentLine})*`;
|
|
2484
|
+
const blankLines = "(?:[ \\t]*\\r?\\n)*";
|
|
2485
|
+
const definePatterns = DB_DEFAULTS.map(([name, value]) => {
|
|
2486
|
+
const escapedValue = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2487
|
+
return `${optionalComments}[ \\t]*define\\([ \\t]*'${name}'[ \\t]*,[ \\t]*'${escapedValue}'[ \\t]*\\)[ \\t]*;[ \\t]*\\r?\\n`;
|
|
2488
|
+
});
|
|
2489
|
+
return new RegExp(definePatterns.join(blankLines));
|
|
2490
|
+
}
|
|
2491
|
+
const DB_SETTINGS_BLOCK_REGEX = buildDefaultDbBlockRegex();
|
|
2492
|
+
const REPLACEMENT_COMMENT = normalizeLineEndings(`/**
|
|
2493
|
+
* Database connection information is automatically provided.
|
|
2494
|
+
* There is no need to set or change the following database configuration
|
|
2495
|
+
* values:
|
|
2496
|
+
* DB_HOST
|
|
2497
|
+
* DB_NAME
|
|
2498
|
+
* DB_USER
|
|
2499
|
+
* DB_PASSWORD
|
|
2500
|
+
* DB_CHARSET
|
|
2501
|
+
* DB_COLLATE
|
|
2502
|
+
*/`);
|
|
2503
|
+
function normalizeLineEndings(content) {
|
|
2504
|
+
return content.replace(/\n/g, "\r\n");
|
|
2505
|
+
}
|
|
2506
|
+
function hasDefaultDbBlock(content) {
|
|
2507
|
+
return DB_SETTINGS_BLOCK_REGEX.test(content);
|
|
2508
|
+
}
|
|
2509
|
+
function removeDbConstants(content) {
|
|
2510
|
+
return content.replace(DB_SETTINGS_BLOCK_REGEX, REPLACEMENT_COMMENT + "\n");
|
|
2511
|
+
}
|
|
2512
|
+
const openZip$1 = promisify(yauzl.open);
|
|
2513
|
+
async function extractZip(zipPath, destinationFolder) {
|
|
2514
|
+
const zipFile = await openZip$1(zipPath, { lazyEntries: true });
|
|
2515
|
+
const openReadStream = promisify(zipFile.openReadStream.bind(zipFile));
|
|
2516
|
+
const resolvedDestination = path__default.resolve(destinationFolder);
|
|
2517
|
+
return new Promise((resolve, reject) => {
|
|
2518
|
+
zipFile.on("entry", async (entry) => {
|
|
2519
|
+
if (entry.fileName.endsWith("/")) {
|
|
2520
|
+
zipFile.readEntry();
|
|
2521
|
+
return;
|
|
2522
|
+
}
|
|
2523
|
+
const normalizedPath = path__default.normalize(entry.fileName);
|
|
2524
|
+
const fullPath = path__default.join(resolvedDestination, normalizedPath);
|
|
2525
|
+
if (!fullPath.startsWith(resolvedDestination + path__default.sep)) {
|
|
2526
|
+
console.warn(`Skipping invalid path: ${entry.fileName}`);
|
|
2527
|
+
zipFile.readEntry();
|
|
2528
|
+
return;
|
|
2529
|
+
}
|
|
2530
|
+
try {
|
|
2531
|
+
let onError = function(error2) {
|
|
2532
|
+
if (!readStream.destroyed) {
|
|
2533
|
+
readStream.destroy();
|
|
2534
|
+
}
|
|
2535
|
+
if (!writeStream.destroyed) {
|
|
2536
|
+
writeStream.destroy();
|
|
2537
|
+
}
|
|
2538
|
+
reject(error2);
|
|
2539
|
+
};
|
|
2540
|
+
await fs__default.promises.mkdir(path__default.dirname(fullPath), { recursive: true });
|
|
2541
|
+
const readStream = await openReadStream(entry);
|
|
2542
|
+
const writeStream = fs__default.createWriteStream(fullPath);
|
|
2543
|
+
readStream.once("error", onError);
|
|
2544
|
+
writeStream.once("error", onError);
|
|
2545
|
+
writeStream.once("finish", () => {
|
|
2546
|
+
zipFile.readEntry();
|
|
2547
|
+
});
|
|
2548
|
+
readStream.pipe(writeStream);
|
|
2549
|
+
} catch (error2) {
|
|
2550
|
+
reject(error2);
|
|
2551
|
+
}
|
|
2552
|
+
});
|
|
2553
|
+
zipFile.on("end", () => {
|
|
2554
|
+
resolve();
|
|
2555
|
+
});
|
|
2556
|
+
zipFile.on("error", reject);
|
|
2557
|
+
zipFile.readEntry();
|
|
2558
|
+
});
|
|
2559
|
+
}
|
|
2560
|
+
async function downloadFile(url, destinationPath) {
|
|
2561
|
+
try {
|
|
2562
|
+
await fs__default.promises.mkdir(path__default.dirname(destinationPath), { recursive: true });
|
|
2563
|
+
} catch (error2) {
|
|
2564
|
+
if (isErrnoException(error2) && error2.code !== "EEXIST") {
|
|
2565
|
+
throw error2;
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
const response2 = await fetch(url);
|
|
2569
|
+
if (!response2.ok) {
|
|
2570
|
+
throw new Error(`Request failed with status code: ${response2.status}`);
|
|
2571
|
+
}
|
|
2572
|
+
if (!response2.body) {
|
|
2573
|
+
throw new Error("Download response did not include a readable body.");
|
|
2574
|
+
}
|
|
2575
|
+
await response2.body.pipeTo(Writable.toWeb(fs__default.createWriteStream(destinationPath)));
|
|
2576
|
+
}
|
|
2577
|
+
const IGNORE_PATTERNS = [".DS_Store", "Thumbs.db"];
|
|
2578
|
+
const IGNORE_INSTANCE = ignore().add(IGNORE_PATTERNS);
|
|
2579
|
+
async function collectDirectoryMetadata(directoryPath, basePath = directoryPath) {
|
|
2580
|
+
const files = /* @__PURE__ */ new Map();
|
|
2581
|
+
const entries = await fs__default.promises.readdir(directoryPath, { withFileTypes: true });
|
|
2582
|
+
for (const entry of entries) {
|
|
2583
|
+
const fullPath = path__default.join(directoryPath, entry.name);
|
|
2584
|
+
const relativePath = path__default.relative(basePath, fullPath);
|
|
2585
|
+
if (IGNORE_INSTANCE.ignores(relativePath)) {
|
|
2586
|
+
continue;
|
|
2587
|
+
}
|
|
2588
|
+
if (entry.isDirectory()) {
|
|
2589
|
+
const nestedFiles = await collectDirectoryMetadata(fullPath, basePath);
|
|
2590
|
+
for (const [key, value] of nestedFiles) {
|
|
2591
|
+
files.set(key, value);
|
|
2592
|
+
}
|
|
2593
|
+
continue;
|
|
2594
|
+
}
|
|
2595
|
+
if (!entry.isFile()) {
|
|
2596
|
+
continue;
|
|
2597
|
+
}
|
|
2598
|
+
const stats = await fs__default.promises.lstat(fullPath);
|
|
2599
|
+
files.set(relativePath, { size: stats.size, mtimeMs: Math.floor(stats.mtimeMs) });
|
|
2600
|
+
}
|
|
2601
|
+
return files;
|
|
2602
|
+
}
|
|
2603
|
+
async function areDirectoriesDifferentBySizeAndMtime(sourceDirectoryPath, targetDirectoryPath) {
|
|
2604
|
+
if (!fs__default.existsSync(sourceDirectoryPath) || !fs__default.existsSync(targetDirectoryPath)) {
|
|
2605
|
+
return true;
|
|
2606
|
+
}
|
|
2607
|
+
const [sourceFiles, targetFiles] = await Promise.all([
|
|
2608
|
+
collectDirectoryMetadata(sourceDirectoryPath),
|
|
2609
|
+
collectDirectoryMetadata(targetDirectoryPath)
|
|
2610
|
+
]);
|
|
2611
|
+
if (sourceFiles.size !== targetFiles.size) {
|
|
2612
|
+
return true;
|
|
2613
|
+
}
|
|
2614
|
+
for (const [relativePath, sourceMetadata] of sourceFiles) {
|
|
2615
|
+
const targetMetadata = targetFiles.get(relativePath);
|
|
2616
|
+
if (!targetMetadata) {
|
|
2617
|
+
return true;
|
|
2618
|
+
}
|
|
2619
|
+
if (sourceMetadata.size !== targetMetadata.size || sourceMetadata.mtimeMs !== targetMetadata.mtimeMs) {
|
|
2620
|
+
return true;
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
return false;
|
|
2624
|
+
}
|
|
2625
|
+
const MINIMUM_SUPPORTED_WP_VERSION = 6;
|
|
2626
|
+
const DEFAULT_WORDPRESS_VERSION = "latest";
|
|
2627
|
+
async function downloadWordPress(wordPressVersion = DEFAULT_WORDPRESS_VERSION, overwrite = false) {
|
|
2628
|
+
const finalFolder = getWordPressVersionPath(wordPressVersion);
|
|
2629
|
+
if (!overwrite && fs__default.existsSync(finalFolder)) {
|
|
2630
|
+
return;
|
|
2631
|
+
}
|
|
2632
|
+
const tempDir = path__default.join(os.tmpdir(), "wordpress-download-", crypto$1.randomUUID());
|
|
2633
|
+
await fs__default.promises.mkdir(tempDir, { recursive: true });
|
|
2634
|
+
try {
|
|
2635
|
+
const tmpDownloadPath = path__default.join(os.tmpdir(), `wordpress-${crypto$1.randomUUID()}.zip`);
|
|
2636
|
+
try {
|
|
2637
|
+
await downloadFile(getWordPressVersionUrl(wordPressVersion), tmpDownloadPath);
|
|
2638
|
+
await extractZip(tmpDownloadPath, tempDir);
|
|
2639
|
+
} finally {
|
|
2640
|
+
await fs__default.promises.rm(tmpDownloadPath, { force: true });
|
|
2641
|
+
}
|
|
2642
|
+
const wpSourcePath = path__default.join(tempDir, "wordpress");
|
|
2643
|
+
await fs__default.promises.mkdir(path__default.dirname(finalFolder), { recursive: true });
|
|
2644
|
+
await fs__default.promises.cp(wpSourcePath, finalFolder, {
|
|
2645
|
+
recursive: true,
|
|
2646
|
+
verbatimSymlinks: true
|
|
2647
|
+
});
|
|
2648
|
+
} finally {
|
|
2649
|
+
try {
|
|
2650
|
+
await fs__default.promises.rm(tempDir, { force: true, recursive: true });
|
|
2651
|
+
} catch {
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
async function fetchWordPressVersions$1() {
|
|
2656
|
+
try {
|
|
2657
|
+
const response2 = await fetch("https://api.wordpress.org/core/stable-check/1.0/");
|
|
2658
|
+
const versionsStatus = await response2.json();
|
|
2659
|
+
const versions = Object.keys(versionsStatus).filter((item) => {
|
|
2660
|
+
const version2 = semver.coerce(item);
|
|
2661
|
+
const minVersion = semver.coerce(MINIMUM_SUPPORTED_WP_VERSION);
|
|
2662
|
+
return version2 && minVersion && semver.gte(version2, minVersion);
|
|
2663
|
+
}).sort((a, b) => {
|
|
2664
|
+
const versionA = semver.coerce(a);
|
|
2665
|
+
const versionB = semver.coerce(b);
|
|
2666
|
+
if (!versionA || !versionB) {
|
|
2667
|
+
return 0;
|
|
2668
|
+
}
|
|
2669
|
+
return semver.compare(versionA, versionB);
|
|
2670
|
+
}).reverse();
|
|
2671
|
+
const latestVersion = Object.keys(versionsStatus).find(
|
|
2672
|
+
(index) => versionsStatus[index] === "latest"
|
|
2673
|
+
);
|
|
2674
|
+
return { versions, latest: latestVersion };
|
|
2675
|
+
} catch (exception) {
|
|
2676
|
+
return { versions: [], latest: DEFAULT_WORDPRESS_VERSION };
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
async function getWordPressVersionFromInstallation(installationPath) {
|
|
2680
|
+
try {
|
|
2681
|
+
const versionFileContent = await fs__default.promises.readFile(
|
|
2682
|
+
path__default.join(installationPath, "wp-includes", "version.php"),
|
|
2683
|
+
"utf8"
|
|
2684
|
+
);
|
|
2685
|
+
const matches = versionFileContent.match(/\$wp_version\s*=\s*'([0-9a-zA-Z.-]+)'/);
|
|
2686
|
+
return matches?.[1];
|
|
2687
|
+
} catch (err) {
|
|
2688
|
+
return null;
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
async function updateLatestWordPressVersion() {
|
|
2692
|
+
let shouldOverwrite = false;
|
|
2693
|
+
const latestVersionPath = getWordPressVersionPath("latest");
|
|
2694
|
+
const latestVersionFiles = fs__default.existsSync(latestVersionPath) ? await fs__default.promises.readdir(latestVersionPath) : [];
|
|
2695
|
+
if (latestVersionFiles.length !== 0) {
|
|
2696
|
+
const installedVersion = await getWordPressVersionFromInstallation(latestVersionPath);
|
|
2697
|
+
const wordPressVersions = await fetchWordPressVersions$1();
|
|
2698
|
+
const latestVersion = wordPressVersions.latest ?? DEFAULT_WORDPRESS_VERSION;
|
|
2699
|
+
if (installedVersion && latestVersion !== "latest" && installedVersion !== latestVersion) {
|
|
2700
|
+
await recursiveCopyDirectory(
|
|
2701
|
+
latestVersionPath,
|
|
2702
|
+
getWordPressVersionPath(installedVersion)
|
|
2703
|
+
);
|
|
2704
|
+
shouldOverwrite = true;
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
await downloadWordPress("latest", shouldOverwrite);
|
|
2708
|
+
}
|
|
2709
|
+
const processIdAllocator = new ProcessIdAllocator();
|
|
2710
|
+
const PLAYGROUND_INTERNAL_SHARED_FOLDER = "/internal/shared";
|
|
2711
|
+
function createNoopSpawnHandler() {
|
|
2712
|
+
return createSpawnHandler(async (args, processApi) => {
|
|
2713
|
+
await new Promise((resolve) => setTimeout(resolve, 1));
|
|
2714
|
+
processApi.exit(1);
|
|
2715
|
+
});
|
|
2716
|
+
}
|
|
2717
|
+
async function runWpCliCommand(siteFolder, phpVersion, args) {
|
|
2718
|
+
const id = await loadNodeRuntime(phpVersion, {
|
|
2719
|
+
followSymlinks: true,
|
|
2720
|
+
withRedis: IS_JSPI_AVAILABLE,
|
|
2721
|
+
withMemcached: IS_JSPI_AVAILABLE,
|
|
2722
|
+
emscriptenOptions: {
|
|
2723
|
+
processId: processIdAllocator.claim()
|
|
2724
|
+
}
|
|
2725
|
+
});
|
|
2726
|
+
const php = new PHP(id);
|
|
2727
|
+
try {
|
|
2728
|
+
await php.setSapiName("cli");
|
|
2729
|
+
php.defineConstant("DB_NAME", "wordpress");
|
|
2730
|
+
php.mkdir("/wordpress");
|
|
2731
|
+
await php.mount("/wordpress", createNodeFsMountHandler(siteFolder));
|
|
2732
|
+
php.writeFile("/tmp/ca-bundle.crt", rootCertificates.join("\n"));
|
|
2733
|
+
await setPhpIniEntries(php, {
|
|
2734
|
+
"openssl.cafile": "/tmp/ca-bundle.crt",
|
|
2735
|
+
allow_url_fopen: 1
|
|
2736
|
+
});
|
|
2737
|
+
await php.setSpawnHandler(createNoopSpawnHandler());
|
|
2738
|
+
await cleanupLegacyMuPlugins(siteFolder);
|
|
2739
|
+
const [studioMuPluginsHostPath, loaderMuPluginHostPath] = await getMuPlugins({
|
|
2740
|
+
isWpAutoUpdating: false
|
|
2741
|
+
});
|
|
2742
|
+
await php.mount(
|
|
2743
|
+
"/internal/studio/mu-plugins",
|
|
2744
|
+
createNodeFsMountHandler(studioMuPluginsHostPath)
|
|
2745
|
+
);
|
|
2746
|
+
await php.mount(
|
|
2747
|
+
PLAYGROUND_INTERNAL_SHARED_FOLDER + "/mu-plugins/99-studio-loader.php",
|
|
2748
|
+
createNodeFsMountHandler(loaderMuPluginHostPath)
|
|
2749
|
+
);
|
|
2750
|
+
await php.mount("/tmp/wp-cli.phar", createNodeFsMountHandler(getWpCliPharPath()));
|
|
2751
|
+
await php.mount("/tmp/sqlite-command", createNodeFsMountHandler(getSqliteCommandPath()));
|
|
2752
|
+
await setupPlatformLevelMuPlugins(php);
|
|
2753
|
+
const response2 = await php.cli(["php", "/tmp/wp-cli.phar", "--path=/wordpress", ...args]);
|
|
2754
|
+
return {
|
|
2755
|
+
response: response2,
|
|
2756
|
+
[Symbol.dispose]() {
|
|
2757
|
+
php.exit();
|
|
2758
|
+
}
|
|
2759
|
+
};
|
|
2760
|
+
} catch (error2) {
|
|
2761
|
+
php.exit();
|
|
2762
|
+
throw new Error(__("An error occurred while running the WP-CLI command."));
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
async function runGlobalWpCliCommand(args) {
|
|
2766
|
+
const id = await loadNodeRuntime(LatestSupportedPHPVersion, {
|
|
2767
|
+
followSymlinks: true,
|
|
2768
|
+
withRedis: false,
|
|
2769
|
+
withMemcached: false,
|
|
2770
|
+
emscriptenOptions: {
|
|
2771
|
+
processId: processIdAllocator.claim()
|
|
2772
|
+
}
|
|
2773
|
+
});
|
|
2774
|
+
const php = new PHP(id);
|
|
2775
|
+
try {
|
|
2776
|
+
await php.setSapiName("cli");
|
|
2777
|
+
php.writeFile("/tmp/ca-bundle.crt", rootCertificates.join("\n"));
|
|
2778
|
+
await setPhpIniEntries(php, {
|
|
2779
|
+
"openssl.cafile": "/tmp/ca-bundle.crt",
|
|
2780
|
+
allow_url_fopen: 1
|
|
2781
|
+
});
|
|
2782
|
+
await php.setSpawnHandler(createNoopSpawnHandler());
|
|
2783
|
+
await php.mount("/tmp/wp-cli.phar", createNodeFsMountHandler(getWpCliPharPath()));
|
|
2784
|
+
const response2 = await php.cli(["php", "/tmp/wp-cli.phar", ...args]);
|
|
2785
|
+
return {
|
|
2786
|
+
response: response2,
|
|
2787
|
+
[Symbol.dispose]() {
|
|
2788
|
+
php.exit();
|
|
2789
|
+
}
|
|
2790
|
+
};
|
|
2791
|
+
} catch (error2) {
|
|
2792
|
+
php.exit();
|
|
2793
|
+
throw new Error(__("An error occurred while running the WP-CLI command."));
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
class ImportExportEventEmitter extends EventEmitter {
|
|
2797
|
+
on(eventName, listener) {
|
|
2798
|
+
return super.on(eventName, listener);
|
|
2799
|
+
}
|
|
2800
|
+
off(eventName, listener) {
|
|
2801
|
+
return super.off(eventName, listener);
|
|
2802
|
+
}
|
|
2803
|
+
emit(eventName, ...args) {
|
|
2804
|
+
return super.emit(eventName, ...args);
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
3145
2807
|
async function exportDatabaseToFile(siteFolder, finalDestination) {
|
|
3146
2808
|
var _stack = [];
|
|
3147
2809
|
try {
|
|
@@ -3397,6 +3059,9 @@ class DefaultExporter extends ImportExportEventEmitter {
|
|
|
3397
3059
|
if (!fs__default.existsSync(fullPath)) {
|
|
3398
3060
|
continue;
|
|
3399
3061
|
}
|
|
3062
|
+
if (this.options.ignoreFilter?.ignores(archivePath)) {
|
|
3063
|
+
continue;
|
|
3064
|
+
}
|
|
3400
3065
|
const stat = await fs$1.stat(fullPath);
|
|
3401
3066
|
if (stat.isDirectory()) {
|
|
3402
3067
|
this.archiveBuilder.directory(fullPath, archivePath, (entry) => {
|
|
@@ -3405,13 +3070,13 @@ class DefaultExporter extends ImportExportEventEmitter {
|
|
|
3405
3070
|
this.options.site.path,
|
|
3406
3071
|
entryPathRelativeToArchiveRoot
|
|
3407
3072
|
);
|
|
3408
|
-
if (this.isExactPathExcluded(entryPathRelativeToArchiveRoot) || this.isPathExcludedByPattern(fullEntryPathOnDisk)) {
|
|
3073
|
+
if (this.isExactPathExcluded(entryPathRelativeToArchiveRoot) || this.isPathExcludedByPattern(fullEntryPathOnDisk) || this.options.ignoreFilter?.ignores(entryPathRelativeToArchiveRoot)) {
|
|
3409
3074
|
return false;
|
|
3410
3075
|
}
|
|
3411
3076
|
return entry;
|
|
3412
3077
|
});
|
|
3413
3078
|
} else {
|
|
3414
|
-
if (this.isExactPathExcluded(archivePath)) {
|
|
3079
|
+
if (this.isExactPathExcluded(archivePath) || this.options.ignoreFilter?.ignores(archivePath)) {
|
|
3415
3080
|
continue;
|
|
3416
3081
|
}
|
|
3417
3082
|
this.archiveBuilder.file(fullPath, { name: archivePath });
|
|
@@ -3459,8 +3124,12 @@ class DefaultExporter extends ImportExportEventEmitter {
|
|
|
3459
3124
|
this.getSitePlugins(this.options.site.path),
|
|
3460
3125
|
this.getSiteThemes(this.options.site.path)
|
|
3461
3126
|
]);
|
|
3462
|
-
studioJson.plugins = plugins
|
|
3463
|
-
|
|
3127
|
+
studioJson.plugins = this.options.ignoreFilter ? plugins.filter(
|
|
3128
|
+
(p) => !this.options.ignoreFilter.ignores(`wp-content/plugins/${p.name}`)
|
|
3129
|
+
) : plugins;
|
|
3130
|
+
studioJson.themes = this.options.ignoreFilter ? themes.filter(
|
|
3131
|
+
(t) => !this.options.ignoreFilter.ignores(`wp-content/themes/${t.name}`)
|
|
3132
|
+
) : themes;
|
|
3464
3133
|
const tempDir = await fs$1.mkdtemp(path__default.join(os.tmpdir(), "studio-export-"));
|
|
3465
3134
|
const studioJsonPath = path__default.join(tempDir, "meta.json");
|
|
3466
3135
|
await fs$1.writeFile(studioJsonPath, JSON.stringify(studioJson, null, 2));
|
|
@@ -3607,6 +3276,48 @@ function untildify(path2) {
|
|
|
3607
3276
|
return process.platform === "win32" ? path2 : path2.replace(/^~/, os$1.homedir());
|
|
3608
3277
|
}
|
|
3609
3278
|
const logger$c = new Logger();
|
|
3279
|
+
function sendIpcEvent$1(eventTuple) {
|
|
3280
|
+
const ipcEvent = { event: eventTuple };
|
|
3281
|
+
process.send(ipcEvent);
|
|
3282
|
+
}
|
|
3283
|
+
function handleExportIpc(emitter2) {
|
|
3284
|
+
emitter2.on(ExportEvents.EXPORT_START, () => {
|
|
3285
|
+
sendIpcEvent$1([ExportEvents.EXPORT_START, void 0]);
|
|
3286
|
+
});
|
|
3287
|
+
emitter2.on(ExportEvents.BACKUP_CREATE_START, () => {
|
|
3288
|
+
sendIpcEvent$1([ExportEvents.BACKUP_CREATE_START, void 0]);
|
|
3289
|
+
});
|
|
3290
|
+
emitter2.on(ExportEvents.WP_CONTENT_EXPORT_START, () => {
|
|
3291
|
+
sendIpcEvent$1([ExportEvents.WP_CONTENT_EXPORT_START, void 0]);
|
|
3292
|
+
});
|
|
3293
|
+
emitter2.on(ExportEvents.WP_CONTENT_EXPORT_COMPLETE, () => {
|
|
3294
|
+
sendIpcEvent$1([ExportEvents.WP_CONTENT_EXPORT_COMPLETE, void 0]);
|
|
3295
|
+
});
|
|
3296
|
+
emitter2.on(ExportEvents.DATABASE_EXPORT_START, () => {
|
|
3297
|
+
sendIpcEvent$1([ExportEvents.DATABASE_EXPORT_START, void 0]);
|
|
3298
|
+
});
|
|
3299
|
+
emitter2.on(ExportEvents.DATABASE_EXPORT_COMPLETE, () => {
|
|
3300
|
+
sendIpcEvent$1([ExportEvents.DATABASE_EXPORT_COMPLETE, void 0]);
|
|
3301
|
+
});
|
|
3302
|
+
emitter2.on(ExportEvents.BACKUP_CREATE_PROGRESS, (progressData) => {
|
|
3303
|
+
sendIpcEvent$1([ExportEvents.BACKUP_CREATE_PROGRESS, progressData]);
|
|
3304
|
+
});
|
|
3305
|
+
emitter2.on(ExportEvents.BACKUP_CREATE_COMPLETE, () => {
|
|
3306
|
+
sendIpcEvent$1([ExportEvents.BACKUP_CREATE_COMPLETE, void 0]);
|
|
3307
|
+
});
|
|
3308
|
+
emitter2.on(ExportEvents.CONFIG_EXPORT_START, () => {
|
|
3309
|
+
sendIpcEvent$1([ExportEvents.CONFIG_EXPORT_START, void 0]);
|
|
3310
|
+
});
|
|
3311
|
+
emitter2.on(ExportEvents.CONFIG_EXPORT_COMPLETE, () => {
|
|
3312
|
+
sendIpcEvent$1([ExportEvents.CONFIG_EXPORT_COMPLETE, void 0]);
|
|
3313
|
+
});
|
|
3314
|
+
emitter2.on(ExportEvents.EXPORT_COMPLETE, () => {
|
|
3315
|
+
sendIpcEvent$1([ExportEvents.EXPORT_COMPLETE, void 0]);
|
|
3316
|
+
});
|
|
3317
|
+
emitter2.on(ExportEvents.EXPORT_ERROR, (error2) => {
|
|
3318
|
+
sendIpcEvent$1([ExportEvents.EXPORT_ERROR, error2]);
|
|
3319
|
+
});
|
|
3320
|
+
}
|
|
3610
3321
|
function handleExportEvents(emitter2) {
|
|
3611
3322
|
emitter2.on(ExportEvents.EXPORT_START, () => {
|
|
3612
3323
|
logger$c.reportStart(SiteCommandLoggerAction.EXPORT_SITE, __("Starting export…"));
|
|
@@ -3651,7 +3362,7 @@ function handleExportEvents(emitter2) {
|
|
|
3651
3362
|
throw new LoggerError(__("Export failed"), error2 instanceof Error ? error2 : void 0);
|
|
3652
3363
|
});
|
|
3653
3364
|
}
|
|
3654
|
-
async function runCommand$e(siteFolder, exportPath, mode) {
|
|
3365
|
+
async function runCommand$e(siteFolder, exportPath, mode = "full", splitDbDumpByTable = false, includeOnlyPaths, applyDeployIgnore = false) {
|
|
3655
3366
|
try {
|
|
3656
3367
|
logger$c.reportStart(SiteCommandLoggerAction.START_DAEMON, __("Starting process daemon…"));
|
|
3657
3368
|
await connectToDaemon();
|
|
@@ -3668,17 +3379,27 @@ async function runCommand$e(siteFolder, exportPath, mode) {
|
|
|
3668
3379
|
const includes = { database: true, wpContent: true };
|
|
3669
3380
|
if (mode === "db") {
|
|
3670
3381
|
includes.wpContent = false;
|
|
3382
|
+
} else if (mode === "content") {
|
|
3383
|
+
includes.database = false;
|
|
3671
3384
|
}
|
|
3385
|
+
const ignoreFilter = applyDeployIgnore ? await createDeployIgnoreFilter(site.path, SYNC_IGNORE_DEFAULTS) : void 0;
|
|
3672
3386
|
const exporter = await getExporter({
|
|
3673
3387
|
site,
|
|
3674
3388
|
backupFile: exportPath,
|
|
3675
3389
|
phpVersion: DEFAULT_PHP_VERSION,
|
|
3676
|
-
includes
|
|
3390
|
+
includes,
|
|
3391
|
+
splitDatabaseDumpByTable: splitDbDumpByTable,
|
|
3392
|
+
specificSelectionPaths: includeOnlyPaths,
|
|
3393
|
+
ignoreFilter
|
|
3677
3394
|
});
|
|
3678
3395
|
if (!exporter) {
|
|
3679
3396
|
throw new LoggerError(__("No suitable exporter found for the provided backup file"));
|
|
3680
3397
|
}
|
|
3681
|
-
|
|
3398
|
+
if (process.send) {
|
|
3399
|
+
handleExportIpc(exporter);
|
|
3400
|
+
} else {
|
|
3401
|
+
handleExportEvents(exporter);
|
|
3402
|
+
}
|
|
3682
3403
|
await exporter.export();
|
|
3683
3404
|
logger$c.reportSuccess(sprintf(__("%s successfully exported"), exportPath));
|
|
3684
3405
|
} finally {
|
|
@@ -3705,18 +3426,38 @@ const registerCommand$e = (yargs2) => {
|
|
|
3705
3426
|
normalize: true,
|
|
3706
3427
|
demandOption: false,
|
|
3707
3428
|
description: __(
|
|
3708
|
-
"Path to the export file.
|
|
3429
|
+
"Path to the export file. All exports can use .zip or .tar.gz. Database-only exports can also use .sql."
|
|
3709
3430
|
),
|
|
3710
3431
|
coerce: (value) => {
|
|
3711
3432
|
return path__default.resolve(untildify(value));
|
|
3712
3433
|
}
|
|
3713
3434
|
}).option("mode", {
|
|
3714
3435
|
type: "string",
|
|
3715
|
-
choices: ["full", "db"],
|
|
3436
|
+
choices: ["full", "content", "db"],
|
|
3716
3437
|
default: "full",
|
|
3717
3438
|
description: __(
|
|
3718
|
-
"Export the full site or just the database. Default exports full site."
|
|
3439
|
+
"Export the full site, just the content, or just the database. Default exports full site."
|
|
3719
3440
|
)
|
|
3441
|
+
}).option("split-db-dump-by-table", {
|
|
3442
|
+
type: "boolean",
|
|
3443
|
+
default: false,
|
|
3444
|
+
description: __("Split the database dump by table"),
|
|
3445
|
+
hidden: true
|
|
3446
|
+
}).option("include-only", {
|
|
3447
|
+
type: "array",
|
|
3448
|
+
description: __("Include only the specified paths in the export"),
|
|
3449
|
+
coerce: (value) => {
|
|
3450
|
+
if (!Array.isArray(value)) {
|
|
3451
|
+
throw new Error(__("include-only must be an array"));
|
|
3452
|
+
}
|
|
3453
|
+
return value.map(String);
|
|
3454
|
+
},
|
|
3455
|
+
hidden: true
|
|
3456
|
+
}).option("apply-deploy-ignore", {
|
|
3457
|
+
type: "boolean",
|
|
3458
|
+
default: false,
|
|
3459
|
+
description: __("Apply .deployignore patterns when exporting"),
|
|
3460
|
+
hidden: true
|
|
3720
3461
|
});
|
|
3721
3462
|
},
|
|
3722
3463
|
handler: async (argv) => {
|
|
@@ -3725,24 +3466,26 @@ const registerCommand$e = (yargs2) => {
|
|
|
3725
3466
|
const timestamp = getTimestamp();
|
|
3726
3467
|
if (argv.exportFile) {
|
|
3727
3468
|
exportFile = argv.exportFile;
|
|
3728
|
-
} else if (argv.mode === "full") {
|
|
3469
|
+
} else if (argv.mode === "full" || argv.mode === "content") {
|
|
3729
3470
|
exportFile = path__default.join(process.cwd(), `studio-backup-${timestamp}.zip`);
|
|
3730
3471
|
} else {
|
|
3731
3472
|
exportFile = path__default.join(process.cwd(), `studio-backup-${timestamp}.sql`);
|
|
3732
3473
|
}
|
|
3733
|
-
if (argv.mode === "full" && !exportFile.endsWith(".zip") && !exportFile.endsWith(".tar.gz")) {
|
|
3474
|
+
if ((argv.mode === "full" || argv.mode === "content") && !exportFile.endsWith(".zip") && !exportFile.endsWith(".tar.gz")) {
|
|
3734
3475
|
throw new LoggerError(
|
|
3735
3476
|
__(
|
|
3736
3477
|
"Invalid export file extension. Must be .zip or .tar.gz when exporting the full site."
|
|
3737
3478
|
)
|
|
3738
3479
|
);
|
|
3739
3480
|
}
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3481
|
+
await runCommand$e(
|
|
3482
|
+
argv.path,
|
|
3483
|
+
exportFile,
|
|
3484
|
+
argv.mode,
|
|
3485
|
+
argv.splitDbDumpByTable,
|
|
3486
|
+
argv.includeOnly,
|
|
3487
|
+
argv.applyDeployIgnore
|
|
3488
|
+
);
|
|
3746
3489
|
} catch (error2) {
|
|
3747
3490
|
if (error2 instanceof LoggerError) {
|
|
3748
3491
|
logger$c.reportError(error2);
|
|
@@ -3761,11 +3504,7 @@ class BackupHandlerSql extends ImportExportEventEmitter {
|
|
|
3761
3504
|
async extractFiles(file, extractionDirectory) {
|
|
3762
3505
|
const fileName = path__default.basename(file.path);
|
|
3763
3506
|
const destPath = path__default.join(extractionDirectory, fileName);
|
|
3764
|
-
this.emit(ImportEvents.BACKUP_EXTRACT_START
|
|
3765
|
-
progress: 0,
|
|
3766
|
-
totalFiles: 1,
|
|
3767
|
-
processedFiles: 0
|
|
3768
|
-
});
|
|
3507
|
+
this.emit(ImportEvents.BACKUP_EXTRACT_START);
|
|
3769
3508
|
this.emit(ImportEvents.BACKUP_EXTRACT_FILE_START, {
|
|
3770
3509
|
progress: 0,
|
|
3771
3510
|
processedFiles: 0,
|
|
@@ -3995,11 +3734,7 @@ class BackupHandlerWpress extends ImportExportEventEmitter {
|
|
|
3995
3734
|
const fileNames = await this.listFiles(file);
|
|
3996
3735
|
this.totalFiles = fileNames.length;
|
|
3997
3736
|
this.processedFiles = 0;
|
|
3998
|
-
this.emit(ImportEvents.BACKUP_EXTRACT_START
|
|
3999
|
-
progress: 0,
|
|
4000
|
-
totalFiles: this.totalFiles,
|
|
4001
|
-
processedFiles: 0
|
|
4002
|
-
});
|
|
3737
|
+
this.emit(ImportEvents.BACKUP_EXTRACT_START);
|
|
4003
3738
|
const inputFile = await fs.promises.open(file.path, "r");
|
|
4004
3739
|
let header;
|
|
4005
3740
|
try {
|
|
@@ -4346,7 +4081,6 @@ class BaseBackupImporter extends BaseImporter {
|
|
|
4346
4081
|
this.shouldCleanUpBeforeImport = true;
|
|
4347
4082
|
}
|
|
4348
4083
|
async import(site) {
|
|
4349
|
-
this.emit(ImportEvents.IMPORT_START);
|
|
4350
4084
|
try {
|
|
4351
4085
|
if (this.shouldCleanUpBeforeImport) {
|
|
4352
4086
|
await this.moveExistingWpContentToTrash(site.path);
|
|
@@ -4363,18 +4097,19 @@ class BaseBackupImporter extends BaseImporter {
|
|
|
4363
4097
|
await this.createEmptyDatabase(dbPath);
|
|
4364
4098
|
await this.importDatabase(site, this.backup.sqlFiles);
|
|
4365
4099
|
}
|
|
4366
|
-
this.emit(ImportEvents.IMPORT_COMPLETE);
|
|
4367
4100
|
return {
|
|
4368
4101
|
extractionDirectory: this.backup.extractionDirectory,
|
|
4369
4102
|
sqlFiles: this.backup.sqlFiles,
|
|
4370
4103
|
wpContentFiles: this.backup.wpContentFiles,
|
|
4371
4104
|
wpContentDirectory: this.backup.wpContentDirectory,
|
|
4372
4105
|
wpConfig: this.backup.wpConfig,
|
|
4373
|
-
meta: this.meta
|
|
4374
|
-
importerType: this.constructor.name
|
|
4106
|
+
meta: this.meta
|
|
4375
4107
|
};
|
|
4376
4108
|
} catch (error2) {
|
|
4377
|
-
this.emit(
|
|
4109
|
+
this.emit(
|
|
4110
|
+
ImportEvents.IMPORT_ERROR,
|
|
4111
|
+
error2 instanceof Error ? error2.message : String(error2)
|
|
4112
|
+
);
|
|
4378
4113
|
throw error2;
|
|
4379
4114
|
}
|
|
4380
4115
|
}
|
|
@@ -4520,6 +4255,12 @@ class JetpackImporter extends BaseBackupImporter {
|
|
|
4520
4255
|
this.emit(ImportEvents.IMPORT_META_COMPLETE);
|
|
4521
4256
|
}
|
|
4522
4257
|
}
|
|
4258
|
+
async import(site) {
|
|
4259
|
+
this.emit(ImportEvents.IMPORT_START, "jetpack");
|
|
4260
|
+
const result = await super.import(site);
|
|
4261
|
+
this.emit(ImportEvents.IMPORT_COMPLETE, "jetpack");
|
|
4262
|
+
return result;
|
|
4263
|
+
}
|
|
4523
4264
|
}
|
|
4524
4265
|
class LocalImporter extends BaseBackupImporter {
|
|
4525
4266
|
async parseMetaFile() {
|
|
@@ -4541,6 +4282,12 @@ class LocalImporter extends BaseBackupImporter {
|
|
|
4541
4282
|
this.emit(ImportEvents.IMPORT_META_COMPLETE);
|
|
4542
4283
|
}
|
|
4543
4284
|
}
|
|
4285
|
+
async import(site) {
|
|
4286
|
+
this.emit(ImportEvents.IMPORT_START, "local");
|
|
4287
|
+
const result = await super.import(site);
|
|
4288
|
+
this.emit(ImportEvents.IMPORT_COMPLETE, "local");
|
|
4289
|
+
return result;
|
|
4290
|
+
}
|
|
4544
4291
|
}
|
|
4545
4292
|
class PlaygroundImporter extends BaseBackupImporter {
|
|
4546
4293
|
async importDatabase(site, sqlFiles) {
|
|
@@ -4559,23 +4306,31 @@ class PlaygroundImporter extends BaseBackupImporter {
|
|
|
4559
4306
|
async parseMetaFile() {
|
|
4560
4307
|
return void 0;
|
|
4561
4308
|
}
|
|
4309
|
+
async import(site) {
|
|
4310
|
+
this.emit(ImportEvents.IMPORT_START, "playground");
|
|
4311
|
+
const result = await super.import(site);
|
|
4312
|
+
this.emit(ImportEvents.IMPORT_COMPLETE, "playground");
|
|
4313
|
+
return result;
|
|
4314
|
+
}
|
|
4562
4315
|
}
|
|
4563
4316
|
class SQLImporter extends BaseImporter {
|
|
4564
4317
|
async import(site) {
|
|
4565
|
-
this.emit(ImportEvents.IMPORT_START);
|
|
4318
|
+
this.emit(ImportEvents.IMPORT_START, "sql");
|
|
4566
4319
|
try {
|
|
4567
4320
|
await this.importDatabase(site, this.backup.sqlFiles);
|
|
4568
|
-
this.emit(ImportEvents.IMPORT_COMPLETE);
|
|
4321
|
+
this.emit(ImportEvents.IMPORT_COMPLETE, "sql");
|
|
4569
4322
|
return {
|
|
4570
4323
|
extractionDirectory: this.backup.extractionDirectory,
|
|
4571
4324
|
sqlFiles: this.backup.sqlFiles,
|
|
4572
4325
|
wpConfig: this.backup.wpConfig,
|
|
4573
4326
|
wpContentFiles: this.backup.wpContentFiles,
|
|
4574
|
-
wpContentDirectory: this.backup.wpContentDirectory
|
|
4575
|
-
importerType: this.constructor.name
|
|
4327
|
+
wpContentDirectory: this.backup.wpContentDirectory
|
|
4576
4328
|
};
|
|
4577
4329
|
} catch (error2) {
|
|
4578
|
-
this.emit(
|
|
4330
|
+
this.emit(
|
|
4331
|
+
ImportEvents.IMPORT_ERROR,
|
|
4332
|
+
error2 instanceof Error ? error2.message : String(error2)
|
|
4333
|
+
);
|
|
4579
4334
|
throw error2;
|
|
4580
4335
|
}
|
|
4581
4336
|
}
|
|
@@ -4655,6 +4410,12 @@ class WpressImporter extends BaseBackupImporter {
|
|
|
4655
4410
|
await this.addSqlToActivatePlugins(sqlFiles);
|
|
4656
4411
|
await super.importDatabase(site, sqlFiles);
|
|
4657
4412
|
}
|
|
4413
|
+
async import(site) {
|
|
4414
|
+
this.emit(ImportEvents.IMPORT_START, "wpress");
|
|
4415
|
+
const result = await super.import(site);
|
|
4416
|
+
this.emit(ImportEvents.IMPORT_COMPLETE, "wpress");
|
|
4417
|
+
return result;
|
|
4418
|
+
}
|
|
4658
4419
|
}
|
|
4659
4420
|
class JetpackValidator extends ImportExportEventEmitter {
|
|
4660
4421
|
canHandle(fileList) {
|
|
@@ -4951,43 +4712,101 @@ async function startWordPressServer(site, logger2, options) {
|
|
|
4951
4712
|
if (site.enableDebugDisplay) {
|
|
4952
4713
|
serverConfig.enableDebugDisplay = true;
|
|
4953
4714
|
}
|
|
4954
|
-
const
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
processDesc.pmId
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4715
|
+
const readyOrExit = await subscribeForReadyOrExit(processName);
|
|
4716
|
+
try {
|
|
4717
|
+
const processDesc = await startProcess(processName, wordPressServerChildPath);
|
|
4718
|
+
await readyOrExit.waitFor(processDesc.pmId);
|
|
4719
|
+
await sendMessage(
|
|
4720
|
+
processDesc.pmId,
|
|
4721
|
+
processName,
|
|
4722
|
+
{
|
|
4723
|
+
topic: "start-server",
|
|
4724
|
+
data: { config: serverConfig }
|
|
4725
|
+
},
|
|
4726
|
+
{ logger: logger2 }
|
|
4727
|
+
);
|
|
4728
|
+
return processDesc;
|
|
4729
|
+
} finally {
|
|
4730
|
+
readyOrExit.dispose();
|
|
4731
|
+
}
|
|
4732
|
+
}
|
|
4733
|
+
function buildChildExitedError(processName, stderrTail) {
|
|
4734
|
+
let message2 = `WordPress server child process "${processName}" exited before becoming ready.`;
|
|
4735
|
+
if (stderrTail?.trim()) {
|
|
4736
|
+
message2 += `
|
|
4737
|
+
${stderrTail.trimEnd()}`;
|
|
4738
|
+
}
|
|
4739
|
+
return new Error(message2);
|
|
4966
4740
|
}
|
|
4967
|
-
async function
|
|
4741
|
+
async function subscribeForReadyOrExit(processName) {
|
|
4968
4742
|
const bus = await getDaemonBus();
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
let
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4743
|
+
const pendingReady = [];
|
|
4744
|
+
const pendingExits = [];
|
|
4745
|
+
let onReady = () => {
|
|
4746
|
+
};
|
|
4747
|
+
let onExit2 = () => {
|
|
4748
|
+
};
|
|
4749
|
+
let waiting = false;
|
|
4750
|
+
const messageHandler = (packet) => {
|
|
4751
|
+
if (packet.process.name !== processName || packet.raw.topic !== "ready") {
|
|
4752
|
+
return;
|
|
4753
|
+
}
|
|
4754
|
+
if (waiting) {
|
|
4755
|
+
onReady();
|
|
4756
|
+
} else {
|
|
4757
|
+
pendingReady.push(packet);
|
|
4758
|
+
}
|
|
4759
|
+
};
|
|
4760
|
+
const eventHandler = (event) => {
|
|
4761
|
+
if (event.process.name !== processName || event.event !== "exit") {
|
|
4762
|
+
return;
|
|
4763
|
+
}
|
|
4764
|
+
if (waiting) {
|
|
4765
|
+
onExit2(event.stderrTail);
|
|
4766
|
+
} else {
|
|
4767
|
+
pendingExits.push(event);
|
|
4768
|
+
}
|
|
4769
|
+
};
|
|
4770
|
+
bus.on("process-message", messageHandler);
|
|
4771
|
+
bus.on("process-event", eventHandler);
|
|
4772
|
+
const waitFor = (pmId) => {
|
|
4773
|
+
waiting = true;
|
|
4774
|
+
let timeoutId;
|
|
4775
|
+
let abortListener;
|
|
4776
|
+
return new Promise((resolve, reject) => {
|
|
4777
|
+
timeoutId = setTimeout(() => {
|
|
4778
|
+
reject(new Error("Timeout waiting for ready message from WordPress server child"));
|
|
4779
|
+
}, PLAYGROUND_CLI_INACTIVITY_TIMEOUT);
|
|
4780
|
+
abortListener = () => {
|
|
4781
|
+
reject(new Error("Operation aborted"));
|
|
4782
|
+
};
|
|
4783
|
+
onReady = () => resolve();
|
|
4784
|
+
onExit2 = (stderrTail) => reject(buildChildExitedError(processName, stderrTail));
|
|
4785
|
+
abortController.signal.addEventListener("abort", abortListener);
|
|
4786
|
+
const bufferedExit = pendingExits.find((event) => event.process.pm_id === pmId);
|
|
4787
|
+
if (bufferedExit) {
|
|
4788
|
+
onExit2(bufferedExit.stderrTail);
|
|
4789
|
+
return;
|
|
4979
4790
|
}
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4791
|
+
const bufferedReady = pendingReady.find((packet) => packet.process.pm_id === pmId);
|
|
4792
|
+
if (bufferedReady) {
|
|
4793
|
+
onReady();
|
|
4794
|
+
}
|
|
4795
|
+
}).finally(() => {
|
|
4796
|
+
clearTimeout(timeoutId);
|
|
4797
|
+
abortController.signal.removeEventListener("abort", abortListener);
|
|
4798
|
+
onReady = () => {
|
|
4799
|
+
};
|
|
4800
|
+
onExit2 = () => {
|
|
4801
|
+
};
|
|
4802
|
+
waiting = false;
|
|
4803
|
+
});
|
|
4804
|
+
};
|
|
4805
|
+
const dispose = () => {
|
|
4806
|
+
bus.off("process-message", messageHandler);
|
|
4807
|
+
bus.off("process-event", eventHandler);
|
|
4808
|
+
};
|
|
4809
|
+
return { waitFor, dispose };
|
|
4991
4810
|
}
|
|
4992
4811
|
const messageActivityTrackers = /* @__PURE__ */ new Map();
|
|
4993
4812
|
async function sendMessage(pmId, processName, message2, options = {}) {
|
|
@@ -5141,20 +4960,25 @@ async function runBlueprint(site, logger2, options) {
|
|
|
5141
4960
|
if (site.enableDebugDisplay) {
|
|
5142
4961
|
serverConfig.enableDebugDisplay = true;
|
|
5143
4962
|
}
|
|
5144
|
-
const
|
|
4963
|
+
const readyOrExit = await subscribeForReadyOrExit(processName);
|
|
5145
4964
|
try {
|
|
5146
|
-
await
|
|
5147
|
-
|
|
5148
|
-
processDesc.pmId
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
4965
|
+
const processDesc = await startProcess(processName, wordPressServerChildPath);
|
|
4966
|
+
try {
|
|
4967
|
+
await readyOrExit.waitFor(processDesc.pmId);
|
|
4968
|
+
await sendMessage(
|
|
4969
|
+
processDesc.pmId,
|
|
4970
|
+
processName,
|
|
4971
|
+
{
|
|
4972
|
+
topic: "run-blueprint",
|
|
4973
|
+
data: { config: serverConfig }
|
|
4974
|
+
},
|
|
4975
|
+
{ logger: logger2 }
|
|
4976
|
+
);
|
|
4977
|
+
} finally {
|
|
4978
|
+
await stopProcess(processName);
|
|
4979
|
+
}
|
|
5156
4980
|
} finally {
|
|
5157
|
-
|
|
4981
|
+
readyOrExit.dispose();
|
|
5158
4982
|
}
|
|
5159
4983
|
}
|
|
5160
4984
|
const wpCliResultSchema = z.object({
|
|
@@ -5192,6 +5016,69 @@ async function setupWordPressFilesOnly(sitePath) {
|
|
|
5192
5016
|
}
|
|
5193
5017
|
await recursiveCopyDirectory(bundledWpPath, sitePath);
|
|
5194
5018
|
}
|
|
5019
|
+
function sendIpcEvent(eventTuple) {
|
|
5020
|
+
const ipcEvent = { event: eventTuple };
|
|
5021
|
+
process.send(ipcEvent);
|
|
5022
|
+
}
|
|
5023
|
+
function handleImportIpc(emitter2) {
|
|
5024
|
+
emitter2.on(ValidatorEvents.IMPORT_VALIDATION_START, () => {
|
|
5025
|
+
sendIpcEvent([ValidatorEvents.IMPORT_VALIDATION_START, void 0]);
|
|
5026
|
+
});
|
|
5027
|
+
emitter2.on(ValidatorEvents.IMPORT_VALIDATION_COMPLETE, () => {
|
|
5028
|
+
sendIpcEvent([ValidatorEvents.IMPORT_VALIDATION_COMPLETE, void 0]);
|
|
5029
|
+
});
|
|
5030
|
+
emitter2.on(ValidatorEvents.IMPORT_VALIDATION_ERROR, (error2) => {
|
|
5031
|
+
sendIpcEvent([ValidatorEvents.IMPORT_VALIDATION_ERROR, error2]);
|
|
5032
|
+
});
|
|
5033
|
+
emitter2.on(BackupExtractEvents.BACKUP_EXTRACT_START, () => {
|
|
5034
|
+
sendIpcEvent([BackupExtractEvents.BACKUP_EXTRACT_START, void 0]);
|
|
5035
|
+
});
|
|
5036
|
+
emitter2.on(BackupExtractEvents.BACKUP_EXTRACT_PROGRESS, (progressData) => {
|
|
5037
|
+
sendIpcEvent([BackupExtractEvents.BACKUP_EXTRACT_PROGRESS, progressData]);
|
|
5038
|
+
});
|
|
5039
|
+
emitter2.on(BackupExtractEvents.BACKUP_EXTRACT_COMPLETE, () => {
|
|
5040
|
+
sendIpcEvent([BackupExtractEvents.BACKUP_EXTRACT_COMPLETE, void 0]);
|
|
5041
|
+
});
|
|
5042
|
+
emitter2.on(BackupExtractEvents.BACKUP_EXTRACT_WARNING, (warningMessage) => {
|
|
5043
|
+
sendIpcEvent([BackupExtractEvents.BACKUP_EXTRACT_WARNING, warningMessage]);
|
|
5044
|
+
});
|
|
5045
|
+
emitter2.on(BackupExtractEvents.BACKUP_EXTRACT_ERROR, (error2) => {
|
|
5046
|
+
sendIpcEvent([BackupExtractEvents.BACKUP_EXTRACT_ERROR, error2]);
|
|
5047
|
+
});
|
|
5048
|
+
emitter2.on(ImporterEvents.IMPORT_START, (importerType) => {
|
|
5049
|
+
sendIpcEvent([ImporterEvents.IMPORT_START, importerType]);
|
|
5050
|
+
});
|
|
5051
|
+
emitter2.on(ImporterEvents.IMPORT_DATABASE_START, () => {
|
|
5052
|
+
sendIpcEvent([ImporterEvents.IMPORT_DATABASE_START, void 0]);
|
|
5053
|
+
});
|
|
5054
|
+
emitter2.on(ImporterEvents.IMPORT_DATABASE_PROGRESS, (progressData) => {
|
|
5055
|
+
sendIpcEvent([ImporterEvents.IMPORT_DATABASE_PROGRESS, progressData]);
|
|
5056
|
+
});
|
|
5057
|
+
emitter2.on(ImporterEvents.IMPORT_DATABASE_COMPLETE, () => {
|
|
5058
|
+
sendIpcEvent([ImporterEvents.IMPORT_DATABASE_COMPLETE, void 0]);
|
|
5059
|
+
});
|
|
5060
|
+
emitter2.on(ImporterEvents.IMPORT_WP_CONTENT_START, () => {
|
|
5061
|
+
sendIpcEvent([ImporterEvents.IMPORT_WP_CONTENT_START, void 0]);
|
|
5062
|
+
});
|
|
5063
|
+
emitter2.on(ImporterEvents.IMPORT_WP_CONTENT_PROGRESS, (progressData) => {
|
|
5064
|
+
sendIpcEvent([ImporterEvents.IMPORT_WP_CONTENT_PROGRESS, progressData]);
|
|
5065
|
+
});
|
|
5066
|
+
emitter2.on(ImporterEvents.IMPORT_WP_CONTENT_COMPLETE, () => {
|
|
5067
|
+
sendIpcEvent([ImporterEvents.IMPORT_WP_CONTENT_COMPLETE, void 0]);
|
|
5068
|
+
});
|
|
5069
|
+
emitter2.on(ImporterEvents.IMPORT_META_START, () => {
|
|
5070
|
+
sendIpcEvent([ImporterEvents.IMPORT_META_START, void 0]);
|
|
5071
|
+
});
|
|
5072
|
+
emitter2.on(ImporterEvents.IMPORT_META_COMPLETE, () => {
|
|
5073
|
+
sendIpcEvent([ImporterEvents.IMPORT_META_COMPLETE, void 0]);
|
|
5074
|
+
});
|
|
5075
|
+
emitter2.on(ImporterEvents.IMPORT_COMPLETE, (importerType) => {
|
|
5076
|
+
sendIpcEvent([ImporterEvents.IMPORT_COMPLETE, importerType]);
|
|
5077
|
+
});
|
|
5078
|
+
emitter2.on(ImporterEvents.IMPORT_ERROR, (error2) => {
|
|
5079
|
+
sendIpcEvent([ImporterEvents.IMPORT_ERROR, error2]);
|
|
5080
|
+
});
|
|
5081
|
+
}
|
|
5195
5082
|
function handleImportEvents(emitter2) {
|
|
5196
5083
|
emitter2.on(ValidatorEvents.IMPORT_VALIDATION_START, () => {
|
|
5197
5084
|
logger$b.reportSuccess(sprintf(__("Started import…")));
|
|
@@ -5290,10 +5177,10 @@ function handleImportEvents(emitter2) {
|
|
|
5290
5177
|
logger$b.reportSuccess(__("Site imported successfully"));
|
|
5291
5178
|
});
|
|
5292
5179
|
emitter2.on(ImporterEvents.IMPORT_ERROR, (error2) => {
|
|
5293
|
-
throw new LoggerError(__("Import failed"),
|
|
5180
|
+
throw new LoggerError(__("Import failed"), new Error(error2));
|
|
5294
5181
|
});
|
|
5295
5182
|
}
|
|
5296
|
-
async function runCommand$d(siteFolder, importFile) {
|
|
5183
|
+
async function runCommand$d(siteFolder, importFile, alwaysStartServer = false) {
|
|
5297
5184
|
let site;
|
|
5298
5185
|
let wasServerRunning = false;
|
|
5299
5186
|
let importError;
|
|
@@ -5325,16 +5212,26 @@ async function runCommand$d(siteFolder, importFile) {
|
|
|
5325
5212
|
{ path: importFile, type: getBackupFileType(importFile) },
|
|
5326
5213
|
DEFAULT_IMPORTER_OPTIONS
|
|
5327
5214
|
);
|
|
5328
|
-
|
|
5329
|
-
|
|
5215
|
+
if (process.send) {
|
|
5216
|
+
handleImportIpc(importer);
|
|
5217
|
+
} else {
|
|
5218
|
+
handleImportEvents(importer);
|
|
5219
|
+
}
|
|
5220
|
+
const importResult = await importer.import(site);
|
|
5221
|
+
const importedPhpVersion = importResult.meta?.phpVersion;
|
|
5222
|
+
if (importedPhpVersion && importedPhpVersion !== site.phpVersion) {
|
|
5223
|
+
await updateSitePhpVersion(site.id, importedPhpVersion);
|
|
5224
|
+
site.phpVersion = importedPhpVersion;
|
|
5225
|
+
}
|
|
5330
5226
|
const siteUrl = getSiteUrl(site);
|
|
5331
5227
|
await fetch(siteUrl).catch(() => {
|
|
5332
5228
|
});
|
|
5229
|
+
await emitCliEvent({ event: SITE_EVENTS.UPDATED, data: { siteId: site.id } });
|
|
5333
5230
|
} catch (error2) {
|
|
5334
5231
|
importError = error2;
|
|
5335
5232
|
} finally {
|
|
5336
5233
|
try {
|
|
5337
|
-
if (site && wasServerRunning) {
|
|
5234
|
+
if (site && (wasServerRunning || alwaysStartServer)) {
|
|
5338
5235
|
logger$b.reportStart(
|
|
5339
5236
|
SiteCommandLoggerAction.INSTALL_SQLITE,
|
|
5340
5237
|
__("Setting up SQLite integration, if needed…")
|
|
@@ -5374,11 +5271,15 @@ const registerCommand$d = (yargs2) => {
|
|
|
5374
5271
|
coerce: (value) => {
|
|
5375
5272
|
return path__default.resolve(untildify(value));
|
|
5376
5273
|
}
|
|
5274
|
+
}).option("start-server", {
|
|
5275
|
+
type: "boolean",
|
|
5276
|
+
default: false,
|
|
5277
|
+
hidden: true
|
|
5377
5278
|
});
|
|
5378
5279
|
},
|
|
5379
5280
|
handler: async (argv) => {
|
|
5380
5281
|
try {
|
|
5381
|
-
await runCommand$d(argv.path, argv.importFile);
|
|
5282
|
+
await runCommand$d(argv.path, argv.importFile, argv.startServer);
|
|
5382
5283
|
} catch (error2) {
|
|
5383
5284
|
if (error2 instanceof LoggerError) {
|
|
5384
5285
|
logger$b.reportError(error2);
|
|
@@ -5406,14 +5307,68 @@ function getMcpServerConfig() {
|
|
|
5406
5307
|
function getMcpServerConfigJson() {
|
|
5407
5308
|
return JSON.stringify(getMcpServerConfig(), null, 2);
|
|
5408
5309
|
}
|
|
5310
|
+
const DEFAULT_BROWSER_ARGS = ["--ignore-certificate-errors"];
|
|
5409
5311
|
let browserPromise = null;
|
|
5312
|
+
const execFileAsync = promisify(execFile);
|
|
5313
|
+
function buildChromiumLaunchAttempts(chromium) {
|
|
5314
|
+
const attempts = [];
|
|
5315
|
+
const executablePath = chromium.executablePath();
|
|
5316
|
+
if (executablePath && existsSync(executablePath)) {
|
|
5317
|
+
attempts.push({
|
|
5318
|
+
args: DEFAULT_BROWSER_ARGS,
|
|
5319
|
+
executablePath
|
|
5320
|
+
});
|
|
5321
|
+
}
|
|
5322
|
+
attempts.push({
|
|
5323
|
+
args: DEFAULT_BROWSER_ARGS
|
|
5324
|
+
});
|
|
5325
|
+
return attempts;
|
|
5326
|
+
}
|
|
5327
|
+
async function installPlaywrightChromium() {
|
|
5328
|
+
const packageJsonPath = fileURLToPath(import.meta.resolve("playwright/package.json"));
|
|
5329
|
+
const cliPath = path__default.join(path__default.dirname(packageJsonPath), "cli.js");
|
|
5330
|
+
await execFileAsync(process.execPath, [cliPath, "install", "chromium"], {
|
|
5331
|
+
env: {
|
|
5332
|
+
...process.env,
|
|
5333
|
+
CI: process.env.CI ?? "1"
|
|
5334
|
+
},
|
|
5335
|
+
maxBuffer: 10 * 1024 * 1024
|
|
5336
|
+
});
|
|
5337
|
+
}
|
|
5338
|
+
async function ensurePlaywrightChromiumInstalled(chromium, installBrowser = installPlaywrightChromium) {
|
|
5339
|
+
const executablePath = chromium.executablePath();
|
|
5340
|
+
if (existsSync(executablePath)) {
|
|
5341
|
+
return null;
|
|
5342
|
+
}
|
|
5343
|
+
try {
|
|
5344
|
+
await installBrowser();
|
|
5345
|
+
} catch (error2) {
|
|
5346
|
+
return `Studio MCP could not auto-install Playwright Chromium. ${error2 instanceof Error ? error2.message : String(error2)}`;
|
|
5347
|
+
}
|
|
5348
|
+
if (!existsSync(chromium.executablePath())) {
|
|
5349
|
+
return "Studio MCP attempted to install Playwright Chromium, but the browser executable is still unavailable.";
|
|
5350
|
+
}
|
|
5351
|
+
return null;
|
|
5352
|
+
}
|
|
5410
5353
|
async function getSharedBrowser() {
|
|
5411
5354
|
if (!browserPromise) {
|
|
5412
5355
|
browserPromise = (async () => {
|
|
5413
5356
|
const { chromium } = await import("playwright");
|
|
5414
|
-
const
|
|
5415
|
-
|
|
5416
|
-
|
|
5357
|
+
const launchErrors = [];
|
|
5358
|
+
let browser2 = await tryLaunchChromium(chromium, launchErrors);
|
|
5359
|
+
let installError = null;
|
|
5360
|
+
if (!browser2) {
|
|
5361
|
+
installError = await ensurePlaywrightChromiumInstalled(chromium);
|
|
5362
|
+
if (!installError) {
|
|
5363
|
+
browser2 = await tryLaunchChromium(chromium, launchErrors);
|
|
5364
|
+
}
|
|
5365
|
+
}
|
|
5366
|
+
if (!browser2) {
|
|
5367
|
+
const repairGuidance = installError ?? "If Playwright Chromium is missing, run `studio mcp` again with network access so Studio can install it automatically.";
|
|
5368
|
+
throw new Error(
|
|
5369
|
+
`Unable to launch a browser for Studio MCP screenshot/validation tools. Tried ${launchErrors.map((error2) => error2.split(": ", 1)[0]).join(", ")}. ${repairGuidance} Launch errors: ${launchErrors.join(" | ")}`
|
|
5370
|
+
);
|
|
5371
|
+
}
|
|
5417
5372
|
const cleanup2 = () => {
|
|
5418
5373
|
browser2.close().catch(() => {
|
|
5419
5374
|
});
|
|
@@ -5435,6 +5390,21 @@ async function closeSharedBrowser() {
|
|
|
5435
5390
|
browserPromise = null;
|
|
5436
5391
|
}
|
|
5437
5392
|
}
|
|
5393
|
+
async function tryLaunchChromium(chromium, launchErrors) {
|
|
5394
|
+
for (const attempt of buildChromiumLaunchAttempts(chromium)) {
|
|
5395
|
+
if (!attempt) {
|
|
5396
|
+
continue;
|
|
5397
|
+
}
|
|
5398
|
+
const attemptedTarget = attempt.executablePath ? `executablePath=${attempt.executablePath}` : "playwright-default";
|
|
5399
|
+
try {
|
|
5400
|
+
return await chromium.launch(attempt);
|
|
5401
|
+
} catch (error2) {
|
|
5402
|
+
launchErrors.push(
|
|
5403
|
+
`${attemptedTarget}: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
5404
|
+
);
|
|
5405
|
+
}
|
|
5406
|
+
}
|
|
5407
|
+
}
|
|
5438
5408
|
async function getPageDiagnostics(page) {
|
|
5439
5409
|
try {
|
|
5440
5410
|
const url = page.url();
|
|
@@ -5732,7 +5702,7 @@ function collectMetricsScript() {
|
|
|
5732
5702
|
fonts
|
|
5733
5703
|
};
|
|
5734
5704
|
}
|
|
5735
|
-
const AUDIT_VIEWPORT = { width: 1040, height: 1248 };
|
|
5705
|
+
const AUDIT_VIEWPORT$1 = { width: 1040, height: 1248 };
|
|
5736
5706
|
const POST_LOAD_DELAY_MS = 2e3;
|
|
5737
5707
|
async function auditPerformance(siteUrl, urlPath = "/") {
|
|
5738
5708
|
const targetUrl = `${siteUrl.replace(/\/+$/, "")}${urlPath}`;
|
|
@@ -5740,7 +5710,7 @@ async function auditPerformance(siteUrl, urlPath = "/") {
|
|
|
5740
5710
|
try {
|
|
5741
5711
|
const browser2 = await getSharedBrowser();
|
|
5742
5712
|
const page = await browser2.newPage({
|
|
5743
|
-
viewport: AUDIT_VIEWPORT,
|
|
5713
|
+
viewport: AUDIT_VIEWPORT$1,
|
|
5744
5714
|
ignoreHTTPSErrors: true
|
|
5745
5715
|
});
|
|
5746
5716
|
try {
|
|
@@ -5773,6 +5743,297 @@ async function auditPerformance(siteUrl, urlPath = "/") {
|
|
|
5773
5743
|
};
|
|
5774
5744
|
}
|
|
5775
5745
|
}
|
|
5746
|
+
function collectSeoScript(origin) {
|
|
5747
|
+
function text2(el) {
|
|
5748
|
+
return el?.textContent?.trim() ?? "";
|
|
5749
|
+
}
|
|
5750
|
+
function attr(selector, name) {
|
|
5751
|
+
const el = document.querySelector(selector);
|
|
5752
|
+
return el ? el.getAttribute(name) : null;
|
|
5753
|
+
}
|
|
5754
|
+
const titleEl = document.querySelector("title");
|
|
5755
|
+
const titleText = text2(titleEl);
|
|
5756
|
+
const description = attr('meta[name="description" i]', "content");
|
|
5757
|
+
const canonical = attr('link[rel="canonical" i]', "href");
|
|
5758
|
+
const robots = attr('meta[name="robots" i]', "content");
|
|
5759
|
+
const viewport = attr('meta[name="viewport" i]', "content");
|
|
5760
|
+
const htmlLang = document.documentElement.getAttribute("lang");
|
|
5761
|
+
const charset = document.characterSet || attr("meta[charset]", "charset") || attr('meta[http-equiv="Content-Type" i]', "content");
|
|
5762
|
+
const openGraph = {};
|
|
5763
|
+
for (const meta of Array.from(
|
|
5764
|
+
document.querySelectorAll('meta[property^="og:" i]')
|
|
5765
|
+
)) {
|
|
5766
|
+
const property = meta.getAttribute("property");
|
|
5767
|
+
const content = meta.getAttribute("content");
|
|
5768
|
+
if (property && content) {
|
|
5769
|
+
openGraph[property.toLowerCase()] = content;
|
|
5770
|
+
}
|
|
5771
|
+
}
|
|
5772
|
+
const twitter = {};
|
|
5773
|
+
for (const meta of Array.from(
|
|
5774
|
+
document.querySelectorAll('meta[name^="twitter:" i]')
|
|
5775
|
+
)) {
|
|
5776
|
+
const name = meta.getAttribute("name");
|
|
5777
|
+
const content = meta.getAttribute("content");
|
|
5778
|
+
if (name && content) {
|
|
5779
|
+
twitter[name.toLowerCase()] = content;
|
|
5780
|
+
}
|
|
5781
|
+
}
|
|
5782
|
+
const headingCounts = { h1: 0, h2: 0, h3: 0, h4: 0, h5: 0, h6: 0 };
|
|
5783
|
+
const h1Texts = [];
|
|
5784
|
+
const seenLevels = [];
|
|
5785
|
+
const skippedLevels = [];
|
|
5786
|
+
for (const el of Array.from(
|
|
5787
|
+
document.querySelectorAll("h1, h2, h3, h4, h5, h6")
|
|
5788
|
+
)) {
|
|
5789
|
+
const level = Number(el.tagName.substring(1));
|
|
5790
|
+
const tag = el.tagName.toLowerCase();
|
|
5791
|
+
headingCounts[tag]++;
|
|
5792
|
+
if (level === 1) {
|
|
5793
|
+
h1Texts.push(text2(el).slice(0, 200));
|
|
5794
|
+
}
|
|
5795
|
+
const previous = seenLevels[seenLevels.length - 1];
|
|
5796
|
+
if (previous !== void 0 && level > previous + 1) {
|
|
5797
|
+
skippedLevels.push(
|
|
5798
|
+
`${tag} after h${previous} (skipped ${level - previous - 1} level(s))`
|
|
5799
|
+
);
|
|
5800
|
+
}
|
|
5801
|
+
seenLevels.push(level);
|
|
5802
|
+
}
|
|
5803
|
+
let imgTotal = 0;
|
|
5804
|
+
let imgWithAlt = 0;
|
|
5805
|
+
let imgWithoutAlt = 0;
|
|
5806
|
+
let imgEmptyAlt = 0;
|
|
5807
|
+
const missingAltUrls = [];
|
|
5808
|
+
for (const img of Array.from(document.querySelectorAll("img"))) {
|
|
5809
|
+
imgTotal++;
|
|
5810
|
+
const alt = img.getAttribute("alt");
|
|
5811
|
+
if (alt === null) {
|
|
5812
|
+
imgWithoutAlt++;
|
|
5813
|
+
if (missingAltUrls.length < 20) {
|
|
5814
|
+
missingAltUrls.push(img.currentSrc || img.src || "(no src)");
|
|
5815
|
+
}
|
|
5816
|
+
} else if (alt.trim() === "") {
|
|
5817
|
+
imgEmptyAlt++;
|
|
5818
|
+
} else {
|
|
5819
|
+
imgWithAlt++;
|
|
5820
|
+
}
|
|
5821
|
+
}
|
|
5822
|
+
let internal = 0;
|
|
5823
|
+
let external = 0;
|
|
5824
|
+
let nofollow = 0;
|
|
5825
|
+
let emptyText = 0;
|
|
5826
|
+
const emptyTextHrefs = [];
|
|
5827
|
+
for (const a of Array.from(document.querySelectorAll("a[href]"))) {
|
|
5828
|
+
const href = a.getAttribute("href") ?? "";
|
|
5829
|
+
if (!href || href.startsWith("#") || href.startsWith("mailto:") || href.startsWith("tel:")) {
|
|
5830
|
+
continue;
|
|
5831
|
+
}
|
|
5832
|
+
let isExternal = false;
|
|
5833
|
+
try {
|
|
5834
|
+
const url = new URL(href, origin);
|
|
5835
|
+
isExternal = url.origin !== origin;
|
|
5836
|
+
} catch {
|
|
5837
|
+
continue;
|
|
5838
|
+
}
|
|
5839
|
+
if (isExternal) {
|
|
5840
|
+
external++;
|
|
5841
|
+
} else {
|
|
5842
|
+
internal++;
|
|
5843
|
+
}
|
|
5844
|
+
const rel = (a.getAttribute("rel") ?? "").toLowerCase();
|
|
5845
|
+
if (rel.split(/\s+/).includes("nofollow")) {
|
|
5846
|
+
nofollow++;
|
|
5847
|
+
}
|
|
5848
|
+
const visibleText = (a.textContent ?? "").trim();
|
|
5849
|
+
const hasImage = a.querySelector('img[alt]:not([alt=""])');
|
|
5850
|
+
const ariaLabel = a.getAttribute("aria-label")?.trim();
|
|
5851
|
+
if (!visibleText && !hasImage && !ariaLabel) {
|
|
5852
|
+
emptyText++;
|
|
5853
|
+
if (emptyTextHrefs.length < 20) {
|
|
5854
|
+
emptyTextHrefs.push(href);
|
|
5855
|
+
}
|
|
5856
|
+
}
|
|
5857
|
+
}
|
|
5858
|
+
const jsonLdTypes = [];
|
|
5859
|
+
let jsonLdCount = 0;
|
|
5860
|
+
for (const script of Array.from(
|
|
5861
|
+
document.querySelectorAll('script[type="application/ld+json"]')
|
|
5862
|
+
)) {
|
|
5863
|
+
jsonLdCount++;
|
|
5864
|
+
try {
|
|
5865
|
+
const parsed = JSON.parse(script.textContent ?? "");
|
|
5866
|
+
const items = Array.isArray(parsed) ? parsed : [parsed];
|
|
5867
|
+
for (const item of items) {
|
|
5868
|
+
const graph = item?.["@graph"];
|
|
5869
|
+
const nodes = Array.isArray(graph) ? graph : [item];
|
|
5870
|
+
for (const node2 of nodes) {
|
|
5871
|
+
const t = node2?.["@type"];
|
|
5872
|
+
if (typeof t === "string") {
|
|
5873
|
+
jsonLdTypes.push(t);
|
|
5874
|
+
} else if (Array.isArray(t)) {
|
|
5875
|
+
for (const v of t) {
|
|
5876
|
+
if (typeof v === "string") {
|
|
5877
|
+
jsonLdTypes.push(v);
|
|
5878
|
+
}
|
|
5879
|
+
}
|
|
5880
|
+
}
|
|
5881
|
+
}
|
|
5882
|
+
}
|
|
5883
|
+
} catch {
|
|
5884
|
+
}
|
|
5885
|
+
}
|
|
5886
|
+
const wordCount = (document.body.innerText || "").trim().split(/\s+/).filter(Boolean).length;
|
|
5887
|
+
return {
|
|
5888
|
+
title: titleText || null,
|
|
5889
|
+
description,
|
|
5890
|
+
canonical,
|
|
5891
|
+
robots,
|
|
5892
|
+
viewport,
|
|
5893
|
+
htmlLang,
|
|
5894
|
+
charset,
|
|
5895
|
+
openGraph,
|
|
5896
|
+
twitter,
|
|
5897
|
+
headingCounts,
|
|
5898
|
+
h1Texts,
|
|
5899
|
+
skippedLevels,
|
|
5900
|
+
images: {
|
|
5901
|
+
total: imgTotal,
|
|
5902
|
+
withAlt: imgWithAlt,
|
|
5903
|
+
withoutAlt: imgWithoutAlt,
|
|
5904
|
+
emptyAlt: imgEmptyAlt,
|
|
5905
|
+
missingAltUrls
|
|
5906
|
+
},
|
|
5907
|
+
links: {
|
|
5908
|
+
totalInternal: internal,
|
|
5909
|
+
totalExternal: external,
|
|
5910
|
+
nofollow,
|
|
5911
|
+
emptyText,
|
|
5912
|
+
emptyTextHrefs
|
|
5913
|
+
},
|
|
5914
|
+
structuredData: { jsonLdCount, jsonLdTypes: Array.from(new Set(jsonLdTypes)) },
|
|
5915
|
+
wordCount
|
|
5916
|
+
};
|
|
5917
|
+
}
|
|
5918
|
+
async function checkResource(browser2, url) {
|
|
5919
|
+
const context = await browser2.newContext({ ignoreHTTPSErrors: true });
|
|
5920
|
+
try {
|
|
5921
|
+
const response2 = await context.request.get(url, {
|
|
5922
|
+
failOnStatusCode: false,
|
|
5923
|
+
timeout: 1e4
|
|
5924
|
+
});
|
|
5925
|
+
const ok = response2.status() >= 200 && response2.status() < 400;
|
|
5926
|
+
const body = ok ? await response2.text() : "";
|
|
5927
|
+
return { ok, body };
|
|
5928
|
+
} catch {
|
|
5929
|
+
return { ok: false, body: "" };
|
|
5930
|
+
} finally {
|
|
5931
|
+
await context.close();
|
|
5932
|
+
}
|
|
5933
|
+
}
|
|
5934
|
+
function robotsBlocksAll(robotsTxt) {
|
|
5935
|
+
const lines = robotsTxt.split(/\r?\n/);
|
|
5936
|
+
let inGlobalGroup = false;
|
|
5937
|
+
for (const line of lines) {
|
|
5938
|
+
const trimmed = line.split("#")[0].trim();
|
|
5939
|
+
if (!trimmed) {
|
|
5940
|
+
continue;
|
|
5941
|
+
}
|
|
5942
|
+
const [rawKey, ...rest] = trimmed.split(":");
|
|
5943
|
+
const key = rawKey.trim().toLowerCase();
|
|
5944
|
+
const value = rest.join(":").trim();
|
|
5945
|
+
if (key === "user-agent") {
|
|
5946
|
+
inGlobalGroup = value === "*";
|
|
5947
|
+
} else if (inGlobalGroup && key === "disallow" && value === "/") {
|
|
5948
|
+
return true;
|
|
5949
|
+
}
|
|
5950
|
+
}
|
|
5951
|
+
return false;
|
|
5952
|
+
}
|
|
5953
|
+
const AUDIT_VIEWPORT = { width: 1040, height: 1248 };
|
|
5954
|
+
async function auditSeo(siteUrl, urlPath = "/") {
|
|
5955
|
+
const origin = siteUrl.replace(/\/+$/, "");
|
|
5956
|
+
const targetUrl = `${origin}${urlPath}`;
|
|
5957
|
+
try {
|
|
5958
|
+
const browser2 = await getSharedBrowser();
|
|
5959
|
+
const page = await browser2.newPage({
|
|
5960
|
+
viewport: AUDIT_VIEWPORT,
|
|
5961
|
+
ignoreHTTPSErrors: true
|
|
5962
|
+
});
|
|
5963
|
+
try {
|
|
5964
|
+
const response2 = await page.goto(targetUrl, {
|
|
5965
|
+
waitUntil: "networkidle",
|
|
5966
|
+
timeout: 3e4
|
|
5967
|
+
});
|
|
5968
|
+
const httpStatus = response2?.status() ?? 0;
|
|
5969
|
+
const collected = await page.evaluate(collectSeoScript, origin);
|
|
5970
|
+
const [robotsResult, sitemapAtRoot, sitemapIndex] = await Promise.all([
|
|
5971
|
+
checkResource(browser2, `${origin}/robots.txt`),
|
|
5972
|
+
checkResource(browser2, `${origin}/sitemap.xml`),
|
|
5973
|
+
checkResource(browser2, `${origin}/sitemap_index.xml`)
|
|
5974
|
+
]);
|
|
5975
|
+
let sitemapUrl = null;
|
|
5976
|
+
if (sitemapAtRoot.ok) {
|
|
5977
|
+
sitemapUrl = `${origin}/sitemap.xml`;
|
|
5978
|
+
} else if (sitemapIndex.ok) {
|
|
5979
|
+
sitemapUrl = `${origin}/sitemap_index.xml`;
|
|
5980
|
+
} else if (robotsResult.ok) {
|
|
5981
|
+
const match2 = robotsResult.body.match(/^\s*Sitemap:\s*(\S+)/im);
|
|
5982
|
+
if (match2) {
|
|
5983
|
+
sitemapUrl = match2[1];
|
|
5984
|
+
}
|
|
5985
|
+
}
|
|
5986
|
+
const metrics = {
|
|
5987
|
+
url: targetUrl,
|
|
5988
|
+
httpStatus,
|
|
5989
|
+
wordCount: collected.wordCount,
|
|
5990
|
+
meta: {
|
|
5991
|
+
title: collected.title,
|
|
5992
|
+
titleLength: collected.title?.length ?? 0,
|
|
5993
|
+
description: collected.description,
|
|
5994
|
+
descriptionLength: collected.description?.length ?? 0,
|
|
5995
|
+
canonical: collected.canonical,
|
|
5996
|
+
robots: collected.robots,
|
|
5997
|
+
viewport: collected.viewport,
|
|
5998
|
+
htmlLang: collected.htmlLang,
|
|
5999
|
+
charset: collected.charset
|
|
6000
|
+
},
|
|
6001
|
+
social: {
|
|
6002
|
+
openGraph: collected.openGraph,
|
|
6003
|
+
twitter: collected.twitter
|
|
6004
|
+
},
|
|
6005
|
+
headings: {
|
|
6006
|
+
counts: collected.headingCounts,
|
|
6007
|
+
h1Texts: collected.h1Texts,
|
|
6008
|
+
skippedLevels: collected.skippedLevels
|
|
6009
|
+
},
|
|
6010
|
+
images: collected.images,
|
|
6011
|
+
links: collected.links,
|
|
6012
|
+
structuredData: collected.structuredData,
|
|
6013
|
+
resources: {
|
|
6014
|
+
robotsTxtFound: robotsResult.ok,
|
|
6015
|
+
robotsTxtBlocksAll: robotsResult.ok && robotsBlocksAll(robotsResult.body),
|
|
6016
|
+
sitemapFound: sitemapUrl !== null,
|
|
6017
|
+
sitemapUrl
|
|
6018
|
+
}
|
|
6019
|
+
};
|
|
6020
|
+
return {
|
|
6021
|
+
url: targetUrl,
|
|
6022
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6023
|
+
metrics
|
|
6024
|
+
};
|
|
6025
|
+
} finally {
|
|
6026
|
+
await page.close();
|
|
6027
|
+
}
|
|
6028
|
+
} catch (error2) {
|
|
6029
|
+
return {
|
|
6030
|
+
url: targetUrl,
|
|
6031
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6032
|
+
metrics: null,
|
|
6033
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
6034
|
+
};
|
|
6035
|
+
}
|
|
6036
|
+
}
|
|
5776
6037
|
var src$2 = { exports: {} };
|
|
5777
6038
|
var browser = { exports: {} };
|
|
5778
6039
|
var ms;
|
|
@@ -26236,6 +26497,30 @@ async function deleteAllSnapshotsForUserFromConfig(userId) {
|
|
|
26236
26497
|
await unlockCliConfig();
|
|
26237
26498
|
}
|
|
26238
26499
|
}
|
|
26500
|
+
async function pruneExpiredOrphanedSnapshots(userId, isExpired) {
|
|
26501
|
+
try {
|
|
26502
|
+
await lockCliConfig();
|
|
26503
|
+
const config = await readCliConfig();
|
|
26504
|
+
const siteIds = new Set(config.sites.map((s) => s.id));
|
|
26505
|
+
const filtered = config.snapshots.filter((snapshot) => {
|
|
26506
|
+
if (snapshot.userId !== userId) {
|
|
26507
|
+
return true;
|
|
26508
|
+
}
|
|
26509
|
+
if (siteIds.has(snapshot.localSiteId)) {
|
|
26510
|
+
return true;
|
|
26511
|
+
}
|
|
26512
|
+
return !isExpired(snapshot);
|
|
26513
|
+
});
|
|
26514
|
+
const pruned = config.snapshots.length - filtered.length;
|
|
26515
|
+
if (pruned > 0) {
|
|
26516
|
+
config.snapshots = filtered;
|
|
26517
|
+
await saveCliConfig(config);
|
|
26518
|
+
}
|
|
26519
|
+
return pruned;
|
|
26520
|
+
} finally {
|
|
26521
|
+
await unlockCliConfig();
|
|
26522
|
+
}
|
|
26523
|
+
}
|
|
26239
26524
|
async function setSnapshotInConfig(snapshotUrl, updates) {
|
|
26240
26525
|
try {
|
|
26241
26526
|
await lockCliConfig();
|
|
@@ -26473,7 +26758,31 @@ const _delete$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
|
|
|
26473
26758
|
registerCommand: registerCommand$b,
|
|
26474
26759
|
runCommand: runCommand$b
|
|
26475
26760
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
26476
|
-
|
|
26761
|
+
function buildSnapshotTable(snapshots) {
|
|
26762
|
+
const colWidths = getColumnWidths([0.4, 0.25, 0.175, 0.175]);
|
|
26763
|
+
const table = new CliTable3({
|
|
26764
|
+
head: [__("URL"), __("Site Name"), __("Updated"), __("Expires in")],
|
|
26765
|
+
wordWrap: true,
|
|
26766
|
+
wrapOnWordBoundary: false,
|
|
26767
|
+
colWidths,
|
|
26768
|
+
style: {
|
|
26769
|
+
head: [],
|
|
26770
|
+
border: []
|
|
26771
|
+
}
|
|
26772
|
+
});
|
|
26773
|
+
for (const snapshot of snapshots) {
|
|
26774
|
+
const durationUntilExpiry = formatDurationUntilExpiry(snapshot.date);
|
|
26775
|
+
const url = `https://${snapshot.url}`;
|
|
26776
|
+
table.push([
|
|
26777
|
+
{ href: url, content: url },
|
|
26778
|
+
snapshot.name,
|
|
26779
|
+
format(snapshot.date, "yyyy-MM-dd HH:mm"),
|
|
26780
|
+
durationUntilExpiry
|
|
26781
|
+
]);
|
|
26782
|
+
}
|
|
26783
|
+
return table.toString();
|
|
26784
|
+
}
|
|
26785
|
+
async function runCommand$a(siteFolder, outputFormat, all = false) {
|
|
26477
26786
|
const logger2 = new Logger();
|
|
26478
26787
|
try {
|
|
26479
26788
|
if (outputFormat === "json") {
|
|
@@ -26484,7 +26793,9 @@ async function runCommand$a(siteFolder, outputFormat) {
|
|
|
26484
26793
|
return;
|
|
26485
26794
|
}
|
|
26486
26795
|
logger2.reportStart(PreviewCommandLoggerAction.VALIDATE, __("Validating…"));
|
|
26487
|
-
|
|
26796
|
+
if (!all) {
|
|
26797
|
+
await getSiteByFolder(siteFolder);
|
|
26798
|
+
}
|
|
26488
26799
|
const token = await readAuthToken();
|
|
26489
26800
|
if (!token) {
|
|
26490
26801
|
throw new LoggerError(
|
|
@@ -26492,7 +26803,8 @@ async function runCommand$a(siteFolder, outputFormat) {
|
|
|
26492
26803
|
);
|
|
26493
26804
|
}
|
|
26494
26805
|
logger2.reportStart(PreviewCommandLoggerAction.LOAD, __("Loading preview sites…"));
|
|
26495
|
-
|
|
26806
|
+
await pruneExpiredOrphanedSnapshots(token.id, isSnapshotExpired);
|
|
26807
|
+
const snapshots = await getSnapshotsFromConfig(token.id, all ? void 0 : siteFolder);
|
|
26496
26808
|
if (snapshots.length === 0) {
|
|
26497
26809
|
logger2.reportSuccess(__("No preview sites found"));
|
|
26498
26810
|
return;
|
|
@@ -26512,30 +26824,41 @@ async function runCommand$a(siteFolder, outputFormat) {
|
|
|
26512
26824
|
} else {
|
|
26513
26825
|
logger2.reportSuccess(snapshotsMessage);
|
|
26514
26826
|
}
|
|
26515
|
-
if (
|
|
26516
|
-
const
|
|
26517
|
-
const
|
|
26518
|
-
|
|
26519
|
-
|
|
26520
|
-
wrapOnWordBoundary: false,
|
|
26521
|
-
colWidths,
|
|
26522
|
-
style: {
|
|
26523
|
-
head: [],
|
|
26524
|
-
border: []
|
|
26525
|
-
}
|
|
26526
|
-
});
|
|
26827
|
+
if (all) {
|
|
26828
|
+
const config = await readCliConfig();
|
|
26829
|
+
const siteById = new Map(config.sites.map((site) => [site.id, site]));
|
|
26830
|
+
const unknownSiteLabel = __("Unknown site");
|
|
26831
|
+
const snapshotsByLocalSiteId = /* @__PURE__ */ new Map();
|
|
26527
26832
|
for (const snapshot of snapshots) {
|
|
26528
|
-
const
|
|
26529
|
-
const
|
|
26530
|
-
|
|
26531
|
-
|
|
26532
|
-
|
|
26533
|
-
|
|
26534
|
-
|
|
26535
|
-
|
|
26536
|
-
|
|
26537
|
-
|
|
26833
|
+
const key = siteById.has(snapshot.localSiteId) ? snapshot.localSiteId : unknownSiteLabel;
|
|
26834
|
+
const bucket = snapshotsByLocalSiteId.get(key) ?? [];
|
|
26835
|
+
bucket.push(snapshot);
|
|
26836
|
+
snapshotsByLocalSiteId.set(key, bucket);
|
|
26837
|
+
}
|
|
26838
|
+
const sortedGroups = [...snapshotsByLocalSiteId.entries()].map(([key, siteSnapshots]) => {
|
|
26839
|
+
const site = siteById.get(key);
|
|
26840
|
+
const displayName = site ? `${site.name} — ${site.path}` : unknownSiteLabel;
|
|
26841
|
+
return { displayName, siteSnapshots };
|
|
26842
|
+
}).sort((a, b) => {
|
|
26843
|
+
if (b.siteSnapshots.length !== a.siteSnapshots.length) {
|
|
26844
|
+
return b.siteSnapshots.length - a.siteSnapshots.length;
|
|
26845
|
+
}
|
|
26846
|
+
return a.displayName.localeCompare(b.displayName);
|
|
26847
|
+
});
|
|
26848
|
+
const sections = sortedGroups.map(({ displayName, siteSnapshots }) => {
|
|
26849
|
+
const header = sprintf(
|
|
26850
|
+
/* translators: 1: Local site name (may include path for disambiguation). 2: Number of preview sites for that local site. */
|
|
26851
|
+
_n("%1$s (%2$d preview site)", "%1$s (%2$d preview sites)", siteSnapshots.length),
|
|
26852
|
+
displayName,
|
|
26853
|
+
siteSnapshots.length
|
|
26854
|
+
);
|
|
26855
|
+
return `${header}
|
|
26856
|
+
${buildSnapshotTable(siteSnapshots)}`;
|
|
26857
|
+
});
|
|
26858
|
+
console.log(sections.join("\n\n"));
|
|
26859
|
+
return;
|
|
26538
26860
|
}
|
|
26861
|
+
console.log(buildSnapshotTable(snapshots));
|
|
26539
26862
|
} catch (error2) {
|
|
26540
26863
|
if (error2 instanceof LoggerError) {
|
|
26541
26864
|
logger2.reportError(error2);
|
|
@@ -26555,10 +26878,14 @@ const registerCommand$a = (yargs2) => {
|
|
|
26555
26878
|
choices: ["table", "json"],
|
|
26556
26879
|
default: "table",
|
|
26557
26880
|
description: __("Output format")
|
|
26881
|
+
}).option("all", {
|
|
26882
|
+
type: "boolean",
|
|
26883
|
+
default: false,
|
|
26884
|
+
description: __("List preview sites for all local sites, grouped by site")
|
|
26558
26885
|
});
|
|
26559
26886
|
},
|
|
26560
26887
|
handler: async (argv) => {
|
|
26561
|
-
await runCommand$a(argv.path, argv.format);
|
|
26888
|
+
await runCommand$a(argv.path, argv.format, argv.all);
|
|
26562
26889
|
}
|
|
26563
26890
|
});
|
|
26564
26891
|
};
|
|
@@ -26665,21 +26992,6 @@ const update = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
|
|
|
26665
26992
|
registerCommand: registerCommand$9,
|
|
26666
26993
|
runCommand: runCommand$9
|
|
26667
26994
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
26668
|
-
const SYNC_POLL_INTERVAL_MS = 3e3;
|
|
26669
|
-
const SYNC_MAX_STALLED_ATTEMPTS = 200;
|
|
26670
|
-
const SYNC_PUSH_SIZE_LIMIT_GB = 5;
|
|
26671
|
-
const SYNC_PUSH_SIZE_LIMIT_BYTES = SYNC_PUSH_SIZE_LIMIT_GB * 1024 * 1024 * 1024;
|
|
26672
|
-
const SYNC_EXCLUSIONS = [
|
|
26673
|
-
"database",
|
|
26674
|
-
"db.php",
|
|
26675
|
-
"debug.log",
|
|
26676
|
-
"sqlite-database-integration",
|
|
26677
|
-
".DS_Store",
|
|
26678
|
-
"Thumbs.db",
|
|
26679
|
-
".git",
|
|
26680
|
-
"node_modules",
|
|
26681
|
-
"cache"
|
|
26682
|
-
];
|
|
26683
26995
|
const sitesEndpointSiteSchema = z.object({
|
|
26684
26996
|
ID: z.number(),
|
|
26685
26997
|
is_wpcom_atomic: z.boolean(),
|
|
@@ -27102,15 +27414,6 @@ async function fetchRemoteFileTree(token, remoteSiteId, rewindId, treePath = "wp
|
|
|
27102
27414
|
throw wrapError(__("Failed to fetch remote file tree"), error2);
|
|
27103
27415
|
}
|
|
27104
27416
|
}
|
|
27105
|
-
const shouldExcludeFromSync = (itemName) => {
|
|
27106
|
-
if (itemName.startsWith(".")) {
|
|
27107
|
-
return true;
|
|
27108
|
-
}
|
|
27109
|
-
if (SYNC_EXCLUSIONS.includes(itemName)) {
|
|
27110
|
-
return true;
|
|
27111
|
-
}
|
|
27112
|
-
return false;
|
|
27113
|
-
};
|
|
27114
27417
|
const shouldLimitDepth = (relativePath) => {
|
|
27115
27418
|
const normalizedPath = relativePath.replace(/^wp-content\//, "");
|
|
27116
27419
|
if (normalizedPath.match(/^plugins\/[^/]+\/?$/)) {
|
|
@@ -27136,17 +27439,27 @@ function categorizePath(relativePath) {
|
|
|
27136
27439
|
}
|
|
27137
27440
|
return "contents";
|
|
27138
27441
|
}
|
|
27139
|
-
|
|
27442
|
+
const shouldExcludeFromSync = (relativePath, deployIgnore) => {
|
|
27443
|
+
const itemName = path__default.basename(relativePath);
|
|
27444
|
+
if (itemName.startsWith(".")) {
|
|
27445
|
+
return true;
|
|
27446
|
+
}
|
|
27447
|
+
return deployIgnore.ignores(relativePath);
|
|
27448
|
+
};
|
|
27449
|
+
async function listLocalFileTree(sitePath, relativePath, maxDepth = 2, currentDepth = 0, deployIgnore) {
|
|
27450
|
+
if (!deployIgnore) {
|
|
27451
|
+
deployIgnore = await createDeployIgnoreFilter(sitePath, SYNC_IGNORE_DEFAULTS);
|
|
27452
|
+
}
|
|
27140
27453
|
const fullPath = path__default.join(sitePath, relativePath);
|
|
27141
27454
|
try {
|
|
27142
27455
|
const entries = await fs__default.promises.readdir(fullPath, { withFileTypes: true });
|
|
27143
27456
|
const result = [];
|
|
27144
27457
|
for (const entry of entries) {
|
|
27145
|
-
|
|
27458
|
+
const itemPath = path__default.join(relativePath, entry.name).replace(/\\/g, "/");
|
|
27459
|
+
if (shouldExcludeFromSync(itemPath, deployIgnore)) {
|
|
27146
27460
|
continue;
|
|
27147
27461
|
}
|
|
27148
27462
|
const isDirectory = entry.isDirectory();
|
|
27149
|
-
const itemPath = path__default.join(relativePath, entry.name).replace(/\\/g, "/");
|
|
27150
27463
|
const directoryEntry = {
|
|
27151
27464
|
name: entry.name,
|
|
27152
27465
|
isDirectory,
|
|
@@ -27159,7 +27472,8 @@ async function listLocalFileTree(sitePath, relativePath, maxDepth = 2, currentDe
|
|
|
27159
27472
|
sitePath,
|
|
27160
27473
|
itemPath,
|
|
27161
27474
|
maxDepth,
|
|
27162
|
-
currentDepth + 1
|
|
27475
|
+
currentDepth + 1,
|
|
27476
|
+
deployIgnore
|
|
27163
27477
|
);
|
|
27164
27478
|
} catch {
|
|
27165
27479
|
directoryEntry.children = [];
|
|
@@ -34069,13 +34383,15 @@ async function runCommand$7(siteFolder, syncOptions, remoteSiteIdentifier) {
|
|
|
34069
34383
|
wpContent: optionsToSync.includes("uploads") || optionsToSync.includes("plugins") || optionsToSync.includes("themes") || optionsToSync.includes("contents")
|
|
34070
34384
|
};
|
|
34071
34385
|
}
|
|
34386
|
+
const deployIgnore = await createDeployIgnoreFilter(site.path, SYNC_IGNORE_DEFAULTS);
|
|
34072
34387
|
const exporter = await getExporter({
|
|
34073
34388
|
site,
|
|
34074
34389
|
backupFile: archivePath,
|
|
34075
34390
|
includes,
|
|
34076
|
-
phpVersion:
|
|
34391
|
+
phpVersion: site.phpVersion,
|
|
34077
34392
|
splitDatabaseDumpByTable: true,
|
|
34078
|
-
specificSelectionPaths
|
|
34393
|
+
specificSelectionPaths,
|
|
34394
|
+
ignoreFilter: deployIgnore
|
|
34079
34395
|
});
|
|
34080
34396
|
if (!exporter) {
|
|
34081
34397
|
throw new LoggerError(__("No suitable exporter found for the provided backup file"));
|
|
@@ -48382,40 +48698,6 @@ function getInstallTypeLaunchStatGroups() {
|
|
|
48382
48698
|
};
|
|
48383
48699
|
}
|
|
48384
48700
|
}
|
|
48385
|
-
async function copySourceDirectoryIfNewerOrMissing({
|
|
48386
|
-
sourceDirectoryPath,
|
|
48387
|
-
targetDirectoryPath,
|
|
48388
|
-
readSourceVersion,
|
|
48389
|
-
readTargetVersion
|
|
48390
|
-
}) {
|
|
48391
|
-
if (!fs__default.existsSync(sourceDirectoryPath)) {
|
|
48392
|
-
return;
|
|
48393
|
-
}
|
|
48394
|
-
let sourceVersion;
|
|
48395
|
-
let shouldCopy = false;
|
|
48396
|
-
try {
|
|
48397
|
-
sourceVersion = await readSourceVersion();
|
|
48398
|
-
if (!sourceVersion) {
|
|
48399
|
-
return;
|
|
48400
|
-
}
|
|
48401
|
-
} catch {
|
|
48402
|
-
return;
|
|
48403
|
-
}
|
|
48404
|
-
try {
|
|
48405
|
-
const targetVersion = await readTargetVersion();
|
|
48406
|
-
const isSourceVersionNewer = targetVersion && semver.gt(sourceVersion, targetVersion);
|
|
48407
|
-
shouldCopy = Boolean(!targetVersion || isSourceVersionNewer);
|
|
48408
|
-
} catch {
|
|
48409
|
-
shouldCopy = true;
|
|
48410
|
-
}
|
|
48411
|
-
if (shouldCopy) {
|
|
48412
|
-
try {
|
|
48413
|
-
await fs__default.promises.rm(targetDirectoryPath, { recursive: true, force: true });
|
|
48414
|
-
} catch {
|
|
48415
|
-
}
|
|
48416
|
-
await recursiveCopyDirectory(sourceDirectoryPath, targetDirectoryPath);
|
|
48417
|
-
}
|
|
48418
|
-
}
|
|
48419
48701
|
async function copyBundledLatestWpVersion() {
|
|
48420
48702
|
const bundledWpVersionPath = path__default.join(getWpFilesPath(), "latest", "wordpress");
|
|
48421
48703
|
const bundledWpVersion = await getWordPressVersionFromInstallation(bundledWpVersionPath);
|
|
@@ -48436,110 +48718,6 @@ async function copyBundledLatestWpVersion() {
|
|
|
48436
48718
|
await recursiveCopyDirectory(bundledWpVersionPath, latestWpVersionPath);
|
|
48437
48719
|
}
|
|
48438
48720
|
}
|
|
48439
|
-
const SQLITE_FILENAME = "sqlite-database-integration";
|
|
48440
|
-
async function copyBundledSqlite() {
|
|
48441
|
-
const sourceSqlitePath = path__default.join(getWpFilesPath(), SQLITE_FILENAME);
|
|
48442
|
-
const targetSqlitePath = getSqlitePluginPath();
|
|
48443
|
-
await copySourceDirectoryIfNewerOrMissing({
|
|
48444
|
-
sourceDirectoryPath: sourceSqlitePath,
|
|
48445
|
-
targetDirectoryPath: targetSqlitePath,
|
|
48446
|
-
readSourceVersion: async () => semver.coerce(await getSqliteVersionFromInstallation(sourceSqlitePath), {
|
|
48447
|
-
includePrerelease: true
|
|
48448
|
-
}),
|
|
48449
|
-
readTargetVersion: async () => semver.coerce(await getSqliteVersionFromInstallation(targetSqlitePath), {
|
|
48450
|
-
includePrerelease: true
|
|
48451
|
-
})
|
|
48452
|
-
});
|
|
48453
|
-
}
|
|
48454
|
-
async function copyBundledWpCli() {
|
|
48455
|
-
const sourceWpCLIPath = path__default.join(getWpFilesPath(), "wp-cli", "wp-cli.phar");
|
|
48456
|
-
const sourceStats = await fs__default.promises.lstat(sourceWpCLIPath);
|
|
48457
|
-
let shouldCopy = false;
|
|
48458
|
-
try {
|
|
48459
|
-
const targetStats = await fs__default.promises.lstat(getWpCliPharPath());
|
|
48460
|
-
shouldCopy = sourceStats.size !== targetStats.size || Math.floor(sourceStats.mtimeMs) !== Math.floor(targetStats.mtimeMs);
|
|
48461
|
-
} catch {
|
|
48462
|
-
shouldCopy = true;
|
|
48463
|
-
}
|
|
48464
|
-
if (shouldCopy) {
|
|
48465
|
-
await fs__default.promises.cp(sourceWpCLIPath, getWpCliPharPath(), {
|
|
48466
|
-
mode: fs__default.constants.COPYFILE_FICLONE,
|
|
48467
|
-
preserveTimestamps: true
|
|
48468
|
-
});
|
|
48469
|
-
}
|
|
48470
|
-
}
|
|
48471
|
-
async function copyBundledSqliteCommand() {
|
|
48472
|
-
await copySourceDirectoryIfNewerOrMissing({
|
|
48473
|
-
sourceDirectoryPath: path__default.join(getWpFilesPath(), "sqlite-command"),
|
|
48474
|
-
targetDirectoryPath: getSqliteCommandPath(),
|
|
48475
|
-
readSourceVersion: async () => {
|
|
48476
|
-
const versionFilePath = path__default.join(getWpFilesPath(), "sqlite-command", "version");
|
|
48477
|
-
return semver.coerce(fs__default.readFileSync(versionFilePath, "utf8"));
|
|
48478
|
-
},
|
|
48479
|
-
readTargetVersion: async () => {
|
|
48480
|
-
const versionFilePath = path__default.join(getSqliteCommandPath(), "version");
|
|
48481
|
-
return semver.coerce(fs__default.readFileSync(versionFilePath, "utf8"));
|
|
48482
|
-
}
|
|
48483
|
-
});
|
|
48484
|
-
}
|
|
48485
|
-
async function copyBundledTranslations() {
|
|
48486
|
-
const sourceTranslationsPath = path__default.join(
|
|
48487
|
-
getWpFilesPath(),
|
|
48488
|
-
"latest",
|
|
48489
|
-
"available-site-translations.json"
|
|
48490
|
-
);
|
|
48491
|
-
const targetTranslationsPath = path__default.join(
|
|
48492
|
-
getWordPressVersionPath("latest"),
|
|
48493
|
-
"available-site-translations.json"
|
|
48494
|
-
);
|
|
48495
|
-
const sourceStats = await fs__default.promises.lstat(sourceTranslationsPath);
|
|
48496
|
-
let shouldCopy = false;
|
|
48497
|
-
try {
|
|
48498
|
-
const targetStats = await fs__default.promises.lstat(targetTranslationsPath);
|
|
48499
|
-
shouldCopy = sourceStats.size !== targetStats.size || Math.floor(sourceStats.mtimeMs) !== Math.floor(targetStats.mtimeMs);
|
|
48500
|
-
} catch {
|
|
48501
|
-
shouldCopy = true;
|
|
48502
|
-
}
|
|
48503
|
-
if (shouldCopy) {
|
|
48504
|
-
await fs__default.promises.cp(sourceTranslationsPath, targetTranslationsPath, {
|
|
48505
|
-
mode: fs__default.constants.COPYFILE_FICLONE,
|
|
48506
|
-
preserveTimestamps: true
|
|
48507
|
-
});
|
|
48508
|
-
}
|
|
48509
|
-
}
|
|
48510
|
-
async function copyBundledAiInstructions() {
|
|
48511
|
-
const sourceAiInstructionsPath = path__default.join(getWpFilesPath(), "skills");
|
|
48512
|
-
if (!fs__default.existsSync(sourceAiInstructionsPath)) {
|
|
48513
|
-
return;
|
|
48514
|
-
}
|
|
48515
|
-
const isSourceDirectoryDifferent = await areDirectoriesDifferentBySizeAndMtime(
|
|
48516
|
-
sourceAiInstructionsPath,
|
|
48517
|
-
getAiInstructionsPath()
|
|
48518
|
-
);
|
|
48519
|
-
if (isSourceDirectoryDifferent) {
|
|
48520
|
-
try {
|
|
48521
|
-
await fs__default.promises.rm(getAiInstructionsPath(), { recursive: true, force: true });
|
|
48522
|
-
} catch {
|
|
48523
|
-
}
|
|
48524
|
-
await recursiveCopyDirectory(sourceAiInstructionsPath, getAiInstructionsPath());
|
|
48525
|
-
}
|
|
48526
|
-
}
|
|
48527
|
-
async function copyBundledPhpMyAdmin() {
|
|
48528
|
-
await copySourceDirectoryIfNewerOrMissing({
|
|
48529
|
-
sourceDirectoryPath: path__default.join(getWpFilesPath(), "phpmyadmin"),
|
|
48530
|
-
targetDirectoryPath: getPhpMyAdminPath(),
|
|
48531
|
-
readSourceVersion: async () => {
|
|
48532
|
-
const composerFilePath = path__default.join(getWpFilesPath(), "phpmyadmin", "composer.json");
|
|
48533
|
-
const composerFile = JSON.parse(fs__default.readFileSync(composerFilePath, "utf8"));
|
|
48534
|
-
return semver.coerce(composerFile.version);
|
|
48535
|
-
},
|
|
48536
|
-
readTargetVersion: async () => {
|
|
48537
|
-
const composerFilePath = path__default.join(getPhpMyAdminPath(), "composer.json");
|
|
48538
|
-
const composerFile = JSON.parse(fs__default.readFileSync(composerFilePath, "utf8"));
|
|
48539
|
-
return semver.coerce(composerFile.version);
|
|
48540
|
-
}
|
|
48541
|
-
});
|
|
48542
|
-
}
|
|
48543
48721
|
async function copyBundledLanguagePacks() {
|
|
48544
48722
|
const sourceLanguagePacksPath = path__default.join(getWpFilesPath(), "latest", "languages");
|
|
48545
48723
|
if (!fs__default.existsSync(sourceLanguagePacksPath)) {
|
|
@@ -48561,13 +48739,7 @@ async function copyBundledLanguagePacks() {
|
|
|
48561
48739
|
async function setupServerFiles() {
|
|
48562
48740
|
const steps = [
|
|
48563
48741
|
["WordPress version", copyBundledLatestWpVersion],
|
|
48564
|
-
["
|
|
48565
|
-
["WP-CLI", copyBundledWpCli],
|
|
48566
|
-
["SQLite command", copyBundledSqliteCommand],
|
|
48567
|
-
["translations", copyBundledTranslations],
|
|
48568
|
-
["language packs", copyBundledLanguagePacks],
|
|
48569
|
-
["AI instructions", copyBundledAiInstructions],
|
|
48570
|
-
["phpMyAdmin", copyBundledPhpMyAdmin]
|
|
48742
|
+
["language packs", copyBundledLanguagePacks]
|
|
48571
48743
|
];
|
|
48572
48744
|
for (const [name, step] of steps) {
|
|
48573
48745
|
try {
|
|
@@ -74525,8 +74697,7 @@ const defaultTranslation = {
|
|
|
74525
74697
|
const SKIP_LOCALE_TAGS = ["formal", "informal"];
|
|
74526
74698
|
function getLatestVersionTranslations() {
|
|
74527
74699
|
const latestVersionTranslationsPath = path__default.join(
|
|
74528
|
-
|
|
74529
|
-
"wordpress-versions",
|
|
74700
|
+
getWpFilesPath(),
|
|
74530
74701
|
"latest",
|
|
74531
74702
|
"available-site-translations.json"
|
|
74532
74703
|
);
|
|
@@ -76704,6 +76875,33 @@ const auditPerformanceTool = tool(
|
|
|
76704
76875
|
}
|
|
76705
76876
|
}
|
|
76706
76877
|
);
|
|
76878
|
+
const auditSeoTool = tool(
|
|
76879
|
+
"rank_me_up",
|
|
76880
|
+
"Runs an on-page SEO audit on a WordPress site page. Returns title and meta description, canonical/robots/viewport tags, Open Graph and Twitter cards, heading structure, image alt-text coverage, internal/external link counts, JSON-LD structured data, and robots.txt/sitemap.xml availability. The site must be running. Use this to identify on-page SEO issues.",
|
|
76881
|
+
{
|
|
76882
|
+
nameOrPath: z$1.string().describe("The site name or file system path — the site must be running"),
|
|
76883
|
+
path: z$1.string().optional().describe('URL path to audit (e.g., "/", "/about"). Defaults to "/".')
|
|
76884
|
+
},
|
|
76885
|
+
async (args) => {
|
|
76886
|
+
try {
|
|
76887
|
+
const site = await resolveSite(args.nameOrPath);
|
|
76888
|
+
const siteUrl = getSiteUrl(site);
|
|
76889
|
+
const urlPath = args.path ?? "/";
|
|
76890
|
+
emitProgress(`Auditing SEO of ${siteUrl}${urlPath}…`);
|
|
76891
|
+
const result = await auditSeo(siteUrl, urlPath);
|
|
76892
|
+
if (result.error) {
|
|
76893
|
+
emitProgress(`Audit failed: ${result.error.slice(0, 80)}`);
|
|
76894
|
+
return errorResult(`SEO audit failed: ${result.error}`);
|
|
76895
|
+
}
|
|
76896
|
+
emitProgress(`SEO audit complete for ${urlPath}`);
|
|
76897
|
+
return textResult(JSON.stringify(result, null, 2));
|
|
76898
|
+
} catch (error2) {
|
|
76899
|
+
return errorResult(
|
|
76900
|
+
`SEO audit failed: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
76901
|
+
);
|
|
76902
|
+
}
|
|
76903
|
+
}
|
|
76904
|
+
);
|
|
76707
76905
|
const pushSiteTool = tool(
|
|
76708
76906
|
"site_push",
|
|
76709
76907
|
"Pushes a local WordPress site to a WordPress.com site. Requires WordPress.com authentication (studio auth login). Exports the local site, uploads it, and imports it on the remote site. This can take several minutes depending on site size.",
|
|
@@ -76840,6 +77038,7 @@ const studioToolDefinitions = [
|
|
|
76840
77038
|
takeScreenshotTool,
|
|
76841
77039
|
installTaxonomyScriptsTool,
|
|
76842
77040
|
auditPerformanceTool,
|
|
77041
|
+
auditSeoTool,
|
|
76843
77042
|
pushSiteTool,
|
|
76844
77043
|
pullSiteTool,
|
|
76845
77044
|
importSiteTool,
|
|
@@ -77045,7 +77244,7 @@ function formatUpdateBanner(currentVersion, latestVersion) {
|
|
|
77045
77244
|
});
|
|
77046
77245
|
return ["", top, ...paddedLines, bottom, ""].join("\n");
|
|
77047
77246
|
}
|
|
77048
|
-
const version = "1.7.
|
|
77247
|
+
const version = "1.7.11-beta1";
|
|
77049
77248
|
suppressPunycodeWarning();
|
|
77050
77249
|
async function main() {
|
|
77051
77250
|
await setupUpdateNotifier(version);
|
|
@@ -77076,9 +77275,9 @@ async function main() {
|
|
|
77076
77275
|
}
|
|
77077
77276
|
}).middleware(async () => {
|
|
77078
77277
|
const { runMigrations } = await import("./migration-B2m6ZEJz.mjs");
|
|
77079
|
-
const { migrations } = await import("./index-
|
|
77278
|
+
const { migrations } = await import("./index-Bej4fL6n.mjs");
|
|
77080
77279
|
await runMigrations(migrations);
|
|
77081
|
-
const { prunePmLogs } = await import("./prune-pm-logs-
|
|
77280
|
+
const { prunePmLogs } = await import("./prune-pm-logs-Dm_Bwi7l.mjs");
|
|
77082
77281
|
await prunePmLogs();
|
|
77083
77282
|
}).middleware(async (argv) => {
|
|
77084
77283
|
if (!argv.avoidTelemetry) {
|
|
@@ -77110,9 +77309,9 @@ async function main() {
|
|
|
77110
77309
|
{ registerCommand: registerAuthLogoutCommand },
|
|
77111
77310
|
{ registerCommand: registerAuthStatusCommand }
|
|
77112
77311
|
] = await Promise.all([
|
|
77113
|
-
import("./login-
|
|
77114
|
-
import("./logout-
|
|
77115
|
-
import("./status-
|
|
77312
|
+
import("./login-CDVrFvfk.mjs"),
|
|
77313
|
+
import("./logout-DLAXkbrZ.mjs"),
|
|
77314
|
+
import("./status-D6Huwi6x.mjs")
|
|
77116
77315
|
]);
|
|
77117
77316
|
registerAuthLoginCommand(authYargs);
|
|
77118
77317
|
registerAuthLogoutCommand(authYargs);
|
|
@@ -77120,7 +77319,7 @@ async function main() {
|
|
|
77120
77319
|
authYargs.version(false).demandCommand(1, __("You must provide a valid auth command"));
|
|
77121
77320
|
});
|
|
77122
77321
|
const studioCodeCommandBuilder = async (aiYargs) => {
|
|
77123
|
-
const { registerCommand: registerAiCommand } = await import("./index-
|
|
77322
|
+
const { registerCommand: registerAiCommand } = await import("./index-jpyzVGrl.mjs").then((n) => n.i);
|
|
77124
77323
|
registerAiCommand(aiYargs);
|
|
77125
77324
|
aiYargs.command("sessions", __("Manage code sessions"), async (sessionsYargs) => {
|
|
77126
77325
|
const [
|
|
@@ -77128,9 +77327,9 @@ async function main() {
|
|
|
77128
77327
|
{ registerCommand: registerAiSessionsListCommand },
|
|
77129
77328
|
{ registerCommand: registerAiSessionsResumeCommand }
|
|
77130
77329
|
] = await Promise.all([
|
|
77131
|
-
import("./delete-
|
|
77132
|
-
import("./list-
|
|
77133
|
-
import("./resume-
|
|
77330
|
+
import("./delete-C4R7DV_E.mjs"),
|
|
77331
|
+
import("./list-BRllR-8H.mjs"),
|
|
77332
|
+
import("./resume-B8M2e-Ii.mjs")
|
|
77134
77333
|
]);
|
|
77135
77334
|
sessionsYargs.option("path", {
|
|
77136
77335
|
hidden: true
|
|
@@ -77167,7 +77366,7 @@ async function main() {
|
|
|
77167
77366
|
Promise.resolve().then(() => list$2),
|
|
77168
77367
|
Promise.resolve().then(() => _delete$1),
|
|
77169
77368
|
Promise.resolve().then(() => update),
|
|
77170
|
-
import("./set-
|
|
77369
|
+
import("./set-CizYT2gE.mjs")
|
|
77171
77370
|
]);
|
|
77172
77371
|
registerPreviewCreateCommand(previewYargs);
|
|
77173
77372
|
registerPreviewListCommand(previewYargs);
|
|
@@ -77194,7 +77393,7 @@ async function main() {
|
|
|
77194
77393
|
Promise.resolve().then(() => start),
|
|
77195
77394
|
Promise.resolve().then(() => stop),
|
|
77196
77395
|
Promise.resolve().then(() => _delete),
|
|
77197
|
-
import("./set-
|
|
77396
|
+
import("./set-_5ABvjxt.mjs")
|
|
77198
77397
|
]);
|
|
77199
77398
|
registerSiteStatusCommand(sitesYargs);
|
|
77200
77399
|
registerSiteCreateCommand(sitesYargs);
|
|
@@ -77216,7 +77415,7 @@ async function main() {
|
|
|
77216
77415
|
});
|
|
77217
77416
|
},
|
|
77218
77417
|
handler: async (argv) => {
|
|
77219
|
-
const { commandHandler: wpCliCommandHandler } = await import("./wp-
|
|
77418
|
+
const { commandHandler: wpCliCommandHandler } = await import("./wp-BevvZcM1.mjs");
|
|
77220
77419
|
return wpCliCommandHandler(argv);
|
|
77221
77420
|
}
|
|
77222
77421
|
}).command({
|
|
@@ -77224,7 +77423,7 @@ async function main() {
|
|
|
77224
77423
|
describe: false,
|
|
77225
77424
|
// Hidden command
|
|
77226
77425
|
handler: async () => {
|
|
77227
|
-
const { commandHandler: eventsCommandHandler } = await import("./_events-
|
|
77426
|
+
const { commandHandler: eventsCommandHandler } = await import("./_events-D_POEBYs.mjs");
|
|
77228
77427
|
return eventsCommandHandler();
|
|
77229
77428
|
}
|
|
77230
77429
|
}).demandCommand(1, __("You must provide a valid command")).strict();
|