extension-develop 4.0.19 → 4.0.20

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.
Files changed (31) hide show
  1. package/dist/0~dev-server.mjs +8 -3
  2. package/dist/0~rspack-config.mjs +3759 -205
  3. package/dist/101.mjs +65 -2
  4. package/dist/349.mjs +2 -0
  5. package/dist/494.mjs +7 -1
  6. package/dist/839.mjs +164 -3619
  7. package/dist/887.mjs +5 -1
  8. package/dist/bridge-entry.d.ts +5 -4
  9. package/dist/bridge.mjs +25 -5
  10. package/dist/contract/codes.json +19 -2
  11. package/dist/contract/envelope.schema.json +10 -1
  12. package/dist/contract/golden.eval.target-not-found.json +14 -0
  13. package/dist/contract/golden.open.headed-window-required.json +14 -0
  14. package/dist/contracts-entry.d.ts +2 -0
  15. package/dist/contracts.mjs +12 -0
  16. package/dist/dev-server/control-bridge/consumer-client.d.ts +15 -1
  17. package/dist/dev-server/control-bridge/contracts.d.ts +6 -0
  18. package/dist/dev-server/control-bridge/producer-runtime.d.ts +1 -1
  19. package/dist/ensure-hmr-for-scripts.js +1 -1
  20. package/dist/ensure-hmr-for-scripts.mjs +1 -1
  21. package/dist/feature-scripts-content-script-wrapper.js +2 -2
  22. package/dist/feature-scripts-content-script-wrapper.mjs +2 -2
  23. package/dist/lib/extension-id.d.ts +9 -0
  24. package/dist/lib/messaging.d.ts +9 -0
  25. package/dist/lib/session-paths.d.ts +2 -0
  26. package/dist/plugin-playwright/index.d.ts +7 -0
  27. package/dist/plugin-web-extension/feature-icons/messages.d.ts +3 -0
  28. package/dist/plugin-web-extension/feature-manifest/manifest-lib/theme-values.d.ts +7 -0
  29. package/dist/plugin-web-extension/feature-manifest/messages.d.ts +1 -0
  30. package/dist/plugin-web-extension/feature-manifest/steps/validate-theme-values.d.ts +9 -0
  31. package/package.json +10 -1
package/dist/839.mjs CHANGED
@@ -4,24 +4,18 @@ import { execFileSync, spawn, spawnSync as external_node_child_process_spawnSync
4
4
  import { buildExecEnv, detectPackageManagerFromLockfile } from "prefers-yarn";
5
5
  import pintor from "pintor";
6
6
  import { EventEmitter } from "node:events";
7
- import { Compilation as core_Compilation, WebpackError as core_WebpackError, sources as core_sources } from "@rspack/core";
8
7
  import { isDebug, ENVELOPE, CODES, prefix as messaging_prefix, claimCardKey, card } from "./349.mjs";
9
- import { package_namespaceObject, buildWarningsDetails, sanitize, getDirs, projectInstallFallbackToNpm, assertNoManagedDependencyConflicts, isGeckoBasedBrowser, debugDirs, browserLaunchFailed, buildSuccess, extensionLoadRecovered, writingTypeDefinitionsError, extensionLoadStillRefused, projectInstallScriptsDisabled, authorInstallNotice, getSpecialFoldersDataForProjectRoot, buildSuccessWithWarnings, loadCommandConfig, needsInstall, buildCommandFailed, writingTypeDefinitions, buildWebpack, debugBrowser, normalizeBrowser, debugOutputPath, loadCustomConfig, devCommandFailed, getProjectStructure, getDistPath, resolveCompanionExtensionsConfig, loadBrowserConfig, isChromiumBasedBrowser } from "./101.mjs";
8
+ import { package_namespaceObject, buildWarningsDetails, sanitize, getDirs, projectInstallFallbackToNpm, assertNoManagedDependencyConflicts, debugDirs, browserLaunchFailed, buildSuccess, extensionLoadRecovered, writingTypeDefinitionsError, extensionLoadStillRefused, projectInstallScriptsDisabled, authorInstallNotice, getSpecialFoldersDataForProjectRoot, buildSuccessWithWarnings, loadCommandConfig, needsInstall, buildCommandFailed, writingTypeDefinitions, buildWebpack, normalizeBrowser, debugBrowser, debugOutputPath, loadCustomConfig, devCommandFailed, getProjectStructure, getDistPath, resolveCompanionExtensionsConfig, loadBrowserConfig } from "./101.mjs";
10
9
  import { stripBom, parseJsonSafe } from "./23.mjs";
11
10
  import { hasProjectDependency, findNearestProjectManifestDirSync } from "./80.mjs";
12
11
  import { buildSummaryPath, ensureSessionArtifactsIgnoreFile } from "./494.mjs";
13
- import { getCanonicalContentScriptEntryName, parseCanonicalContentScriptAsset, getCanonicalContentScriptJsAssetName, getCanonicalContentScriptCssAssetName } from "./291.mjs";
12
+ import { getCanonicalContentScriptEntryName } from "./291.mjs";
14
13
  import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
15
14
  import { dirname as __rspack_dirname } from "node:path";
16
15
  var __rspack_import_meta_dirname__ = __rspack_dirname(__rspack_fileURLToPath(import.meta.url));
17
16
  import * as __rspack_external_node_fs_5ea92f0c from "node:fs";
18
17
  import * as __rspack_external_node_path_c5b9b54f from "node:path";
19
18
  import * as __rspack_external_node_fs_promises_153e37e0 from "node:fs/promises";
20
- const RUST_MIN_STACK_BYTES = 268435456;
21
- function ensureRustMinStack(env = process.env) {
22
- if (!env.RUST_MIN_STACK) env.RUST_MIN_STACK = String(RUST_MIN_STACK_BYTES);
23
- }
24
- ensureRustMinStack();
25
19
  const MAX_OUTPUT_CHARS = 2000;
26
20
  const ANSI_PATTERN = /\u001b\[[0-9;]*m/g;
27
21
  const MACHINE_OUTPUT_VALUES = new Set([
@@ -1099,3560 +1093,182 @@ async function extensionBuild(pathOrRemoteUrl, buildOptions) {
1099
1093
  process.exit(1);
1100
1094
  }
1101
1095
  }
1102
- const BRIDGE_PRODUCER_SOURCE = `;(function () {
1103
- try {
1104
- var g = (typeof globalThis === "object" && globalThis) ? globalThis : this;
1105
- if (!g || g.__extjsBridgeProducerInstalled) return;
1106
-
1107
- var PORT = parseInt("__EXTJS_CONTROL_PORT__", 10);
1108
- var INSTANCE_ID = "__EXTJS_INSTANCE_ID__";
1109
- var CONTEXT = "__EXTJS_CONTEXT__";
1110
- // Connectable host of the dev-server control WS. Baked from the resolved
1111
- // connectable host (loopback locally; the public host for remote/devcontainer).
1112
- var HOST = "__EXTJS_CONTROL_HOST__";
1113
- if (!HOST || HOST.indexOf("__EXTJS") === 0) HOST = "127.0.0.1";
1114
- if (!PORT || PORT < 1) return;
1115
-
1116
- var WS = g.WebSocket;
1117
- if (typeof WS !== "function") return;
1118
-
1119
- var consoleRef = g.console || {};
1120
- g.__extjsBridgeProducerInstalled = true;
1121
-
1122
- // Capture extension event listeners at install time: these wraps run
1123
- // BEFORE user code, and no platform API exists to dispatch these events.
1124
- function captureEvent(event, sink) {
1125
- try {
1126
- if (!event || typeof event.addListener !== "function") return;
1127
- var origAdd = event.addListener.bind(event);
1128
- event.addListener = function (cb) {
1129
- if (typeof cb === "function" && sink.indexOf(cb) === -1) sink.push(cb);
1130
- return origAdd(cb);
1131
- };
1132
- if (typeof event.removeListener === "function") {
1133
- var origRemove = event.removeListener.bind(event);
1134
- event.removeListener = function (cb) {
1135
- var i = sink.indexOf(cb);
1136
- if (i !== -1) sink.splice(i, 1);
1137
- return origRemove(cb);
1138
- };
1139
- }
1140
- } catch (e) { /* non-fatal: trigger falls back to its no-listener reply */ }
1141
- }
1142
-
1143
- // Use g.chrome (not the hoisted chrome var below). This runs first.
1144
- var actionClickedListeners = [];
1145
- var commandListeners = [];
1146
- if (g.chrome) {
1147
- captureEvent(g.chrome.action && g.chrome.action.onClicked, actionClickedListeners);
1148
- captureEvent(g.chrome.commands && g.chrome.commands.onCommand, commandListeners);
1149
- }
1150
-
1151
- var LEVELS = ["log", "info", "warn", "error", "debug", "trace"];
1152
- var socket = null;
1153
- var open = false;
1154
- var queue = [];
1155
- var backoff = 250;
1156
- var MAX_BACKOFF = 5000;
1157
- var MAX_QUEUE = 1000;
1158
- var MAX_RESULT_BYTES = 256 * 1024;
1159
-
1160
- function nowId() {
1161
- return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
1162
- }
1163
-
1164
- function engineName() {
1165
- try {
1166
- return (typeof navigator !== "undefined" && /firefox/i.test(navigator.userAgent)) ? "firefox" : "chromium";
1167
- } catch (e) { return "chromium"; }
1168
- }
1169
-
1170
- function safeValue(v) {
1171
- try {
1172
- var s = JSON.stringify(v);
1173
- if (s === undefined) return {value: undefined, truncated: false};
1174
- if (s.length > MAX_RESULT_BYTES) {
1175
- return {value: {__type: "truncated", preview: s.slice(0, 1024)}, truncated: true};
1176
- }
1177
- return {value: JSON.parse(s), truncated: false};
1178
- } catch (e) {
1179
- return {value: {__type: "unserializable", preview: String(v).slice(0, 512)}, truncated: false};
1180
- }
1181
- }
1182
-
1183
- function replyOk(cmdId, value) {
1184
- var s = safeValue(value);
1185
- var frame = {type: "result", cmdId: cmdId, ok: true, value: s.value};
1186
- if (s.truncated) frame.truncated = true;
1187
- try { socket && socket.send(JSON.stringify(frame)); } catch (e) {
1188
- // Ignore
1189
- }
1190
- }
1191
-
1192
- function replyErr(cmdId, name, message) {
1193
- var frame = {type: "result", cmdId: cmdId, ok: false, error: {name: name, message: String(message), engine: engineName()}};
1194
- try { socket && socket.send(JSON.stringify(frame)); } catch (e) {
1195
- // Ignore
1196
- }
1197
- }
1198
-
1199
- // Resolve a numeric tab id when the caller did not pass one: match
1200
- // target.url against open tabs, else default to the active tab. (#51)
1201
- function resolveTargetTab(target, cb) {
1202
- var chrome = g.chrome;
1203
- if (!chrome || !chrome.tabs) { cb(null, "chrome.tabs is not available"); return; }
1204
- var url = target && target.url;
1205
- if (url) {
1206
- var substringMatch = function () {
1207
- try {
1208
- chrome.tabs.query({}, function (all) {
1209
- var hit = (all || []).filter(function (t) { return t.url && String(t.url).indexOf(url) !== -1; })[0];
1210
- if (hit && typeof hit.id === "number") { cb(hit.id, null); return; }
1211
- cb(null, "no open tab matches url: " + url);
1212
- });
1213
- } catch (e2) { cb(null, "tab query failed"); }
1214
- };
1215
- try {
1216
- // chrome.tabs.query accepts URL match patterns; a bare url/glob may
1217
- // not be a valid pattern, so fall back to a substring scan.
1218
- chrome.tabs.query({url: url}, function (tabs) {
1219
- if (chrome.runtime && chrome.runtime.lastError) { substringMatch(); return; }
1220
- if (tabs && tabs.length && typeof tabs[0].id === "number") { cb(tabs[0].id, null); return; }
1221
- substringMatch();
1222
- });
1223
- } catch (e) { substringMatch(); }
1224
- return;
1225
- }
1226
- try {
1227
- chrome.tabs.query({active: true, lastFocusedWindow: true}, function (tabs) {
1228
- var t = tabs && tabs[0];
1229
- if (t && typeof t.id === "number") { cb(t.id, null); return; }
1230
- cb(null, "no active tab to target");
1231
- });
1232
- } catch (e3) { cb(null, "tab query failed"); }
1233
- }
1234
-
1235
- // chrome.* async APIs are callback-only on Gecko. Prefer promisified
1236
- // browser.* when present, else the chrome.* callback form. cb(err, result).
1237
- function nsCall(nsName, method, callArgs, cb) {
1238
- var chromeG = g.chrome;
1239
- var done = false;
1240
- var once = function (err, r) { if (!done) { done = true; cb(err, r); } };
1241
- var viaPromise = function (ns) {
1242
- try {
1243
- var ret = ns[method].apply(ns, callArgs);
1244
- if (ret && typeof ret.then === "function") {
1245
- ret.then(function (r) { once(null, r); }, function (e) { once(e); });
1246
- return true;
1247
- }
1248
- } catch (e) { once(e); return true; }
1249
- return false;
1250
- };
1251
- var bNS = g.browser && g.browser[nsName];
1252
- if (bNS && typeof bNS[method] === "function" && viaPromise(bNS)) return;
1253
- var cNS = chromeG && chromeG[nsName];
1254
- if (!cNS || typeof cNS[method] !== "function") {
1255
- once(new Error("chrome." + nsName + "." + method + " is not available (engine: " + engineName() + ")"));
1256
- return;
1257
- }
1258
- try {
1259
- var ret2 = cNS[method].apply(cNS, callArgs.concat([function (r) {
1260
- var le = chromeG.runtime && chromeG.runtime.lastError;
1261
- if (le) once(le); else once(null, r);
1262
- }]));
1263
- // A promise-only implementation ignores the trailing callback and
1264
- // returns a thenable instead; resolve through it (once() dedupes).
1265
- if (ret2 && typeof ret2.then === "function") {
1266
- ret2.then(function (r) { once(null, r); }, function (e) { once(e); });
1267
- }
1268
- } catch (e2) {
1269
- if (!viaPromise(cNS)) once(e2);
1270
- }
1271
- }
1272
-
1273
- function executeCommand(cmd) {
1274
- var op = cmd.op, target = cmd.target || {}, args = cmd.args || {};
1275
- var ctx = target.context || "background";
1276
- var chrome = g.chrome;
1277
- var cmdId = cmd.cmdId;
1278
- // content/page eval & inspect need a numeric tab id: resolve --url (or
1279
- // nothing) to a tab and re-dispatch with target.tabId filled in. (#51)
1280
- if ((op === "eval" || op === "inspect") && (ctx === "content" || ctx === "page") && target.tabId == null) {
1281
- resolveTargetTab(target, function (tabId, err) {
1282
- if (tabId == null) {
1283
- replyErr(cmdId, "Unsupported", err || ("eval/inspect in context " + ctx + " needs a --tab id, a --url to match, or an active tab"));
1096
+ function viaBroker(broker, instruction) {
1097
+ return broker.broadcastReload({
1098
+ type: instruction.type,
1099
+ changedContentScriptEntries: instruction.changedContentScriptEntries,
1100
+ label: instruction.label,
1101
+ changedFiles: instruction.changedAssets
1102
+ });
1103
+ }
1104
+ function formatReloadingLine(label) {
1105
+ return `Reloading ${pintor.brightBlue(label)}…`;
1106
+ }
1107
+ async function dispatchReload(instruction, executor) {
1108
+ if (!instruction) return;
1109
+ if ('true' === process.env.EXTENSION_NO_RELOAD) return;
1110
+ if (executor.broker) {
1111
+ const notified = viaBroker(executor.broker, instruction);
1112
+ if (notified > 0) {
1113
+ if (instruction.label) console.log(formatReloadingLine(instruction.label));
1284
1114
  return;
1285
- }
1286
- var next = {};
1287
- for (var k in cmd) { if (Object.prototype.hasOwnProperty.call(cmd, k)) next[k] = cmd[k]; }
1288
- next.target = {};
1289
- for (var tk in target) { if (Object.prototype.hasOwnProperty.call(target, tk)) next.target[tk] = target[tk]; }
1290
- next.target.tabId = tabId;
1291
- executeCommand(next);
1292
- });
1293
- return;
1294
- }
1295
- try {
1296
- if (!chrome) { replyErr(cmdId, "Unsupported", "chrome.* not available in this context"); return; }
1297
- if (op === "eval") {
1298
- if (ctx === "background") {
1299
- Promise.resolve().then(function () { return (0, eval)(args.expression); })
1300
- .then(function (r) { replyOk(cmdId, r); }, function (e) {
1301
- var msg = (e && e.message) || String(e);
1302
- // MV3 forbids eval of strings in the SW/extension pages and
1303
- // rejects 'unsafe-eval'. Surface it honestly with an alternative.
1304
- if (/Content Security Policy|unsafe-eval/i.test(msg)) {
1305
- replyErr(cmdId, "Unsupported", "eval is blocked in the MV3 service worker by CSP. Use --context page --tab <id> (eval runs in the page's MAIN world), or run on an MV2/Firefox build. Engine: " + engineName());
1306
- } else {
1307
- replyErr(cmdId, (e && e.name) || "EvalError", msg);
1308
- }
1309
- });
1310
- } else if ((ctx === "content" || ctx === "page") && target.tabId && chrome.scripting) {
1311
- // Wrap the eval so a throw INSIDE the tab comes back as data:
1312
- // Chrome swallows an injected function's exception (null result).
1313
- nsCall("scripting", "executeScript", [{
1314
- target: {tabId: target.tabId},
1315
- world: ctx === "page" ? "MAIN" : "ISOLATED",
1316
- func: function (src) {
1317
- try { return {__extjsEval: 1, ok: true, value: eval(src)}; }
1318
- catch (e) { return {__extjsEval: 1, ok: false, name: (e && e.name) || "EvalError", message: (e && e.message) || String(e)}; }
1319
- },
1320
- args: [String(args.expression)]
1321
- }], function (err, res) {
1322
- if (err) { replyErr(cmdId, "EvalError", (err && err.message) || err); return; }
1323
- var frame = res && res[0] ? res[0].result : undefined;
1324
- if (!frame || frame.__extjsEval !== 1) {
1325
- replyErr(cmdId, "EvalError", "the expression never executed in tab " + target.tabId + ": no injectable frame returned a result (restricted page, or outside host_permissions)");
1326
- return;
1327
- }
1328
- if (frame.ok) { replyOk(cmdId, frame.value); return; }
1329
- if (ctx === "content" && /Content Security Policy|unsafe-eval|EvalError/i.test(String(frame.name) + " " + String(frame.message))) {
1330
- replyErr(cmdId, "Unsupported", "eval of a string is blocked in the ISOLATED (content) world by the extension CSP. Use --context page (runs in the page's MAIN world). Original: " + frame.message);
1331
- return;
1332
- }
1333
- replyErr(cmdId, frame.name || "EvalError", frame.message);
1334
- });
1335
- } else if (ctx === "popup" || ctx === "options" || ctx === "sidebar" || ctx === "devtools" || ctx === "newtab" || ctx === "history" || ctx === "bookmarks") {
1336
- // The SW can't eval in another extension page; ask the surface's
1337
- // own in-bundle relay (only the open, matching-context page responds).
1338
- chrome.runtime.sendMessage(
1339
- {__extjsEvalRequest: true, target: target, args: {expression: String(args.expression)}},
1340
- function (resp) {
1341
- if ((chrome.runtime && chrome.runtime.lastError) || !resp) {
1342
- replyErr(cmdId, "Unsupported", "surface '" + ctx + "' is not open (open it first: extension open " + ctx + ")");
1343
- } else if (resp.ok) {
1344
- replyOk(cmdId, resp.value);
1345
- } else {
1346
- replyErr(cmdId, (resp.error && resp.error.name) || "EvalError", (resp.error && resp.error.message) || "eval failed");
1347
- }
1348
- }
1349
- );
1350
- } else if (ctx === "content" || ctx === "page") {
1351
- replyErr(cmdId, "Unsupported", target.tabId
1352
- ? "chrome.scripting is not available on this engine (MV2 has no scripting API); use --context background"
1353
- : "eval in context " + ctx + " requires a tabId");
1354
- } else {
1355
- replyErr(cmdId, "BadRequest", "unknown eval context: " + ctx);
1356
- }
1357
- return;
1358
1115
  }
1359
- if (op === "storage.get" || op === "storage.set") {
1360
- // chrome.* storage is callback-only on Gecko and older Chromium.
1361
- // Prefer promisified browser.*, then fall back to the callback shape.
1362
- var storageNS = (g.browser && g.browser.storage) ? g.browser.storage : chrome.storage;
1363
- var storageArea = storageNS && storageNS[args.area || "local"];
1364
- if (!storageArea) { replyErr(cmdId, "StorageError", "storage." + (args.area || "local") + " unavailable"); return; }
1365
- var isSet = op === "storage.set";
1366
- var callArgs = isSet ? [args.items || {}] : [args.key != null ? args.key : null];
1367
- var onStorageOk = function (r) { replyOk(cmdId, isSet ? {set: Object.keys(args.items || {})} : r); };
1368
- var onStorageErr = function (e) { replyErr(cmdId, "StorageError", (e && e.message) || String(e)); };
1369
- var storageFn = isSet ? storageArea.set : storageArea.get;
1370
- try {
1371
- var storageRet = storageFn.apply(storageArea, callArgs);
1372
- if (storageRet && typeof storageRet.then === "function") {
1373
- storageRet.then(onStorageOk, onStorageErr);
1374
- return;
1116
+ const warning = executor.broker.undeliveredReloadWarning?.();
1117
+ if (warning) console.warn(warning);
1118
+ }
1119
+ }
1120
+ function createChangedSourcesTracker(compiler) {
1121
+ let forcedFull = false;
1122
+ let changedSources = [];
1123
+ compiler.hooks.watchRun.tap('extjs-reload-changed-sources', ()=>{
1124
+ forcedFull = false;
1125
+ changedSources = [];
1126
+ const modifiedFiles = compiler.modifiedFiles;
1127
+ if (!modifiedFiles || 0 === modifiedFiles.size) return;
1128
+ const contextDir = compiler.options.context || '';
1129
+ for (const file of modifiedFiles){
1130
+ const normalized = __rspack_external_node_path_c5b9b54f.relative(contextDir, file).replace(/\\/g, '/');
1131
+ if (normalized) {
1132
+ changedSources.push(normalized);
1133
+ if (normalized.includes('manifest.json') || normalized.includes('_locales/')) forcedFull = true;
1375
1134
  }
1376
- } catch (e) { /* not thenable / threw synchronously: fall back to callback */ }
1377
- try {
1378
- storageFn.apply(storageArea, callArgs.concat([function (r) {
1379
- var le = chrome.runtime && chrome.runtime.lastError;
1380
- if (le) onStorageErr(le); else onStorageOk(r);
1381
- }]));
1382
- } catch (e) { onStorageErr(e); }
1383
- return;
1384
- }
1385
- if (op === "reload") {
1386
- if (ctx === "background") {
1387
- replyOk(cmdId, {reloading: true});
1388
- setTimeout(function () { try { chrome.runtime.reload(); } catch (e) {
1389
- // Ignore
1390
- } }, 50);
1391
- } else if (target.tabId) {
1392
- nsCall("tabs", "reload", [target.tabId], function (err) {
1393
- if (err) replyErr(cmdId, "ReloadError", (err && err.message) || err);
1394
- else replyOk(cmdId, {reloaded: target.tabId});
1395
- });
1396
- } else {
1397
- replyErr(cmdId, "Unsupported", "reload needs background or a tabId");
1398
- }
1399
- return;
1400
1135
  }
1401
- if (op === "open") {
1402
- var surface = args.surface || ctx;
1403
- // getPopup is promise-style on Gecko and (MV3) Chromium; fall back to
1404
- // callback-style for older Chromium. Always resolves to a string.
1405
- var getActionPopup = function (cb) {
1406
- try {
1407
- var r = chrome.action && chrome.action.getPopup && chrome.action.getPopup({});
1408
- if (r && typeof r.then === "function") {
1409
- r.then(function (p) { cb(p || ""); }, function () { cb(""); });
1410
- return;
1411
- }
1412
- } catch (e) {
1413
- // Ignore
1414
- }
1415
- try { chrome.action.getPopup({}, function (p) { cb(p || ""); }); }
1416
- catch (e) { cb(""); }
1417
- };
1418
- var resolveActiveTab = function (a, cb) {
1419
- if (a && typeof a.tabId === "number") {
1420
- try { chrome.tabs.get(a.tabId, function (t) { cb(t || {id: a.tabId}); }); return; }
1421
- catch (e) {
1422
- // Ignore
1423
- }
1424
- }
1425
- try { chrome.tabs.query({active: true, lastFocusedWindow: true}, function (tabs) { cb((tabs && tabs[0]) || undefined); }); }
1426
- catch (e) { cb(undefined); }
1427
- };
1428
- // Replaying a listener carries no user gesture, so activeTab is never
1429
- // granted. Warn when the manifest declares it.
1430
- var activeTabWarning = function () {
1136
+ });
1137
+ return {
1138
+ snapshot: ()=>({
1139
+ forcedFull,
1140
+ changedSources
1141
+ })
1142
+ };
1143
+ }
1144
+ function formatReloadContextLabel(context, files) {
1145
+ if (!files.length) return context;
1146
+ const shown = files.slice(0, 2).join(', ');
1147
+ const extra = files.length > 2 ? ` +${files.length - 2} more` : '';
1148
+ return `${context} (${shown}${extra})`;
1149
+ }
1150
+ function pageContextFromSources(changedSources) {
1151
+ const rules = [
1152
+ [
1153
+ /(^|\/)(sidebar|side[-_]?panel)(\.|\/)/i,
1154
+ 'sidebar page'
1155
+ ],
1156
+ [
1157
+ /(^|\/)(action|popup)(\.|\/)/i,
1158
+ 'popup page'
1159
+ ],
1160
+ [
1161
+ /(^|\/)options(\.|\/)/i,
1162
+ 'options page'
1163
+ ],
1164
+ [
1165
+ /(^|\/)devtools(\.|\/)/i,
1166
+ 'devtools page'
1167
+ ],
1168
+ [
1169
+ /(^|\/)(newtab|new[-_]?tab)(\.|\/)/i,
1170
+ 'new tab page'
1171
+ ],
1172
+ [
1173
+ /(^|\/)(history|bookmarks)(\.|\/)/i,
1174
+ 'page'
1175
+ ]
1176
+ ];
1177
+ for (const rel of changedSources)for (const [re, name] of rules)if (re.test(rel)) return name;
1178
+ return 'page';
1179
+ }
1180
+ function moduleResourcesFromIdentifier(identifier, contextDir) {
1181
+ const afterLoaders = identifier.slice(identifier.lastIndexOf('!') + 1);
1182
+ const noLayer = afterLoaders.split('|')[0];
1183
+ const queryIndex = noLayer.indexOf('?');
1184
+ const resourcePath = -1 === queryIndex ? noLayer : noLayer.slice(0, queryIndex);
1185
+ const query = -1 === queryIndex ? '' : noLayer.slice(queryIndex);
1186
+ const out = [];
1187
+ const push = (absolute)=>{
1188
+ if (!absolute || !__rspack_external_node_path_c5b9b54f.isAbsolute(absolute)) return;
1189
+ out.push(__rspack_external_node_path_c5b9b54f.relative(contextDir, absolute).replace(/\\/g, '/'));
1190
+ };
1191
+ push(resourcePath);
1192
+ const concatMatch = query.match(/[?&]__extensionjs_classic_concat__=([^&]+)/);
1193
+ if (concatMatch) try {
1194
+ const data = JSON.parse(decodeURIComponent(concatMatch[1]));
1195
+ for (const file of data?.js || [])push(String(file));
1196
+ } catch {}
1197
+ return out;
1198
+ }
1199
+ function buildSourceFeatureIndex(compilation, contextDir) {
1200
+ const index = {
1201
+ swSources: new Set(),
1202
+ contentEntriesBySource: new Map(),
1203
+ pageSources: new Set()
1204
+ };
1205
+ const chunkGraph = compilation.chunkGraph;
1206
+ for (const chunk of compilation.chunks || []){
1207
+ const name = String(chunk?.name || '');
1208
+ if (!name) continue;
1209
+ const isBackground = /^background\//.test(name);
1210
+ const isContent = /^content_scripts\//.test(name);
1211
+ for (const module of chunkGraph.getChunkModulesIterable(chunk)){
1212
+ let identifier = '';
1431
1213
  try {
1432
- var m = chrome.runtime.getManifest();
1433
- var perms = (m && m.permissions) || [];
1434
- if (perms.indexOf("activeTab") !== -1) {
1435
- return "replayed without a user gesture: activeTab is NOT granted, so APIs that depend on it (scripting on the active tab, captureVisibleTab) behave differently than a real click";
1436
- }
1437
- } catch (e) {
1438
- // Ignore
1439
- }
1440
- return null;
1441
- };
1442
- if (surface === "popup") {
1443
- if (chrome.action && chrome.action.openPopup) {
1444
- nsCall("action", "openPopup", [], function (err) {
1445
- if (err) replyErr(cmdId, "Unsupported", "openPopup: " + ((err && err.message) || err));
1446
- else replyOk(cmdId, {opened: "popup"});
1447
- });
1448
- } else { replyErr(cmdId, "Unsupported", "action.openPopup not available"); }
1449
- } else if (surface === "options") {
1450
- try { chrome.runtime.openOptionsPage(function () { replyOk(cmdId, {opened: "options"}); }); }
1451
- catch (e) { replyErr(cmdId, "Unsupported", "openOptionsPage: " + e); }
1452
- } else if (surface === "sidebar") {
1453
- if (chrome.sidePanel && chrome.sidePanel.open && chrome.windows) {
1454
- chrome.windows.getCurrent(function (w) {
1455
- nsCall("sidePanel", "open", [{windowId: w.id}], function (err) {
1456
- if (err) replyErr(cmdId, "Unsupported", "sidePanel.open: " + ((err && err.message) || err));
1457
- else replyOk(cmdId, {opened: "sidebar"});
1458
- });
1459
- });
1460
- } else { replyErr(cmdId, "Unsupported", "sidePanel not available (engine: " + engineName() + ")"); }
1461
- } else if (surface === "action") {
1462
- // With a default_popup the click opens it; without one, replay the
1463
- // onClicked listeners captured at install time.
1464
- if (chrome.action) {
1465
- getActionPopup(function (popup) {
1466
- if (popup) {
1467
- nsCall("action", "openPopup", [], function (err) {
1468
- if (err) replyErr(cmdId, "Unsupported", "openPopup: " + ((err && err.message) || err));
1469
- else replyOk(cmdId, {triggered: "popup"});
1470
- });
1471
- } else if (actionClickedListeners.length) {
1472
- resolveActiveTab(args, function (tab) {
1473
- var fired = 0;
1474
- for (var i = 0; i < actionClickedListeners.length; i++) {
1475
- try { actionClickedListeners[i](tab); fired++; } catch (e) {
1476
- // Ignore
1477
- }
1478
- }
1479
- var reply = {triggered: "onClicked", listeners: fired, gesture: false};
1480
- var warning = activeTabWarning();
1481
- if (warning) reply.warning = warning;
1482
- replyOk(cmdId, reply);
1483
- });
1484
- } else {
1485
- replyErr(cmdId, "Unsupported", "action has no popup and no onClicked listener registered");
1486
- }
1487
- });
1488
- } else { replyErr(cmdId, "Unsupported", "chrome.action not available (engine: " + engineName() + ")"); }
1489
- } else if (surface === "command") {
1490
- var commandName = (args && args.name) || undefined;
1491
- if (!commandListeners.length) {
1492
- replyErr(cmdId, "Unsupported", "no chrome.commands.onCommand listener registered");
1493
- } else {
1494
- resolveActiveTab(args, function (tab) {
1495
- var fired = 0;
1496
- for (var i = 0; i < commandListeners.length; i++) {
1497
- try { commandListeners[i](commandName, tab); fired++; } catch (e) {
1498
- // Ignore
1499
- }
1500
- }
1501
- replyOk(cmdId, {triggered: "command", command: commandName || null, listeners: fired, gesture: false});
1502
- });
1214
+ identifier = String(module.identifier());
1215
+ } catch {
1216
+ continue;
1503
1217
  }
1504
- } else { replyErr(cmdId, "BadRequest", "unknown surface: " + surface); }
1505
- return;
1506
- }
1507
- if (op === "tabs.query") {
1508
- nsCall("tabs", "query", [args || {}], function (err, tabs) {
1509
- if (err) { replyErr(cmdId, "TabsError", (err && err.message) || err); return; }
1510
- replyOk(cmdId, (tabs || []).map(function (t) { return {id: t.id, url: t.url, title: t.title, active: t.active, windowId: t.windowId}; }));
1511
- });
1512
- return;
1513
- }
1514
- if (op === "inspect") {
1515
- // Extract a DOM snapshot from the target page via chrome.scripting
1516
- // (CDP-free). Closed shadow roots need CDP; here we read open ones.
1517
- if ((ctx === "content" || ctx === "page") && target.tabId && chrome.scripting) {
1518
- var maxBytes = (args && args.maxBytes) || 262144;
1519
- var includeHtml = !args || !args.include || args.include.indexOf("html") !== -1;
1520
- nsCall("scripting", "executeScript", [{
1521
- target: {tabId: target.tabId},
1522
- world: ctx === "page" ? "MAIN" : "ISOLATED",
1523
- func: function (wantHtml, cap) {
1524
- function countShadow(root) {
1525
- var n = 0, els = root.querySelectorAll("*");
1526
- for (var i = 0; i < els.length; i++) { if (els[i].shadowRoot) { n++; n += countShadow(els[i].shadowRoot); } }
1527
- return n;
1528
- }
1529
- var doc = document;
1530
- var roots = doc.querySelectorAll('#extension-root,[data-extension-root]');
1531
- var out = {
1532
- url: location.href,
1533
- title: doc.title,
1534
- summary: {
1535
- htmlLength: doc.documentElement.outerHTML.length,
1536
- scriptCount: doc.querySelectorAll("script").length,
1537
- styleCount: doc.querySelectorAll("style,link[rel=stylesheet]").length,
1538
- extensionRootCount: roots.length,
1539
- openShadowRoots: countShadow(doc),
1540
- bodyChildCount: doc.body ? doc.body.children.length : 0
1541
- }
1542
- };
1543
- if (wantHtml) {
1544
- var html = doc.documentElement.outerHTML;
1545
- if (cap > 0 && html.length > cap) { out.html = html.slice(0, cap); out.htmlTruncated = true; }
1546
- else { out.html = html; }
1547
- }
1548
- return out;
1549
- },
1550
- args: [includeHtml, maxBytes]
1551
- }], function (err, res) {
1552
- if (err) { replyErr(cmdId, "InspectError", (err && err.message) || err); return; }
1553
- var snap = res && res[0] ? res[0].result : undefined;
1554
- if (snap == null) { replyErr(cmdId, "InspectError", "no injectable frame returned a snapshot for tab " + target.tabId + " (restricted page, or outside host_permissions)"); return; }
1555
- replyOk(cmdId, snap);
1556
- });
1557
- } else if (ctx === "popup" || ctx === "options" || ctx === "sidebar" || ctx === "devtools" || ctx === "newtab" || ctx === "history" || ctx === "bookmarks") {
1558
- // The SW can't read a surface page's DOM; ask the surface's own
1559
- // in-bundle relay (only the open, matching-context page responds).
1560
- chrome.runtime.sendMessage(
1561
- {__extjsInspectRequest: true, target: target, args: args},
1562
- function (resp) {
1563
- if (chrome.runtime.lastError || !resp) {
1564
- replyErr(cmdId, "Unsupported", "surface '" + ctx + "' is not open (open it first: extension open " + ctx + ")");
1565
- } else if (resp.ok) {
1566
- replyOk(cmdId, resp.value);
1567
- } else {
1568
- replyErr(cmdId, (resp.error && resp.error.name) || "InspectError", (resp.error && resp.error.message) || "inspect failed");
1218
+ for (const rel of moduleResourcesFromIdentifier(identifier, contextDir))if (isBackground) index.swSources.add(rel);
1219
+ else if (isContent) {
1220
+ let entries = index.contentEntriesBySource.get(rel);
1221
+ if (!entries) {
1222
+ entries = new Set();
1223
+ index.contentEntriesBySource.set(rel, entries);
1569
1224
  }
1570
- }
1571
- );
1572
- } else if (ctx === "background") {
1573
- replyErr(cmdId, "Unsupported", "the service worker has no DOM; inspect a content/page (with --tab) or an open surface");
1574
- } else {
1575
- replyErr(cmdId, "Unsupported", "inspect of " + ctx + " requires a tabId (content/page) or an open surface");
1576
- }
1577
- return;
1225
+ entries.add(name);
1226
+ } else index.pageSources.add(rel);
1578
1227
  }
1579
- replyErr(cmdId, "BadRequest", "unknown op: " + op);
1580
- } catch (e) {
1581
- replyErr(cmdId, "ExecutorError", e);
1582
- }
1583
1228
  }
1584
-
1585
- function noopLastError() { try { void g.chrome.runtime.lastError; } catch (e) {
1586
- // Ignore
1587
- } }
1588
-
1589
- // Only http(s)/file/ftp tabs can run a content script; skip chrome://, the
1590
- // extension's own pages, about:blank, etc.
1591
- function isInjectableUrl(url) {
1592
- return typeof url === "string" && /^(https?|file|ftp):/i.test(url);
1229
+ return index;
1230
+ }
1231
+ function classifyReloadFromSources(opts) {
1232
+ const { changedSources, forcedFull, getContentScriptCount, getSourceFeatureIndex, outputPath } = opts;
1233
+ if (0 === changedSources.length) return;
1234
+ if (forcedFull) return {
1235
+ type: 'full',
1236
+ changedAssets: changedSources,
1237
+ label: formatReloadContextLabel('extension', changedSources)
1238
+ };
1239
+ let index = null;
1240
+ try {
1241
+ index = getSourceFeatureIndex ? getSourceFeatureIndex() : null;
1242
+ } catch {
1243
+ index = null;
1593
1244
  }
1594
-
1595
- // Re-inject one declared entry's fresh files into every matching open tab.
1596
- // exclude_matches MUST be honored: query the excludes and subtract them.
1597
- function reinjectContentScriptEntry(entry) {
1598
- var chrome = g.chrome;
1599
- var matches = (entry && entry.matches) || [];
1600
- if (!Array.isArray(matches) || !matches.length) return;
1601
- var excludeMatches = (entry && entry.exclude_matches) || [];
1602
- var jsFiles = ((entry && entry.js) || []).filter(function (f) { return typeof f === "string"; });
1603
- var cssFiles = ((entry && entry.css) || []).filter(function (f) { return typeof f === "string"; });
1604
- if (!jsFiles.length && !cssFiles.length) return;
1605
- var world = entry.world === "MAIN" ? "MAIN" : "ISOLATED";
1606
- var allFrames = !!entry.all_frames;
1607
- function injectInto(excludedTabIds) {
1608
- try {
1609
- chrome.tabs.query({url: matches}, function (tabs) {
1610
- var err = null;
1611
- try { err = chrome.runtime.lastError; } catch (e) {
1612
- // Ignore
1613
- }
1614
- if (err || !tabs) return;
1615
- for (var i = 0; i < tabs.length; i++) {
1616
- (function (tab) {
1617
- if (!tab || tab.id == null || !isInjectableUrl(tab.url)) return;
1618
- if (excludedTabIds[tab.id]) return;
1619
- var target = {tabId: tab.id, allFrames: allFrames};
1620
- if (cssFiles.length && chrome.scripting.insertCSS) {
1621
- try { chrome.scripting.insertCSS({target: target, files: cssFiles}, noopLastError); } catch (e) {
1622
- // Ignore
1623
- }
1624
- }
1625
- if (jsFiles.length && chrome.scripting.executeScript) {
1626
- try {
1627
- chrome.scripting.executeScript(
1628
- {target: target, files: jsFiles, world: world, injectImmediately: true},
1629
- noopLastError
1630
- );
1631
- } catch (e) {
1632
- // Ignore
1633
- }
1634
- }
1635
- })(tabs[i]);
1636
- }
1637
- });
1638
- } catch (e) {
1639
- // Ignore
1245
+ const swChanged = [];
1246
+ const contentEntries = new Set();
1247
+ const contentChanged = [];
1248
+ const pageChanged = [];
1249
+ const unknown = [];
1250
+ for (const rel of changedSources){
1251
+ let known = false;
1252
+ if (index?.swSources.has(rel)) {
1253
+ swChanged.push(rel);
1254
+ known = true;
1640
1255
  }
1641
- }
1642
- if (Array.isArray(excludeMatches) && excludeMatches.length) {
1643
- try {
1644
- chrome.tabs.query({url: excludeMatches}, function (excludedTabs) {
1645
- try { void chrome.runtime.lastError; } catch (e) {
1646
- // Ignore
1647
- }
1648
- var excluded = {};
1649
- if (excludedTabs) {
1650
- for (var i = 0; i < excludedTabs.length; i++) {
1651
- if (excludedTabs[i] && excludedTabs[i].id != null) excluded[excludedTabs[i].id] = true;
1652
- }
1653
- }
1654
- injectInto(excluded);
1655
- });
1656
- return;
1657
- } catch (e) {
1658
- // Ignore
1256
+ if (index?.contentEntriesBySource.has(rel)) {
1257
+ contentChanged.push(rel);
1258
+ for (const entry of index.contentEntriesBySource.get(rel))contentEntries.add(entry);
1259
+ known = true;
1659
1260
  }
1660
- }
1661
- injectInto({});
1662
- }
1663
-
1664
- // Re-inject all content scripts into their open tabs. Reads the manifest
1665
- // FROM DISK: getManifest() is frozen and dev filenames are content-hashed.
1666
- function reinjectContentScripts(onDone) {
1667
- var chrome = g.chrome;
1668
- try {
1669
- if (typeof g.fetch !== "function" || !chrome.scripting) return false;
1670
- g.fetch(chrome.runtime.getURL("manifest.json"), {cache: "no-store"})
1671
- .then(function (r) { return r.json(); })
1672
- .then(function (manifest) {
1673
- var entries = (manifest && manifest.content_scripts) || [];
1674
- for (var i = 0; i < entries.length; i++) reinjectContentScriptEntry(entries[i]);
1675
- reregisterForFutureNavigations(entries);
1676
- if (onDone) { try { onDone(); } catch (e) {
1677
- // Ignore
1678
- } }
1679
- })
1680
- .catch(function () {
1681
- // Ignore
1682
- });
1683
- return true;
1684
- } catch (e) { return false; }
1685
- }
1686
-
1687
- function mapRunAt(runAt) {
1688
- if (runAt === "document_start") return "document_start";
1689
- if (runAt === "document_end") return "document_end";
1690
- return "document_idle";
1691
- }
1692
-
1693
- // Re-register content scripts dynamically at the fresh files, so tabs
1694
- // opened AFTER an edit also get the new build. Idempotent per entry id.
1695
- function reregisterForFutureNavigations(entries) {
1696
- var chrome = g.chrome;
1697
- if (
1698
- !chrome.scripting ||
1699
- !chrome.scripting.registerContentScripts ||
1700
- !chrome.scripting.getRegisteredContentScripts
1701
- ) return;
1702
-
1703
- var scripts = [];
1704
- for (var i = 0; i < entries.length; i++) {
1705
- var e = entries[i] || {};
1706
- if (!Array.isArray(e.matches) || !e.matches.length) continue;
1707
- var js = (e.js || []).filter(function (f) { return typeof f === "string"; });
1708
- var css = (e.css || []).filter(function (f) { return typeof f === "string"; });
1709
- if (!js.length && !css.length) continue;
1710
- var s = {
1711
- id: "extjs-dev-cs-" + i,
1712
- matches: e.matches,
1713
- runAt: mapRunAt(e.run_at),
1714
- allFrames: !!e.all_frames,
1715
- world: e.world === "MAIN" ? "MAIN" : "ISOLATED"
1716
- };
1717
- // Dev registration must not be BROADER than the static one: dropping
1718
- // exclude_matches injects into pages the browser itself would skip.
1719
- if (Array.isArray(e.exclude_matches) && e.exclude_matches.length) {
1720
- s.excludeMatches = e.exclude_matches;
1261
+ if (!known && index?.pageSources.has(rel)) {
1262
+ pageChanged.push(rel);
1263
+ known = true;
1721
1264
  }
1722
- if (js.length) s.js = js;
1723
- if (css.length) s.css = css;
1724
- scripts.push(s);
1725
- }
1726
- if (!scripts.length) return;
1727
-
1728
- try {
1729
- chrome.scripting.getRegisteredContentScripts(function (existing) {
1730
- try { void chrome.runtime.lastError; } catch (e) {
1731
- // Ignore
1732
- }
1733
- var have = {};
1734
- if (existing) for (var k = 0; k < existing.length; k++) have[existing[k].id] = true;
1735
- var toRegister = [], toUpdate = [];
1736
- for (var j = 0; j < scripts.length; j++) {
1737
- (have[scripts[j].id] ? toUpdate : toRegister).push(scripts[j]);
1738
- }
1739
- if (toRegister.length) {
1740
- try { chrome.scripting.registerContentScripts(toRegister, noopLastError); } catch (e) {
1741
- // Ignore
1742
- }
1743
- }
1744
- if (toUpdate.length) {
1745
- try { chrome.scripting.updateContentScripts(toUpdate, noopLastError); } catch (e) {
1746
- // Ignore
1747
- }
1748
- }
1749
- });
1750
- } catch (e) {
1751
- // Ignore
1752
- }
1265
+ if (!known) unknown.push(rel);
1753
1266
  }
1754
-
1755
- // Dev-loop reload without the CDP controller: content-scripts re-inject in
1756
- // place into open tabs; service-worker/full/manifest restart the extension.
1757
- function performDevReload(type, onDone) {
1758
- var chrome = g.chrome;
1759
- if (!chrome) return;
1760
-
1761
- var fullReload = function () {
1762
- // Flag a pending reinject for the NEXT producer generation: the
1763
- // boot-time heal is what converges open tabs after a SW/full reload.
1267
+ const staticAssetChanged = outputPath && unknown.some((rel)=>{
1764
1268
  try {
1765
- if (chrome.storage && chrome.storage.local) {
1766
- chrome.storage.local.set({__extjsDevPendingReinject: Date.now()}, noopLastError);
1767
- }
1768
- } catch (e) {
1769
- // Ignore
1770
- }
1771
- // Deferred so in-flight frames and the tab console announcement flush
1772
- // before the SW dies; the devtools companion confirms completion.
1773
- try { setTimeout(function () { try { chrome.runtime.reload(); } catch (e) {
1774
- // Ignore
1775
- } }, 150); } catch (e) {}
1776
- };
1777
-
1778
- if (type === "content-scripts" && chrome.scripting && chrome.tabs && chrome.tabs.query) {
1779
- if (reinjectContentScripts(onDone)) return;
1780
- }
1781
-
1782
- fullReload();
1783
- }
1784
-
1785
- // One server-built label travels the ReloadFrame; both announcement
1786
- // surfaces fire HERE, next to the actual reload action, so they stay true.
1787
-
1788
- // Stable IDs of the bundled extension-js-devtools companion: Chromium pins
1789
- // via the manifest "key"; Firefox via browser_specific_settings.gecko.id.
1790
- var DEVTOOLS_COMPANION_ID_CHROMIUM = "kgdaecdpfkikjncaalnmmnjjfpofkcbl";
1791
- var DEVTOOLS_COMPANION_ID_FIREFOX = "devtools@extension.js";
1792
-
1793
- function notifyDevtoolsCompanion(phase, label, kind) {
1794
- try {
1795
- var chrome = g.chrome;
1796
- if (!chrome || !chrome.runtime || typeof chrome.runtime.sendMessage !== "function") return;
1797
- var id = engineName() === "firefox" ? DEVTOOLS_COMPANION_ID_FIREFOX : DEVTOOLS_COMPANION_ID_CHROMIUM;
1798
- chrome.runtime.sendMessage(
1799
- id,
1800
- {type: "extjs-dev-reload-state", phase: phase, label: label || "", kind: kind || "", instanceId: INSTANCE_ID},
1801
- function () { noopLastError(); }
1802
- );
1803
- } catch (e) {
1804
- // Ignore
1805
- }
1806
- }
1807
-
1808
- // console.log the reload line into every open injectable tab; the patched
1809
- // ISOLATED-world console also lands it in the dev-server log stream.
1810
- function announceReloadInTabs(text) {
1811
- var chrome = g.chrome;
1812
- if (!chrome || !chrome.tabs || !chrome.tabs.query) return;
1813
- try {
1814
- chrome.tabs.query({}, function (tabs) {
1815
- noopLastError();
1816
- if (!tabs) return;
1817
- for (var i = 0; i < tabs.length; i++) {
1818
- (function (tab) {
1819
- if (!tab || tab.id == null || !isInjectableUrl(tab.url)) return;
1820
- if (chrome.scripting && chrome.scripting.executeScript) {
1821
- try {
1822
- chrome.scripting.executeScript(
1823
- {target: {tabId: tab.id}, func: function (t) { console.log(t); }, args: [text]},
1824
- noopLastError
1825
- );
1826
- return;
1827
- } catch (e) {
1828
- // Ignore
1829
- }
1830
- }
1831
- // MV2 fallback (Firefox without the scripting API).
1832
- if (chrome.tabs.executeScript) {
1833
- try {
1834
- chrome.tabs.executeScript(tab.id, {code: "console.log(" + JSON.stringify(text) + ");"}, noopLastError);
1835
- } catch (e) {
1836
- // Ignore
1837
- }
1838
- }
1839
- })(tabs[i]);
1840
- }
1841
- });
1842
- } catch (e) {
1843
- // Ignore
1844
- }
1845
- }
1846
-
1847
- function handleDevReloadFrame(frame) {
1848
- var kind = frame.reloadType || "full";
1849
- var label = typeof frame.label === "string" ? frame.label : "";
1850
- // The server always builds the label; the kind-derived fallback only
1851
- // covers a malformed frame so the announcement stays truthful.
1852
- var fallback = kind === "content-scripts" ? "content_script"
1853
- : kind === "service-worker" ? "service_worker"
1854
- : "extension";
1855
- var announced = "[Extension.js] Reloading " + (label || fallback) + "…";
1856
-
1857
- notifyDevtoolsCompanion("reloading", label, kind);
1858
-
1859
- if (kind === "page") {
1860
- // Notify-only: rspack-dev-server's livereload refreshes the open
1861
- // surface; reloading the extension here would race it.
1862
- return;
1863
- }
1864
-
1865
- // Delivery ack for the broker: proves this SW's message pump processed
1866
- // the frame (a socket write proves nothing). Sent on receipt, by design.
1867
- send({type: "reload-ack", reloadType: kind, label: label});
1868
-
1869
- announceReloadInTabs(announced);
1870
- performDevReload(kind, function () {
1871
- // Only the content-scripts path confirms from here; full/SW reloads
1872
- // are confirmed by the devtools companion's chrome.management listeners.
1873
- notifyDevtoolsCompanion("reloaded", label, kind);
1874
- });
1875
- }
1876
-
1877
- function sanitize(args) {
1878
- var out = [];
1879
- for (var i = 0; i < args.length; i++) {
1880
- var p = args[i];
1881
- try {
1882
- if (typeof p === "string") out.push(p.length > 2048 ? p.slice(0, 2048) + "..." : p);
1883
- else if (p instanceof Error) out.push(p.name + ": " + p.message);
1884
- else out.push(JSON.stringify(p));
1885
- } catch (e) {
1886
- out.push(String(p));
1887
- }
1888
- if (out.join(" ").length > 8192) break;
1889
- }
1890
- return out;
1891
- }
1892
-
1893
- function flush() {
1894
- while (queue.length && open && socket) {
1895
- var f = queue.shift();
1896
- try { socket.send(JSON.stringify(f)); } catch (e) { break; }
1897
- }
1898
- }
1899
-
1900
- function send(frame) {
1901
- if (open && socket) {
1902
- try { socket.send(JSON.stringify(frame)); return; } catch (e) {
1903
- // Ignore
1904
- }
1905
- }
1906
- if (queue.length < MAX_QUEUE) queue.push(frame);
1907
- }
1908
-
1909
- function schedule() {
1910
- var delay = backoff;
1911
- backoff = Math.min(backoff * 2, MAX_BACKOFF);
1912
- try { setTimeout(connect, delay); } catch (e) {
1913
- // Ignore
1914
- }
1915
- }
1916
-
1917
- function connect() {
1918
- try {
1919
- socket = new WS("ws://" + HOST + ":" + PORT + "/extjs-control");
1920
- } catch (e) {
1921
- schedule();
1922
- return;
1923
- }
1924
- socket.onopen = function () {
1925
- open = true;
1926
- backoff = 250;
1927
- try {
1928
- socket.send(JSON.stringify({type: "hello", v: 1, role: "producer", instanceId: INSTANCE_ID}));
1929
- } catch (e) {
1930
- // Ignore
1931
- }
1932
- flush();
1933
- };
1934
- socket.onmessage = function (ev) {
1935
- var frame;
1936
- try { frame = JSON.parse(typeof ev.data === "string" ? ev.data : ""); } catch (e) { return; }
1937
- if (frame && frame.type === "command") {
1938
- try { executeCommand(frame); } catch (e) { replyErr(frame.cmdId, "ExecutorError", e); }
1939
- } else if (frame && frame.type === "reload") {
1940
- // Dev-loop reload broadcast (see broker.broadcastReload).
1941
- // Fire-and-forget: no result frame is expected.
1942
- try { handleDevReloadFrame(frame); } catch (e) {
1943
- // Ignore
1944
- }
1945
- } else if (frame && frame.type === "ping") {
1946
- // Server keepalive: merely RECEIVING this resets the MV3 SW's ~30s
1947
- // idle timer (Chrome 116+), keeping it reachable. Nothing to do.
1948
- }
1949
- };
1950
- socket.onclose = function () {
1951
- open = false;
1952
- socket = null;
1953
- schedule();
1954
- };
1955
- socket.onerror = function () {
1956
- try { socket && socket.close(); } catch (e) {
1957
- // Ignore
1958
- }
1959
- };
1960
- }
1961
-
1962
- // Uncaught errors don't route through the patched console; the handlers
1963
- // below forward them, using these signatures to skip already-logged throws.
1964
- var recentErrorSigs = [];
1965
- function errorSig(message, stack) {
1966
- return String(message == null ? "" : message) + "::" + String(stack == null ? "" : stack).slice(0, 200);
1967
- }
1968
- function noteErrorSig(sig) {
1969
- recentErrorSigs.push({sig: sig, t: Date.now()});
1970
- if (recentErrorSigs.length > 50) recentErrorSigs.shift();
1971
- }
1972
- function errorSigSeenRecently(sig) {
1973
- var now = Date.now();
1974
- for (var i = recentErrorSigs.length - 1; i >= 0; i--) {
1975
- if (now - recentErrorSigs[i].t > 3000) { recentErrorSigs.splice(i, 1); continue; }
1976
- if (recentErrorSigs[i].sig === sig) return true;
1977
- }
1978
- return false;
1979
- }
1980
-
1981
- LEVELS.forEach(function (level) {
1982
- var orig = typeof consoleRef[level] === "function"
1983
- ? consoleRef[level].bind(consoleRef)
1984
- : function () {};
1985
- consoleRef[level] = function () {
1986
- try {
1987
- if (level === "error") {
1988
- for (var ai = 0; ai < arguments.length; ai++) {
1989
- if (arguments[ai] instanceof Error) noteErrorSig(errorSig(arguments[ai].message, arguments[ai].stack));
1990
- }
1991
- }
1992
- send({
1993
- type: "log",
1994
- event: {
1995
- v: 1,
1996
- id: nowId(),
1997
- timestamp: Date.now(),
1998
- level: level,
1999
- context: CONTEXT,
2000
- messageParts: sanitize([].slice.call(arguments)),
2001
- runId: INSTANCE_ID
2002
- }
2003
- });
2004
- } catch (e) {
2005
- // Ignore
2006
- }
2007
- return orig.apply(consoleRef, arguments);
2008
- };
2009
- });
2010
-
2011
- // Forward uncaught throws / unhandled rejections as level:"error", deduped
2012
- // so a logged-then-rethrown Error emits once.
2013
- function shipUncaughtError(message, stack, url) {
2014
- try {
2015
- var sig = errorSig(message, stack);
2016
- if (errorSigSeenRecently(sig)) return;
2017
- noteErrorSig(sig);
2018
- send({
2019
- type: "log",
2020
- event: {
2021
- v: 1,
2022
- id: nowId(),
2023
- timestamp: Date.now(),
2024
- level: "error",
2025
- context: CONTEXT,
2026
- messageParts: sanitize([String(message) + (stack ? "\\n" + stack : "")]),
2027
- url: url,
2028
- runId: INSTANCE_ID
2029
- }
2030
- });
2031
- } catch (e) {
2032
- // Ignore
2033
- }
2034
- }
2035
- try {
2036
- if (typeof g.addEventListener === "function") {
2037
- g.addEventListener("error", function (ev) {
2038
- var err = ev && ev.error;
2039
- var message = (err && err.message) || (ev && ev.message) || "Uncaught error";
2040
- shipUncaughtError(message, err && err.stack, ev && ev.filename);
2041
- });
2042
- g.addEventListener("unhandledrejection", function (ev) {
2043
- var reason = ev && ev.reason;
2044
- var message = (reason && reason.message) || (reason != null ? String(reason) : "Unhandled rejection");
2045
- shipUncaughtError("Unhandled promise rejection: " + message, reason && reason.stack, undefined);
2046
- });
2047
- }
2048
- } catch (e) {
2049
- // Ignore
2050
- }
2051
-
2052
- // Other contexts relay console logs to this SW over a NAMED runtime.Port:
2053
- // they can't open the WS under page CSP, and sendMessage can echo-loop.
2054
- function shipRelayedLog(ev, sender) {
2055
- try {
2056
- send({
2057
- type: "log",
2058
- event: {
2059
- v: 1,
2060
- id: nowId(),
2061
- timestamp: Date.now(),
2062
- level: ev.level || "log",
2063
- context: ev.context || "content",
2064
- messageParts: Array.isArray(ev.messageParts) ? ev.messageParts : [],
2065
- url: ev.url || (sender ? sender.url : undefined),
2066
- tabId: sender && sender.tab ? sender.tab.id : undefined,
2067
- frameId: sender ? sender.frameId : undefined,
2068
- runId: INSTANCE_ID
2069
- }
2070
- });
2071
- } catch (e) {
2072
- // Ignore
2073
- }
2074
- }
2075
- try {
2076
- var rtPort = g.chrome;
2077
- if (rtPort && rtPort.runtime && rtPort.runtime.onConnect) {
2078
- rtPort.runtime.onConnect.addListener(function (port) {
2079
- if (!port || port.name !== "__extjs-bridge-log__") return;
2080
- try {
2081
- port.onMessage.addListener(function (msg) {
2082
- if (!msg || !msg.__extjsBridgeLog) return;
2083
- shipRelayedLog(msg.__extjsBridgeLog, port.sender);
2084
- });
2085
- } catch (e) {
2086
- // Ignore
2087
- }
2088
- });
2089
- }
2090
- } catch (e) {
2091
- // Ignore
2092
- }
2093
- // Legacy sendMessage path: kept for any surface still relaying the old way
2094
- // (harmless one-predicate listener; new relays never use it).
2095
- try {
2096
- var rt = g.chrome;
2097
- if (rt && rt.runtime && rt.runtime.onMessage) {
2098
- rt.runtime.onMessage.addListener(function (msg, sender) {
2099
- if (!msg || !msg.__extjsBridgeLog) return;
2100
- shipRelayedLog(msg.__extjsBridgeLog, sender);
2101
- });
2102
- }
2103
- } catch (e) {
2104
- // Ignore
2105
- }
2106
-
2107
- // Chrome never injects manifest content scripts into already-open tabs, so
2108
- // fire one reinject on onInstalled (which skips idle-stop SW wakes).
2109
- try {
2110
- var rtBoot = g.chrome;
2111
- if (rtBoot && rtBoot.runtime && rtBoot.runtime.onInstalled) {
2112
- rtBoot.runtime.onInstalled.addListener(function () {
2113
- try {
2114
- setTimeout(function () {
2115
- try { reinjectContentScripts(); } catch (e) {
2116
- // Ignore
2117
- }
2118
- }, 250);
2119
- } catch (e) {
2120
- // Ignore
2121
- }
2122
- });
2123
- }
2124
- } catch (e) {
2125
- // Ignore
2126
- }
2127
-
2128
- // runtime.reload() does NOT fire onInstalled (verified Chromium 146), so
2129
- // consume the pre-reload storage flag here (fresh only) to heal open tabs.
2130
- try {
2131
- var stBoot = g.chrome;
2132
- if (stBoot && stBoot.storage && stBoot.storage.local) {
2133
- stBoot.storage.local.get("__extjsDevPendingReinject", function (res) {
2134
- noopLastError();
2135
- var ts = res && res.__extjsDevPendingReinject;
2136
- if (ts == null) return;
2137
- try { stBoot.storage.local.remove("__extjsDevPendingReinject", noopLastError); } catch (e) {
2138
- // Ignore
2139
- }
2140
- if (typeof ts !== "number" || Date.now() - ts > 30000) return;
2141
- setTimeout(function () {
2142
- try { reinjectContentScripts(); } catch (e) {
2143
- // Ignore
2144
- }
2145
- }, 250);
2146
- });
2147
- }
2148
- } catch (e) {
2149
- // Ignore
2150
- }
2151
-
2152
- connect();
2153
- } catch (e) {
2154
- // Ignore
2155
- }
2156
- })();
2157
- `;
2158
- const BRIDGE_RELAY_SOURCE = `;(function () {
2159
- try {
2160
- var g = (typeof globalThis === "object" && globalThis) ? globalThis : this;
2161
- if (!g || g.__extjsBridgeRelayInstalled) return;
2162
-
2163
- var CONTEXT = "__EXTJS_CONTEXT__";
2164
- var chrome = g.chrome;
2165
- if (!chrome || !chrome.runtime) return;
2166
- var canRelay = typeof chrome.runtime.connect === "function";
2167
-
2168
- g.__extjsBridgeRelayInstalled = true;
2169
- var consoleRef = g.console || {};
2170
- var LEVELS = ["log", "info", "warn", "error", "debug", "trace"];
2171
-
2172
- function sanitize(args) {
2173
- var out = [];
2174
- for (var i = 0; i < args.length; i++) {
2175
- var p = args[i];
2176
- try {
2177
- if (typeof p === "string") out.push(p.length > 2048 ? p.slice(0, 2048) + "..." : p);
2178
- else if (p instanceof Error) out.push(p.name + ": " + p.message);
2179
- else out.push(JSON.stringify(p));
2180
- } catch (e) { out.push(String(p)); }
2181
- if (out.join(" ").length > 8192) break;
2182
- }
2183
- return out;
2184
- }
2185
-
2186
- function here() { try { return g.location ? g.location.href : undefined; } catch (e) { return undefined; } }
2187
-
2188
- // Lazy named port to the SW producer: connecting wakes an idle SW, but
2189
- // port frames never reach the extension's own onMessage listeners.
2190
- var logPort = null;
2191
- function getLogPort() {
2192
- if (!canRelay) return null;
2193
- if (logPort) return logPort;
2194
- try {
2195
- logPort = chrome.runtime.connect({name: "__extjs-bridge-log__"});
2196
- logPort.onDisconnect.addListener(function () {
2197
- try { void chrome.runtime.lastError; } catch (e) {
2198
- // Ignore
2199
- }
2200
- logPort = null;
2201
- });
2202
- } catch (e) { logPort = null; }
2203
- return logPort;
2204
- }
2205
-
2206
- // Relay one log payload to the SW producer over the named port, redialing
2207
- // once if the port went stale (SW restarted).
2208
- function postLog(payload) {
2209
- var p = getLogPort();
2210
- if (!p) return;
2211
- try {
2212
- p.postMessage({__extjsBridgeLog: payload});
2213
- } catch (e) {
2214
- logPort = null;
2215
- var p2 = getLogPort();
2216
- if (p2) { try { p2.postMessage({__extjsBridgeLog: payload}); } catch (e2) {
2217
- // Ignore
2218
- } }
2219
- }
2220
- }
2221
-
2222
- // Uncaught throws / unhandled rejections never route through the patched
2223
- // console; forward them, deduped against console.error Error signatures.
2224
- var recentErrorSigs = [];
2225
- function errorSig(message, stack) {
2226
- return String(message == null ? "" : message) + "::" + String(stack == null ? "" : stack).slice(0, 200);
2227
- }
2228
- function noteErrorSig(sig) {
2229
- recentErrorSigs.push({sig: sig, t: Date.now()});
2230
- if (recentErrorSigs.length > 50) recentErrorSigs.shift();
2231
- }
2232
- function errorSigSeenRecently(sig) {
2233
- var now = Date.now();
2234
- for (var i = recentErrorSigs.length - 1; i >= 0; i--) {
2235
- if (now - recentErrorSigs[i].t > 3000) { recentErrorSigs.splice(i, 1); continue; }
2236
- if (recentErrorSigs[i].sig === sig) return true;
2237
- }
2238
- return false;
2239
- }
2240
-
2241
- LEVELS.forEach(function (level) {
2242
- var orig = typeof consoleRef[level] === "function" ? consoleRef[level].bind(consoleRef) : function () {};
2243
- consoleRef[level] = function () {
2244
- try {
2245
- if (level === "error") {
2246
- for (var ai = 0; ai < arguments.length; ai++) {
2247
- if (arguments[ai] instanceof Error) noteErrorSig(errorSig(arguments[ai].message, arguments[ai].stack));
2248
- }
2249
- }
2250
- postLog({level: level, context: CONTEXT, messageParts: sanitize([].slice.call(arguments)), url: here()});
2251
- } catch (e) {
2252
- // Ignore
2253
- }
2254
- return orig.apply(consoleRef, arguments);
2255
- };
2256
- });
2257
-
2258
- function shipUncaughtError(message, stack, url) {
2259
- try {
2260
- var sig = errorSig(message, stack);
2261
- if (errorSigSeenRecently(sig)) return;
2262
- noteErrorSig(sig);
2263
- postLog({level: "error", context: CONTEXT, messageParts: sanitize([String(message) + (stack ? "\\n" + stack : "")]), url: url || here()});
2264
- } catch (e) {
2265
- // Ignore
2266
- }
2267
- }
2268
- try {
2269
- if (typeof g.addEventListener === "function") {
2270
- g.addEventListener("error", function (ev) {
2271
- var err = ev && ev.error;
2272
- var message = (err && err.message) || (ev && ev.message) || "Uncaught error";
2273
- shipUncaughtError(message, err && err.stack, (ev && ev.filename) || undefined);
2274
- });
2275
- g.addEventListener("unhandledrejection", function (ev) {
2276
- var reason = ev && ev.reason;
2277
- var message = (reason && reason.message) || (reason != null ? String(reason) : "Unhandled rejection");
2278
- shipUncaughtError("Unhandled promise rejection: " + message, reason && reason.stack, undefined);
2279
- });
2280
- }
2281
- } catch (e) {
2282
- // Ignore
2283
- }
2284
-
2285
- // The SW executor can't reach another extension page's DOM or eval there,
2286
- // so THIS surface answers inspect/eval requests for its own context.
2287
- try {
2288
- if (chrome.runtime && chrome.runtime.onMessage) {
2289
- chrome.runtime.onMessage.addListener(function (msg, _sender, sendResponse) {
2290
- if (!msg || !msg.__extjsEvalRequest) return;
2291
- if (!msg.target || msg.target.context !== CONTEXT) return; // not for me
2292
- try {
2293
- var value = (0, eval)(String((msg.args && msg.args.expression) || ""));
2294
- try { sendResponse({ok: true, value: value}); }
2295
- catch (eSend) { sendResponse({ok: true, value: String(value)}); }
2296
- } catch (e) {
2297
- var emsg = (e && e.message) || String(e);
2298
- if (/Content Security Policy|unsafe-eval|call to eval/i.test(emsg)) {
2299
- sendResponse({ok: false, error: {name: "Unsupported", message: "eval of a string is blocked in the " + CONTEXT + " page by the MV3 extension CSP. Use extension inspect " + CONTEXT + " to read its DOM, or --context background on an MV2/Firefox build. Original: " + emsg}});
2300
- } else {
2301
- sendResponse({ok: false, error: {name: (e && e.name) || "EvalError", message: emsg}});
2302
- }
2303
- }
2304
- return true; // responded
2305
- });
2306
- }
2307
- } catch (e) {
2308
- // Ignore
2309
- }
2310
-
2311
- try {
2312
- if (chrome.runtime && chrome.runtime.onMessage) {
2313
- chrome.runtime.onMessage.addListener(function (msg, _sender, sendResponse) {
2314
- if (!msg || !msg.__extjsInspectRequest) return;
2315
- if (!msg.target || msg.target.context !== CONTEXT) return; // not for me
2316
- try {
2317
- var args = msg.args || {};
2318
- var wantHtml = !args.include || args.include.indexOf("html") !== -1;
2319
- var cap = args.maxBytes || 262144;
2320
- function countShadow(root) {
2321
- var n = 0, els = root.querySelectorAll("*");
2322
- for (var i = 0; i < els.length; i++) { if (els[i].shadowRoot) { n++; n += countShadow(els[i].shadowRoot); } }
2323
- return n;
2324
- }
2325
- var doc = g.document;
2326
- var roots = doc.querySelectorAll('#extension-root,[data-extension-root]');
2327
- var snap = {
2328
- context: CONTEXT,
2329
- url: here(),
2330
- title: doc.title,
2331
- summary: {
2332
- htmlLength: doc.documentElement.outerHTML.length,
2333
- scriptCount: doc.querySelectorAll("script").length,
2334
- styleCount: doc.querySelectorAll("style,link[rel=stylesheet]").length,
2335
- extensionRootCount: roots.length,
2336
- openShadowRoots: countShadow(doc),
2337
- bodyChildCount: doc.body ? doc.body.children.length : 0
2338
- }
2339
- };
2340
- if (wantHtml) {
2341
- var html = doc.documentElement.outerHTML;
2342
- if (cap > 0 && html.length > cap) { snap.html = html.slice(0, cap); snap.htmlTruncated = true; }
2343
- else { snap.html = html; }
2344
- }
2345
- sendResponse({ok: true, value: snap});
2346
- } catch (e) {
2347
- sendResponse({ok: false, error: {name: "InspectError", message: String(e)}});
2348
- }
2349
- return true; // responded
2350
- });
2351
- }
2352
- } catch (e) {
2353
- // Ignore
2354
- }
2355
- } catch (e) {
2356
- // Ignore
2357
- }
2358
- })();
2359
- `;
2360
- function buildBridgeRelaySource(opts) {
2361
- return BRIDGE_RELAY_SOURCE.replace(/__EXTJS_CONTEXT__/g, String(opts.context || 'content'));
2362
- }
2363
- function buildBridgeProducerSource(opts) {
2364
- if (!opts.controlPort || opts.controlPort < 1) return '';
2365
- return BRIDGE_PRODUCER_SOURCE.replace(/__EXTJS_CONTROL_PORT__/g, String(opts.controlPort)).replace(/__EXTJS_INSTANCE_ID__/g, String(opts.instanceId)).replace(/__EXTJS_CONTEXT__/g, String(opts.context || 'background')).replace(/__EXTJS_CONTROL_HOST__/g, String(opts.host || '127.0.0.1'));
2366
- }
2367
- const BACKGROUND_ASSET = /(^|\/)background\/(?:service_worker|scripts?)\.js$/i;
2368
- class InjectBridgeProducer {
2369
- apply(compiler) {
2370
- const controlPort = parseInt(String(process.env.EXTENSION_CONTROL_PORT || ''), 10);
2371
- const instanceId = String(process.env.EXTENSION_INSTANCE_ID || '');
2372
- const host = String(process.env.EXTENSION_DEV_SERVER_CONNECTABLE_HOST || '').trim() || '127.0.0.1';
2373
- const source = buildBridgeProducerSource({
2374
- controlPort: Number.isFinite(controlPort) ? controlPort : null,
2375
- instanceId,
2376
- context: 'background',
2377
- host
2378
- });
2379
- if (!source) return;
2380
- compiler.hooks.thisCompilation.tap(InjectBridgeProducer.name, (compilation)=>{
2381
- compilation.hooks.processAssets.tap({
2382
- name: InjectBridgeProducer.name,
2383
- stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT + 101
2384
- }, ()=>{
2385
- for (const asset of compilation.getAssets()){
2386
- if (!BACKGROUND_ASSET.test(asset.name)) continue;
2387
- const original = asset.source.source().toString();
2388
- if (-1 !== original.indexOf('__extjsBridgeProducerInstalled')) continue;
2389
- const next = `${source}\n${original}`;
2390
- compilation.updateAsset(asset.name, new core_sources.RawSource(next));
2391
- }
2392
- });
2393
- });
2394
- }
2395
- }
2396
- const RELAY_TARGETS = [
2397
- {
2398
- re: /(^|\/)content_scripts\/.+\.js$/i,
2399
- context: 'content'
2400
- },
2401
- {
2402
- re: /(^|\/)action\/index\.js$/i,
2403
- context: 'popup'
2404
- },
2405
- {
2406
- re: /(^|\/)options\/index\.js$/i,
2407
- context: 'options'
2408
- },
2409
- {
2410
- re: /(^|\/)sidebar\/index\.js$/i,
2411
- context: 'sidebar'
2412
- },
2413
- {
2414
- re: /(^|\/)devtools\/index\.js$/i,
2415
- context: 'devtools'
2416
- },
2417
- {
2418
- re: /(^|\/)chrome_url_overrides\/newtab\.js$/i,
2419
- context: 'newtab'
2420
- },
2421
- {
2422
- re: /(^|\/)chrome_url_overrides\/history\.js$/i,
2423
- context: 'history'
2424
- },
2425
- {
2426
- re: /(^|\/)chrome_url_overrides\/bookmarks\.js$/i,
2427
- context: 'bookmarks'
2428
- }
2429
- ];
2430
- class InjectBridgeRelay {
2431
- apply(compiler) {
2432
- const controlPort = parseInt(String(process.env.EXTENSION_CONTROL_PORT || ''), 10);
2433
- if (!Number.isFinite(controlPort) || controlPort < 1) return;
2434
- const sourceFor = new Map();
2435
- for (const t of RELAY_TARGETS)if (!sourceFor.has(t.context)) sourceFor.set(t.context, buildBridgeRelaySource({
2436
- context: t.context
2437
- }));
2438
- compiler.hooks.thisCompilation.tap(InjectBridgeRelay.name, (compilation)=>{
2439
- compilation.hooks.processAssets.tap({
2440
- name: InjectBridgeRelay.name,
2441
- stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT + 101
2442
- }, ()=>{
2443
- for (const asset of compilation.getAssets()){
2444
- const target = RELAY_TARGETS.find((t)=>t.re.test(asset.name));
2445
- if (!target) continue;
2446
- const original = asset.source.source().toString();
2447
- if (-1 !== original.indexOf('__extjsBridgeRelayInstalled')) continue;
2448
- const next = `${sourceFor.get(target.context)}\n${original}`;
2449
- compilation.updateAsset(asset.name, new core_sources.RawSource(next));
2450
- }
2451
- });
2452
- });
2453
- }
2454
- }
2455
- const SCRIPTS_REPLAY_SHIM_SOURCE = `;(function () {
2456
- try {
2457
- if (typeof globalThis !== "object" || !globalThis) return;
2458
- var chromeRef =
2459
- (globalThis.chrome && globalThis.chrome.scripting && typeof globalThis.chrome.scripting.executeScript === "function")
2460
- ? globalThis.chrome
2461
- : (globalThis.browser && globalThis.browser.scripting && typeof globalThis.browser.scripting.executeScript === "function")
2462
- ? globalThis.browser
2463
- : null;
2464
- if (!chromeRef) return;
2465
- if (globalThis.__extjsScriptsReplayInstalled) return;
2466
- globalThis.__extjsScriptsReplayInstalled = true;
2467
-
2468
- var registry = new Map();
2469
- var originalExecuteScript = chromeRef.scripting.executeScript.bind(chromeRef.scripting);
2470
-
2471
- var serialize = function (entry) {
2472
- try {
2473
- return JSON.stringify({
2474
- files: entry && Array.isArray(entry.files) ? entry.files : [],
2475
- world: entry && entry.world ? String(entry.world) : ""
2476
- });
2477
- } catch (error) {
2478
- return "";
2479
- }
2480
- };
2481
-
2482
- var track = function (injection) {
2483
- try {
2484
- var tabId = injection && injection.target && injection.target.tabId;
2485
- var files =
2486
- injection && Array.isArray(injection.files) ? injection.files.slice() : null;
2487
- if (typeof tabId !== "number") return;
2488
- if (!files || !files.length) return;
2489
- var world = injection.world ? String(injection.world) : undefined;
2490
- var entry = { files: files, world: world, sig: serialize({ files: files, world: world }) };
2491
- var existing = registry.get(tabId) || [];
2492
- // Dedupe by signature, repeated identical injections (e.g. user
2493
- // clicks the action twice) shouldn't stack in the replay list.
2494
- if (existing.some(function (e) { return e.sig === entry.sig; })) return;
2495
- existing.push(entry);
2496
- // Cap to the most recent 50 distinct injections per tab so a misbehaving
2497
- // user loop can't grow the registry unbounded.
2498
- registry.set(tabId, existing.slice(-50));
2499
- } catch (error) {
2500
- // Tracking is best-effort; never break the user's executeScript call.
2501
- }
2502
- };
2503
-
2504
- chromeRef.scripting.executeScript = function (injection, callback) {
2505
- track(injection);
2506
- return originalExecuteScript(injection, callback);
2507
- };
2508
-
2509
- var normalizeFile = function (value) {
2510
- return String(value || "").replace(/^[/\\\\]+/, "");
2511
- };
2512
-
2513
- var fileMatches = function (entryFile, changedNormalized) {
2514
- var fn = normalizeFile(entryFile);
2515
- for (var i = 0; i < changedNormalized.length; i++) {
2516
- var c = changedNormalized[i];
2517
- if (!c) continue;
2518
- if (fn === c) return true;
2519
- if (fn.length > c.length && fn.slice(fn.length - c.length - 1) === "/" + c) return true;
2520
- if (c.length > fn.length && c.slice(c.length - fn.length - 1) === "/" + fn) return true;
2521
- }
2522
- return false;
2523
- };
2524
-
2525
- globalThis.__extjsScriptsReplay = function (changedFiles) {
2526
- var changedNormalized = (Array.isArray(changedFiles) ? changedFiles : []).map(normalizeFile);
2527
- var promises = [];
2528
- registry.forEach(function (entries, tabId) {
2529
- entries.forEach(function (entry) {
2530
- var matches = false;
2531
- for (var i = 0; i < entry.files.length; i++) {
2532
- if (fileMatches(entry.files[i], changedNormalized)) {
2533
- matches = true;
2534
- break;
2535
- }
2536
- }
2537
- if (!matches) return;
2538
- try {
2539
- promises.push(
2540
- originalExecuteScript({
2541
- target: { tabId: tabId },
2542
- files: entry.files,
2543
- world: entry.world
2544
- }).then(
2545
- function () { return { ok: true, tabId: tabId, files: entry.files }; },
2546
- function (error) {
2547
- return {
2548
- ok: false,
2549
- tabId: tabId,
2550
- files: entry.files,
2551
- error: String((error && error.message) || error)
2552
- };
2553
- }
2554
- )
2555
- );
2556
- } catch (error) {
2557
- // Tab may have closed; skip silently.
2558
- }
2559
- });
2560
- });
2561
- return Promise.all(promises);
2562
- };
2563
- } catch (error) {
2564
- // Best-effort shim; do not break the SW bootstrap on any failure.
2565
- }
2566
- })();
2567
- `;
2568
- const inject_scripts_replay_shim_BACKGROUND_ASSET = /(^|\/)background\/(?:service_worker|script)\.js$/i;
2569
- class InjectScriptsReplayShim {
2570
- apply(compiler) {
2571
- compiler.hooks.thisCompilation.tap(InjectScriptsReplayShim.name, (compilation)=>{
2572
- compilation.hooks.processAssets.tap({
2573
- name: InjectScriptsReplayShim.name,
2574
- stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT + 100
2575
- }, ()=>{
2576
- for (const asset of compilation.getAssets()){
2577
- if (!inject_scripts_replay_shim_BACKGROUND_ASSET.test(asset.name)) continue;
2578
- const original = asset.source.source().toString();
2579
- if (-1 !== original.indexOf('__extjsScriptsReplayInstalled')) continue;
2580
- const next = `${SCRIPTS_REPLAY_SHIM_SOURCE}\n${original}`;
2581
- compilation.updateAsset(asset.name, new core_sources.RawSource(next));
2582
- }
2583
- });
2584
- });
2585
- }
2586
- }
2587
- const HOT_DIR = 'hot';
2588
- class PruneStaleHotUpdates {
2589
- static name = 'plugin-reload:prune-stale-hot-updates';
2590
- previousGeneration = new Set();
2591
- apply(compiler) {
2592
- if (!compiler?.hooks?.done?.tap) return;
2593
- compiler.hooks.done.tap(PruneStaleHotUpdates.name, (stats)=>{
2594
- try {
2595
- const outputPath = compiler.options.output?.path;
2596
- if (!outputPath) return;
2597
- const hotDir = __rspack_external_node_path_c5b9b54f.join(outputPath, HOT_DIR);
2598
- if (!__rspack_external_node_fs_5ea92f0c.existsSync(hotDir)) return;
2599
- const emitted = new Set();
2600
- for (const asset of stats.compilation.getAssets()){
2601
- const name = String(asset.name || '').replace(/\\/g, '/');
2602
- if (name.startsWith(`${HOT_DIR}/`)) emitted.add(name.slice(HOT_DIR.length + 1));
2603
- }
2604
- const keep = new Set([
2605
- ...emitted,
2606
- ...this.previousGeneration
2607
- ]);
2608
- const pruneDir = (dir, relPrefix)=>{
2609
- for (const entry of __rspack_external_node_fs_5ea92f0c.readdirSync(dir, {
2610
- withFileTypes: true
2611
- })){
2612
- const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
2613
- const absolute = __rspack_external_node_path_c5b9b54f.join(dir, entry.name);
2614
- if (entry.isDirectory()) {
2615
- pruneDir(absolute, rel);
2616
- try {
2617
- if (0 === __rspack_external_node_fs_5ea92f0c.readdirSync(absolute).length) __rspack_external_node_fs_5ea92f0c.rmdirSync(absolute);
2618
- } catch {}
2619
- continue;
2620
- }
2621
- if (!keep.has(rel)) try {
2622
- __rspack_external_node_fs_5ea92f0c.rmSync(absolute, {
2623
- force: true
2624
- });
2625
- } catch {}
2626
- }
2627
- };
2628
- pruneDir(hotDir, '');
2629
- this.previousGeneration = emitted;
2630
- } catch {}
2631
- });
2632
- }
2633
- }
2634
- function filterKeysForThisBrowser(manifest, browser) {
2635
- const isSafariTarget = 'safari' === browser || 'webkit-based' === browser || String(browser).includes('webkit');
2636
- const isChromiumTarget = isChromiumBasedBrowser(String(browser)) || isSafariTarget;
2637
- const isGeckoTarget = isGeckoBasedBrowser(String(browser));
2638
- const chromiumPrefixes = new Set([
2639
- 'chromium',
2640
- 'chrome',
2641
- 'edge'
2642
- ]);
2643
- const geckoPrefixes = new Set([
2644
- 'gecko',
2645
- 'firefox'
2646
- ]);
2647
- const webkitPrefixes = new Set([
2648
- 'safari',
2649
- 'webkit'
2650
- ]);
2651
- const isFamilyPrefix = (prefix)=>isChromiumTarget && chromiumPrefixes.has(prefix) || isGeckoTarget && geckoPrefixes.has(prefix);
2652
- const isSpecificPrefix = (prefix)=>prefix === browser || isSafariTarget && webkitPrefixes.has(prefix);
2653
- const resolve = (node)=>{
2654
- if (Array.isArray(node)) return node.map((item)=>resolve(item));
2655
- if (node && 'object' == typeof node) {
2656
- const result = {};
2657
- const familyMatches = {};
2658
- const specificMatches = {};
2659
- for (const [key, value] of Object.entries(node)){
2660
- const indexOfColon = key.indexOf(':');
2661
- if (-1 === indexOfColon) {
2662
- result[key] = resolve(value);
2663
- continue;
2664
- }
2665
- const prefix = key.substring(0, indexOfColon);
2666
- const strippedKey = key.substring(indexOfColon + 1);
2667
- if (isSpecificPrefix(prefix)) specificMatches[strippedKey] = resolve(value);
2668
- else if (isFamilyPrefix(prefix)) familyMatches[strippedKey] = resolve(value);
2669
- }
2670
- for (const [strippedKey, value] of Object.entries(familyMatches))result[strippedKey] = value;
2671
- for (const [strippedKey, value] of Object.entries(specificMatches))result[strippedKey] = value;
2672
- return result;
2673
- }
2674
- return node;
2675
- };
2676
- return resolve(manifest);
2677
- }
2678
- const THEME_DISQUALIFYING_KEYS = [
2679
- 'background',
2680
- "content_scripts",
2681
- 'action',
2682
- 'browser_action',
2683
- 'page_action',
2684
- 'sidebar_action',
2685
- 'side_panel',
2686
- 'options_page',
2687
- 'options_ui',
2688
- 'devtools_page',
2689
- 'chrome_url_overrides',
2690
- 'sandbox',
2691
- "user_scripts",
2692
- 'declarative_net_request',
2693
- 'web_accessible_resources'
2694
- ];
2695
- function isStaticTheme(manifest) {
2696
- if (!manifest || 'object' != typeof manifest) return false;
2697
- const theme = manifest.theme;
2698
- if (!theme || 'object' != typeof theme) return false;
2699
- return !THEME_DISQUALIFYING_KEYS.some((key)=>{
2700
- const value = manifest[key];
2701
- return null != value;
2702
- });
2703
- }
2704
- function isStaticThemeSource(manifestPath, browser) {
2705
- if (!manifestPath) return false;
2706
- try {
2707
- const parsed = parseJsonSafe(__rspack_external_node_fs_5ea92f0c.readFileSync(manifestPath, 'utf-8'));
2708
- return isStaticTheme(filterKeysForThisBrowser(parsed, browser));
2709
- } catch {
2710
- return false;
2711
- }
2712
- }
2713
- function isFromFilepathList(filePath, filepathList) {
2714
- return Object.values(filepathList || {}).some((value)=>value === filePath);
2715
- }
2716
- function getFilename(feature, filePath) {
2717
- const entryExt = __rspack_external_node_path_c5b9b54f.extname(filePath);
2718
- let fileOutputpath = feature;
2719
- if ([
2720
- '.js',
2721
- '.jsx',
2722
- '.tsx',
2723
- '.ts',
2724
- '.vue',
2725
- '.svelte'
2726
- ].includes(entryExt)) fileOutputpath = fileOutputpath.replace(entryExt, '.js');
2727
- if ([
2728
- '.html',
2729
- '.njk',
2730
- '.nunjucks'
2731
- ].includes(entryExt)) fileOutputpath = fileOutputpath.replace(entryExt, '.html');
2732
- if ([
2733
- '.css',
2734
- '.scss',
2735
- '.sass',
2736
- '.less'
2737
- ].includes(entryExt)) fileOutputpath = fileOutputpath.replace(entryExt, '.css');
2738
- return unixify(fileOutputpath || '');
2739
- }
2740
- function unixify(filePath) {
2741
- return filePath.replace(/\\/g, '/');
2742
- }
2743
- function resolveRootAbsoluteRef(ref, projectRoot, publicRoot) {
2744
- if (!ref || !ref.startsWith('/')) return;
2745
- if (projectRoot && ref.startsWith(projectRoot)) return;
2746
- if (ref.startsWith('//')) return;
2747
- const trimmed = ref.replace(/^\/+/, '');
2748
- if (!trimmed) return;
2749
- if (publicRoot && __rspack_external_node_fs_5ea92f0c.existsSync(__rspack_external_node_path_c5b9b54f.join(publicRoot, trimmed))) return;
2750
- const candidate = __rspack_external_node_path_c5b9b54f.resolve(projectRoot, trimmed);
2751
- const rel = __rspack_external_node_path_c5b9b54f.relative(projectRoot, candidate);
2752
- if (rel.startsWith('..') || __rspack_external_node_path_c5b9b54f.isAbsolute(rel)) return;
2753
- if (!__rspack_external_node_fs_5ea92f0c.existsSync(candidate) || !__rspack_external_node_fs_5ea92f0c.statSync(candidate).isFile()) return;
2754
- return candidate;
2755
- }
2756
- function collectRootAbsoluteRefs(source) {
2757
- const refs = new Set();
2758
- const attrRe = /(?:src|href)\s*=\s*["'](\/[^"'#?]*)["']/gi;
2759
- const urlRe = /url\(\s*["']?(\/[^"')#?]+)["']?\s*\)/gi;
2760
- let m;
2761
- while(m = attrRe.exec(source))refs.add(m[1]);
2762
- while(m = urlRe.exec(source))refs.add(m[1]);
2763
- return refs;
2764
- }
2765
- function backgroundPage(manifest) {
2766
- return manifest.background?.page && {
2767
- background: {
2768
- page: (()=>{
2769
- const raw = String(manifest.background.page);
2770
- return getFilename('background/index.html', raw);
2771
- })()
2772
- }
2773
- };
2774
- }
2775
- function normalizeManifestOutputPath(originalPath) {
2776
- if (!originalPath) return originalPath;
2777
- const unixPath = originalPath.replace(/\\/g, '/');
2778
- if (/^\/public\//i.test(unixPath)) return unixPath.replace(/^\/public\//i, '');
2779
- if (/^(?:\.\/)?public\//i.test(unixPath)) return unixPath.replace(/^(?:\.\/)?public\//i, '');
2780
- if (/^\//.test(unixPath)) return unixPath.replace(/^\//, '');
2781
- return unixPath;
2782
- }
2783
- function manifestPageOutputTarget(raw, compiledTarget, manifestPath) {
2784
- const unixPath = raw.replace(/\\/g, '/');
2785
- if (/^(?:\/public\/|(?:\.\/)?public\/)/i.test(unixPath)) return normalizeManifestOutputPath(unixPath);
2786
- if (/^\//.test(unixPath)) {
2787
- const rest = unixPath.replace(/^\/+/, '');
2788
- if (manifestPath && rest) {
2789
- const manifestDir = __rspack_external_node_path_c5b9b54f.dirname(manifestPath);
2790
- const inPublic = __rspack_external_node_fs_5ea92f0c.existsSync(__rspack_external_node_path_c5b9b54f.join(manifestDir, 'public', rest));
2791
- const inRoot = __rspack_external_node_fs_5ea92f0c.existsSync(__rspack_external_node_path_c5b9b54f.join(manifestDir, rest));
2792
- if (inRoot && !inPublic) return compiledTarget;
2793
- }
2794
- return normalizeManifestOutputPath(unixPath);
2795
- }
2796
- return compiledTarget;
2797
- }
2798
- function iconOutputPath(raw) {
2799
- if (/^(?:\/public\/|(?:\.\/)?public\/)/i.test(raw)) return normalizeManifestOutputPath(raw);
2800
- const normalized = normalizeManifestOutputPath(raw).replace(/^\.\//, '');
2801
- const escapes = !normalized || normalized.split('/').includes('..') || /^[A-Za-z]:/.test(normalized);
2802
- return escapes ? `icons/${normalized.split('/').pop() || ''}` : normalized;
2803
- }
2804
- function chromeUrlOverrides(manifest, manifestPath) {
2805
- return manifest.chrome_url_overrides && {
2806
- chrome_url_overrides: {
2807
- ...manifest.chrome_url_overrides.bookmarks && {
2808
- bookmarks: (()=>{
2809
- const raw = String(manifest.chrome_url_overrides.bookmarks);
2810
- return getFilename(manifestPageOutputTarget(raw, 'chrome_url_overrides/bookmarks.html', manifestPath), raw);
2811
- })()
2812
- },
2813
- ...manifest.chrome_url_overrides.history && {
2814
- history: (()=>{
2815
- const raw = String(manifest.chrome_url_overrides.history);
2816
- return getFilename(manifestPageOutputTarget(raw, 'chrome_url_overrides/history.html', manifestPath), raw);
2817
- })()
2818
- },
2819
- ...manifest.chrome_url_overrides.newtab && {
2820
- newtab: (()=>{
2821
- const raw = String(manifest.chrome_url_overrides.newtab);
2822
- return getFilename(manifestPageOutputTarget(raw, 'chrome_url_overrides/newtab.html', manifestPath), raw);
2823
- })()
2824
- }
2825
- }
2826
- };
2827
- }
2828
- function commands(manifest) {
2829
- return manifest.commands && {
2830
- commands: manifest.commands
2831
- };
2832
- }
2833
- function isBundledContentPath(filePath, ext) {
2834
- const normalized = String(filePath || '').replace(/\\/g, '/');
2835
- const bundledAsset = parseCanonicalContentScriptAsset(normalized);
2836
- return bundledAsset?.extension === ext;
2837
- }
2838
- function isAlreadyBundledContentScripts(contentScripts) {
2839
- if (!Array.isArray(contentScripts) || 0 === contentScripts.length) return false;
2840
- return contentScripts.every((contentObj)=>{
2841
- const js = Array.isArray(contentObj?.js) ? contentObj.js : [];
2842
- const css = Array.isArray(contentObj?.css) ? contentObj.css : [];
2843
- return js.every((filePath)=>isBundledContentPath(filePath, 'js')) && css.every((filePath)=>isBundledContentPath(filePath, 'css'));
2844
- });
2845
- }
2846
- function content_scripts_contentScripts(manifest, manifestPath) {
2847
- if (!manifest.content_scripts) return;
2848
- const original = manifest.content_scripts;
2849
- if (isAlreadyBundledContentScripts(original)) return {
2850
- content_scripts: original
2851
- };
2852
- const originalCount = original.length;
2853
- const result = [];
2854
- let bridgeOrdinal = 0;
2855
- for(let index = 0; index < original.length; index++){
2856
- const contentObj = original[index] || {};
2857
- const contentJs = [
2858
- ...new Set(contentObj.js || [])
2859
- ];
2860
- const contentCss = [
2861
- ...new Set(contentObj.css || [])
2862
- ];
2863
- if ('MAIN' === contentObj.world) {
2864
- const bridgeIndex = originalCount + bridgeOrdinal++;
2865
- const { world: _ignoredWorld, js: _ignoredJs, css: _ignoredCss, ...rest } = contentObj;
2866
- result.push({
2867
- ...rest,
2868
- js: [
2869
- getFilename(getCanonicalContentScriptJsAssetName(bridgeIndex), 'main-world-bridge.js')
2870
- ],
2871
- css: []
2872
- });
2873
- }
2874
- result.push({
2875
- ...original[index] || {},
2876
- js: [
2877
- ...new Set(contentJs.map((js)=>getFilename(getCanonicalContentScriptJsAssetName(index), js)))
2878
- ],
2879
- css: [
2880
- ...new Set(contentCss.map((css)=>getFilename(getCanonicalContentScriptCssAssetName(index), css)))
2881
- ]
2882
- });
2883
- }
2884
- return {
2885
- content_scripts: result
2886
- };
2887
- }
2888
- function contentSecurityPolicy(manifest) {
2889
- return manifest.content_security_policy && {
2890
- content_security_policy: manifest.content_security_policy
2891
- };
2892
- }
2893
- function devtoolsPage(manifest, manifestPath) {
2894
- return manifest.devtools_page && {
2895
- devtools_page: (()=>{
2896
- const raw = String(manifest.devtools_page);
2897
- return getFilename(manifestPageOutputTarget(raw, 'devtools/index.html', manifestPath), raw);
2898
- })()
2899
- };
2900
- }
2901
- function icons(manifest) {
2902
- return manifest.icons && {
2903
- icons: Object.fromEntries(Object.entries(manifest.icons).map(([size, icon])=>{
2904
- const raw = String(icon);
2905
- return [
2906
- size,
2907
- getFilename(iconOutputPath(raw), raw)
2908
- ];
2909
- }))
2910
- };
2911
- }
2912
- const getBasename = (filepath)=>__rspack_external_node_path_c5b9b54f.basename(filepath);
2913
- function omnibox_omnibox(manifest) {
2914
- const omnibox = manifest.omnibox;
2915
- return omnibox && {
2916
- omnibox: {
2917
- ...omnibox,
2918
- ...omnibox.default_icon && {
2919
- default_icon: 'string' == typeof omnibox.default_icon ? getFilename(`icons/${getBasename(omnibox.default_icon)}`, omnibox.default_icon) : Object.fromEntries(Object.entries(omnibox.default_icon).map(([size, icon])=>[
2920
- size,
2921
- getFilename(`icons/${getBasename(icon)}`, icon)
2922
- ]))
2923
- }
2924
- }
2925
- };
2926
- }
2927
- function optionsPage(manifest, manifestPath) {
2928
- return manifest.options_page && {
2929
- options_page: (()=>{
2930
- const raw = String(manifest.options_page);
2931
- return getFilename(manifestPageOutputTarget(raw, 'options/index.html', manifestPath), raw);
2932
- })()
2933
- };
2934
- }
2935
- function optionsUi(manifest, manifestPath) {
2936
- return manifest.options_ui && {
2937
- options_ui: {
2938
- ...manifest.options_ui,
2939
- ...manifest.options_ui.page && {
2940
- page: (()=>{
2941
- const raw = String(manifest.options_ui.page);
2942
- return getFilename(manifestPageOutputTarget(raw, 'options/index.html', manifestPath), raw);
2943
- })()
2944
- }
2945
- }
2946
- };
2947
- }
2948
- function permissions(manifest) {
2949
- return manifest.permissions && {
2950
- permissions: manifest.permissions
2951
- };
2952
- }
2953
- function sandbox(manifest) {
2954
- return manifest.sandbox && {
2955
- sandbox: {
2956
- ...manifest.sandbox,
2957
- ...manifest.sandbox.pages && {
2958
- pages: manifest.sandbox.pages.map((page, index)=>getFilename(`sandbox/page-${index}.html`, page))
2959
- }
2960
- }
2961
- };
2962
- }
2963
- function storage(manifest) {
2964
- return manifest.storage && {
2965
- storage: {
2966
- ...manifest.storage.managed_schema && {
2967
- managed_schema: getFilename('storage/managed_schema.json', manifest.storage.managed_schema)
2968
- }
2969
- }
2970
- };
2971
- }
2972
- const theme_getBasename = (filepath)=>__rspack_external_node_path_c5b9b54f.basename(filepath);
2973
- const rewriteThemeImage = (value)=>getFilename(`theme/images/${theme_getBasename(value)}`, value);
2974
- function theme_theme(manifest) {
2975
- return manifest.theme && {
2976
- theme: {
2977
- ...manifest.theme,
2978
- ...manifest.theme.images && {
2979
- images: Object.fromEntries(Object.entries(manifest.theme.images).map(([key, value])=>[
2980
- key,
2981
- Array.isArray(value) ? value.map(rewriteThemeImage) : rewriteThemeImage(value)
2982
- ]))
2983
- }
2984
- }
2985
- };
2986
- }
2987
- function userScripts(manifest) {
2988
- return manifest.user_scripts && {
2989
- user_scripts: {
2990
- ...manifest.user_scripts,
2991
- ...manifest.user_scripts.api_script && {
2992
- api_script: getFilename("user_scripts/api_script.js", manifest.user_scripts.api_script)
2993
- }
2994
- }
2995
- };
2996
- }
2997
- function normalizeOutputPath(originalPath) {
2998
- if (!originalPath) return originalPath;
2999
- const unix = originalPath.replace(/\\/g, '/');
3000
- if (/[*?[\]{}]/.test(unix)) return unix;
3001
- if (/^\/public\//i.test(unix)) return unix.replace(/^\/public\//i, '');
3002
- if (/^(?:\.\/)?public\//i.test(unix)) return unix.replace(/^(?:\.\/)?public\//i, '');
3003
- if (/^\//.test(unix)) return unix.replace(/^\//, '');
3004
- return unix;
3005
- }
3006
- function webAccessibleResources(manifest) {
3007
- if (!manifest.web_accessible_resources || !manifest.web_accessible_resources.length) return;
3008
- const mapResource = (resource)=>getFilename(normalizeOutputPath(resource), resource);
3009
- const entries = manifest.web_accessible_resources.map((entry)=>{
3010
- if ('string' == typeof entry) return mapResource(entry);
3011
- if (!entry || !Array.isArray(entry.resources)) return;
3012
- return {
3013
- ...entry,
3014
- resources: entry.resources.map(mapResource)
3015
- };
3016
- }).filter((entry)=>void 0 !== entry);
3017
- if (0 === entries.length) return;
3018
- return {
3019
- web_accessible_resources: entries
3020
- };
3021
- }
3022
- function manifestCommon(manifest, manifestPath) {
3023
- return {
3024
- ...backgroundPage(manifest),
3025
- ...chromeUrlOverrides(manifest, manifestPath),
3026
- ...content_scripts_contentScripts(manifest, manifestPath),
3027
- ...devtoolsPage(manifest, manifestPath),
3028
- ...icons(manifest),
3029
- ...commands(manifest),
3030
- ...permissions(manifest),
3031
- ...optionsPage(manifest, manifestPath),
3032
- ...optionsUi(manifest, manifestPath),
3033
- ...sandbox(manifest),
3034
- ...storage(manifest),
3035
- ...theme_theme(manifest),
3036
- ...userScripts(manifest),
3037
- ...webAccessibleResources(manifest),
3038
- ...contentSecurityPolicy(manifest),
3039
- ...omnibox_omnibox(manifest)
3040
- };
3041
- }
3042
- function pageAction(manifest) {
3043
- return manifest.page_action && {
3044
- page_action: {
3045
- ...manifest.page_action,
3046
- ...manifest.page_action.default_popup && {
3047
- default_popup: getFilename('action/index.html', manifest.page_action.default_popup)
3048
- },
3049
- ...manifest.page_action.default_icon && {
3050
- default_icon: 'string' == typeof manifest.page_action.default_icon ? (()=>{
3051
- const raw = String(manifest.page_action.default_icon);
3052
- return getFilename(iconOutputPath(raw), raw);
3053
- })() : Object.fromEntries(Object.entries(manifest.page_action.default_icon).map(([size, icon])=>{
3054
- const raw = String(icon);
3055
- return [
3056
- size,
3057
- getFilename(iconOutputPath(raw), raw)
3058
- ];
3059
- }))
3060
- }
3061
- }
3062
- };
3063
- }
3064
- function sidebarAction(manifest) {
3065
- return manifest.sidebar_action && {
3066
- sidebar_action: {
3067
- ...manifest.sidebar_action,
3068
- ...manifest.sidebar_action.default_panel && {
3069
- default_panel: getFilename("sidebar/index.html", manifest.sidebar_action.default_panel)
3070
- },
3071
- ...manifest.sidebar_action.default_icon && {
3072
- default_icon: 'string' == typeof manifest.sidebar_action.default_icon ? (()=>{
3073
- const raw = String(manifest.sidebar_action.default_icon);
3074
- return getFilename(iconOutputPath(raw), raw);
3075
- })() : Object.fromEntries(Object.entries(manifest.sidebar_action.default_icon).map(([size, icon])=>{
3076
- const raw = String(icon);
3077
- return [
3078
- size,
3079
- getFilename(iconOutputPath(raw), raw)
3080
- ];
3081
- }))
3082
- }
3083
- }
3084
- };
3085
- }
3086
- function background_background(manifest) {
3087
- return manifest.background?.scripts && {
3088
- background: {
3089
- scripts: [
3090
- ...new Set(manifest.background.scripts.map((script)=>getFilename("background/scripts.js", String(script))))
3091
- ]
3092
- }
3093
- };
3094
- }
3095
- function browserAction(manifest) {
3096
- return manifest.browser_action && {
3097
- browser_action: {
3098
- ...manifest.browser_action,
3099
- ...manifest.browser_action.default_popup && {
3100
- default_popup: getFilename('action/index.html', manifest.browser_action.default_popup)
3101
- },
3102
- ...manifest.browser_action.default_icon && {
3103
- default_icon: 'string' == typeof manifest.browser_action.default_icon ? (()=>{
3104
- const raw = String(manifest.browser_action.default_icon);
3105
- return getFilename(iconOutputPath(raw), raw);
3106
- })() : Object.fromEntries(Object.entries(manifest.browser_action.default_icon).map(([size, icon])=>{
3107
- const raw = String(icon);
3108
- return [
3109
- size,
3110
- getFilename(iconOutputPath(raw), raw)
3111
- ];
3112
- }))
3113
- },
3114
- ...manifest.browser_action.theme_icons && {
3115
- theme_icons: manifest.browser_action.theme_icons.map((themeIcon)=>({
3116
- ...themeIcon,
3117
- ...themeIcon.light && {
3118
- light: (()=>{
3119
- const raw = String(themeIcon.light);
3120
- const isPublic = /^(?:\/public\/|(?:\.\/)?public\/)/i.test(raw);
3121
- const target = isPublic ? normalizeManifestOutputPath(raw) : `browser_action/${__rspack_external_node_path_c5b9b54f.basename(raw)}`;
3122
- return getFilename(target, raw);
3123
- })()
3124
- },
3125
- ...themeIcon.dark && {
3126
- dark: (()=>{
3127
- const raw = String(themeIcon.dark);
3128
- const isPublic = /^(?:\/public\/|(?:\.\/)?public\/)/i.test(raw);
3129
- const target = isPublic ? normalizeManifestOutputPath(raw) : `browser_action/${__rspack_external_node_path_c5b9b54f.basename(raw)}`;
3130
- return getFilename(target, raw);
3131
- })()
3132
- }
3133
- }))
3134
- }
3135
- }
3136
- };
3137
- }
3138
- function chromeSettingsOverrides(manifest) {
3139
- return manifest.chrome_settings_overrides && {
3140
- chrome_settings_overrides: {
3141
- ...manifest.chrome_settings_overrides.homepage && {
3142
- homepage: manifest.chrome_settings_overrides.homepage
3143
- },
3144
- ...manifest.chrome_settings_overrides.search_provider && {
3145
- search_provider: {
3146
- ...manifest.chrome_settings_overrides.search_provider,
3147
- ...manifest.chrome_settings_overrides.search_provider.favicon_url && {
3148
- favicon_url: (()=>{
3149
- const fav = manifest.chrome_settings_overrides.search_provider.favicon_url;
3150
- const isUrl = /^(?:[a-z]+:)?\/\//i.test(fav);
3151
- return isUrl ? fav : getFilename(`chrome_settings_overrides/${fav.split('/').pop()}`, fav);
3152
- })()
3153
- }
3154
- }
3155
- },
3156
- ...manifest.chrome_settings_overrides.startup_pages && {
3157
- startup_pages: manifest.chrome_settings_overrides.startup_pages.map((page, index)=>getFilename(`chrome_settings_overrides/startup-${index}.html`, page))
3158
- }
3159
- }
3160
- };
3161
- }
3162
- const theme_experiment_getBasename = (filepath)=>__rspack_external_node_path_c5b9b54f.basename(filepath);
3163
- function themeExperiment(manifest) {
3164
- const te = manifest.theme_experiment;
3165
- return te && {
3166
- theme_experiment: {
3167
- ...te,
3168
- ...'string' == typeof te.stylesheet && {
3169
- stylesheet: getFilename(`theme_experiment/${theme_experiment_getBasename(te.stylesheet)}`, te.stylesheet)
3170
- },
3171
- ...Array.isArray(te.stylesheets) && {
3172
- stylesheets: te.stylesheets.map((s, i)=>getFilename(`theme_experiment/stylesheet-${i}.css`, s))
3173
- }
3174
- }
3175
- };
3176
- }
3177
- function manifestV2(manifest) {
3178
- return {
3179
- ...background_background(manifest),
3180
- ...browserAction(manifest),
3181
- ...pageAction(manifest),
3182
- ...sidebarAction(manifest),
3183
- ...chromeSettingsOverrides(manifest),
3184
- ...themeExperiment(manifest)
3185
- };
3186
- }
3187
- function action(manifest) {
3188
- return manifest.action && {
3189
- action: {
3190
- ...manifest.action,
3191
- ...manifest.action.default_popup && {
3192
- default_popup: getFilename("action/index.html", manifest.action.default_popup)
3193
- },
3194
- ...manifest.action.default_icon && {
3195
- default_icon: 'string' == typeof manifest.action.default_icon ? (()=>{
3196
- const raw = String(manifest.action.default_icon);
3197
- return getFilename(iconOutputPath(raw), raw);
3198
- })() : Object.fromEntries(Object.entries(manifest.action.default_icon).map(([size, icon])=>{
3199
- const raw = String(icon);
3200
- return [
3201
- size,
3202
- getFilename(iconOutputPath(raw), raw)
3203
- ];
3204
- }))
3205
- }
3206
- }
3207
- };
3208
- }
3209
- function backgroundServiceWorker(manifest) {
3210
- return manifest.background?.service_worker && {
3211
- background: {
3212
- service_worker: (()=>{
3213
- const raw = String(manifest.background.service_worker);
3214
- return getFilename('background/service_worker.js', raw);
3215
- })()
3216
- }
3217
- };
3218
- }
3219
- function declarativeNetRequest(manifest) {
3220
- return manifest.declarative_net_request && {
3221
- declarative_net_request: {
3222
- ...manifest.declarative_net_request,
3223
- rule_resources: manifest.declarative_net_request.rule_resources.map((resourceObj)=>({
3224
- ...resourceObj,
3225
- path: resourceObj.path && getFilename(`declarative_net_request/${resourceObj.id}.json`, resourceObj.path)
3226
- }))
3227
- }
3228
- };
3229
- }
3230
- function hostPermissions(manifest) {
3231
- return manifest.host_permissions && {
3232
- host_permissions: manifest.host_permissions
3233
- };
3234
- }
3235
- function sidePanel(manifest) {
3236
- return manifest.side_panel && {
3237
- side_panel: {
3238
- ...manifest.side_panel,
3239
- ...manifest.side_panel.default_path && {
3240
- default_path: (()=>{
3241
- const raw = String(manifest.side_panel.default_path);
3242
- return getFilename('sidebar/index.html', raw);
3243
- })()
3244
- }
3245
- }
3246
- };
3247
- }
3248
- function manifestV3(manifest) {
3249
- return {
3250
- ...action(manifest),
3251
- ...backgroundServiceWorker(manifest),
3252
- ...declarativeNetRequest(manifest),
3253
- ...hostPermissions(manifest),
3254
- ...sidePanel(manifest)
3255
- };
3256
- }
3257
- function getManifestOverrides(manifestPath, manifest) {
3258
- const manifestContent = manifest || JSON.parse(stripBom(__rspack_external_node_fs_5ea92f0c.readFileSync(manifestPath, 'utf8')));
3259
- const omit = (obj, key)=>{
3260
- if (!obj) return {};
3261
- const { [key]: _ignored, ...rest } = obj;
3262
- return rest;
3263
- };
3264
- const pickBackground = (obj)=>{
3265
- const value = obj.background;
3266
- return value && 'object' == typeof value ? value : {};
3267
- };
3268
- const common = manifestCommon(manifestContent, manifestPath);
3269
- const mv2 = manifestV2(manifestContent);
3270
- const mv3 = manifestV3(manifestContent);
3271
- const backgroundMerged = {
3272
- ...manifestContent.background || {},
3273
- ...pickBackground(common),
3274
- ...pickBackground(mv2),
3275
- ...pickBackground(mv3)
3276
- };
3277
- const merged = {
3278
- ...manifestContent,
3279
- ...omit(common, 'background'),
3280
- ...omit(mv2, 'background'),
3281
- ...omit(mv3, 'background')
3282
- };
3283
- if (Object.keys(backgroundMerged).length) merged.background = backgroundMerged;
3284
- return JSON.stringify(merged, null, 2);
3285
- }
3286
- const manifest_cjsRequire = createRequire(import.meta.url);
3287
- const INTERNAL_MANIFEST_SOURCE = '__extensionjs_manifest_source__';
3288
- const INTERNAL_MANIFEST_CURRENT_SOURCE = '__extensionjs_manifest_current_source__';
3289
- function readAssetSource(asset) {
3290
- if (!asset) return '';
3291
- const source = asset.source;
3292
- if ('string' == typeof source) return source;
3293
- if ('function' == typeof source) {
3294
- const out = source();
3295
- return 'string' == typeof out ? out : String(out || '');
3296
- }
3297
- if (source && 'function' == typeof source.source) {
3298
- const out = source.source();
3299
- return 'string' == typeof out ? out : String(out || '');
3300
- }
3301
- return '';
3302
- }
3303
- function setOriginalManifestContent(compilation, source) {
3304
- compilation[INTERNAL_MANIFEST_SOURCE] = source;
3305
- }
3306
- function getOriginalManifestContent(compilation) {
3307
- return compilation[INTERNAL_MANIFEST_SOURCE];
3308
- }
3309
- function setCurrentManifestContent(compilation, source) {
3310
- compilation[INTERNAL_MANIFEST_CURRENT_SOURCE] = source;
3311
- }
3312
- function getCurrentManifestContent(compilation) {
3313
- return compilation[INTERNAL_MANIFEST_CURRENT_SOURCE];
3314
- }
3315
- function getManifestContent(compilation, manifestPath) {
3316
- const currentManifest = getCurrentManifestContent(compilation);
3317
- if (currentManifest) return parseJsonSafe(currentManifest);
3318
- const getAsset = compilation.getAsset;
3319
- if ('function' == typeof getAsset) {
3320
- const manifestAsset = getAsset.call(compilation, 'manifest.json');
3321
- const manifest = readAssetSource(manifestAsset);
3322
- if (manifest) return parseJsonSafe(manifest);
3323
- }
3324
- const manifestAsset = compilation.assets?.['manifest.json'];
3325
- if (manifestAsset) {
3326
- const manifest = readAssetSource(manifestAsset);
3327
- if (manifest) return parseJsonSafe(manifest);
3328
- }
3329
- const originalManifest = getOriginalManifestContent(compilation);
3330
- if (originalManifest) return parseJsonSafe(originalManifest);
3331
- try {
3332
- const text = __rspack_external_node_fs_5ea92f0c.readFileSync(manifestPath, 'utf8');
3333
- return parseJsonSafe(text);
3334
- } catch {
3335
- try {
3336
- const resolved = manifest_cjsRequire.resolve(manifestPath);
3337
- delete manifest_cjsRequire.cache[resolved];
3338
- } catch {}
3339
- return manifest_cjsRequire(manifestPath);
3340
- }
3341
- }
3342
- function buildCanonicalManifest(manifestPath, manifest, browser) {
3343
- const filteredManifest = filterKeysForThisBrowser(manifest, browser);
3344
- return {
3345
- ...filteredManifest,
3346
- ...JSON.parse(getManifestOverrides(manifestPath, filteredManifest))
3347
- };
3348
- }
3349
- function createIssue(compiler, message, type = 'error') {
3350
- const ErrorCtor = compiler?.rspack?.WebpackError || Error;
3351
- const issue = new ErrorCtor(message);
3352
- issue.name = 'warning' === type ? 'ExtensionWarning' : 'ExtensionError';
3353
- return issue;
3354
- }
3355
- function reportToCompilation(compilation, compiler, message, type = 'error', file) {
3356
- const issue = createIssue(compiler, message, type);
3357
- if (file) issue.file = file;
3358
- const bucket = 'warning' === type ? 'warnings' : 'errors';
3359
- compilation[bucket] ||= [];
3360
- const existing = compilation[bucket];
3361
- const already = existing.some((e)=>(e?.file || '') === (issue.file || '') && String(e?.message) === String(issue.message));
3362
- if (already) return;
3363
- compilation[bucket].push(issue);
3364
- }
3365
- function backgroundIsRequiredMessageOnly(backgroundChunkName) {
3366
- return `Check the ${pintor.yellow(backgroundChunkName.replace('/', '.'))} field in your ${pintor.yellow('manifest.json')} file.`;
3367
- }
3368
- class SetupBackgroundEntry {
3369
- manifestPath;
3370
- browser;
3371
- constructor(options){
3372
- this.manifestPath = options.manifestPath;
3373
- this.browser = options.browser || 'chrome';
3374
- }
3375
- getMissingBackgroundError(bgFile) {
3376
- if (!__rspack_external_node_fs_5ea92f0c.existsSync(bgFile) && this.manifestPath) {
3377
- const manifest = JSON.parse(stripBom(__rspack_external_node_fs_5ea92f0c.readFileSync(this.manifestPath, 'utf8')));
3378
- const patched = filterKeysForThisBrowser(manifest, this.browser);
3379
- const fieldKey = 3 === patched.manifest_version ? 'background/service_worker' : "background/scripts";
3380
- return backgroundIsRequiredMessageOnly(fieldKey);
3381
- }
3382
- }
3383
- addDefaultEntry(compiler, name, defaultScript) {
3384
- compiler.options.entry = {
3385
- ...compiler.options.entry,
3386
- [name]: {
3387
- import: [
3388
- defaultScript
3389
- ]
3390
- }
3391
- };
3392
- }
3393
- apply(compiler) {
3394
- const manifest = JSON.parse(stripBom(__rspack_external_node_fs_5ea92f0c.readFileSync(this.manifestPath, 'utf-8')));
3395
- const browser = this.browser;
3396
- const isGecko = isGeckoBasedBrowser(String(browser));
3397
- const minimumBgScript = resolveDevelopDistFile(isGecko ? 'minimum-firefox-file' : 'minimum-chromium-file');
3398
- const dirname = __rspack_external_node_path_c5b9b54f.dirname(this.manifestPath);
3399
- const filteredManifest = filterKeysForThisBrowser(manifest, browser) || manifest;
3400
- const manifestBg = filteredManifest?.background ?? manifest.background;
3401
- const manifestVersion = filteredManifest?.manifest_version ?? manifest.manifest_version;
3402
- if (isStaticTheme(filteredManifest)) return;
3403
- function hookError(maybeError) {
3404
- compiler.hooks.thisCompilation.tap('run-chromium:setup-background-entry', (compilation)=>{
3405
- reportToCompilation(compilation, compiler, maybeError, 'error');
3406
- });
3407
- }
3408
- if (isGecko) {
3409
- const bgScripts = manifestBg?.scripts;
3410
- if (bgScripts && bgScripts.length > 0) {
3411
- const bgScriptPath = __rspack_external_node_path_c5b9b54f.join(dirname, bgScripts[0]);
3412
- const maybeError = this.getMissingBackgroundError(bgScriptPath);
3413
- if (maybeError) hookError(maybeError);
3414
- } else this.addDefaultEntry(compiler, "background/script", minimumBgScript);
3415
- return;
3416
- }
3417
- if (2 === manifestVersion) {
3418
- const bgScripts = manifestBg?.scripts;
3419
- if (bgScripts && bgScripts.length > 0) {
3420
- const bgScriptPath = __rspack_external_node_path_c5b9b54f.join(dirname, bgScripts[0]);
3421
- const maybeError = this.getMissingBackgroundError(bgScriptPath);
3422
- if (maybeError) hookError(maybeError);
3423
- } else this.addDefaultEntry(compiler, "background/script", minimumBgScript);
3424
- } else {
3425
- const serviceWorker = manifestBg?.service_worker;
3426
- if (serviceWorker) {
3427
- const swPath = __rspack_external_node_path_c5b9b54f.join(dirname, serviceWorker);
3428
- const maybeError = this.getMissingBackgroundError(swPath);
3429
- if (maybeError) hookError(maybeError);
3430
- const existingEntry = compiler.options.entry && 'background/service_worker' in compiler.options.entry ? compiler.options.entry['background/service_worker'] : void 0;
3431
- if (!existingEntry && __rspack_external_node_fs_5ea92f0c.existsSync(swPath)) this.addDefaultEntry(compiler, 'background/service_worker', swPath);
3432
- } else this.addDefaultEntry(compiler, 'background/service_worker', minimumBgScript);
3433
- }
3434
- }
3435
- }
3436
- function TemplateFn(compilation, Template) {
3437
- return {
3438
- f: (args, body)=>{
3439
- if (!compilation) throw new TypeError('No compilation is found.');
3440
- if (compilation.runtimeTemplate) return compilation.runtimeTemplate.basicFunction(args, body);
3441
- return compilation.outputOptions.environment.arrowFunction ? `(${args}) => {\n${Template.indent(body)}\n}` : `function(${args}) {\n${Template.indent(body)}\n}`;
3442
- },
3443
- retF: (returnValue, args = '')=>{
3444
- if (!compilation) throw new TypeError('No compilation is found.');
3445
- if (compilation.runtimeTemplate) return compilation.runtimeTemplate.returningFunction(returnValue, args);
3446
- return compilation.outputOptions.environment.arrowFunction ? `(${args}) => (${returnValue})` : `function(${args}) { return ${returnValue}; }`;
3447
- }
3448
- };
3449
- }
3450
- const RuntimeGlobal = '__webpack_require__.webExtRt';
3451
- const RuntimeGlobalIsBrowser = '__webpack_require__.webExtRtModern';
3452
- function BrowserRuntimeModule(webpack, acceptWeak) {
3453
- const { RuntimeModule, Template } = webpack;
3454
- class BrowserRuntime extends RuntimeModule {
3455
- constructor(){
3456
- super('WebExtensionBrowserRuntime', RuntimeModule.STAGE_NORMAL);
3457
- }
3458
- generate() {
3459
- const { compilation } = this;
3460
- if (!compilation) return Template.asString('/* [webpack-target-webextension] BrowserRuntimeModule skipped because no compilation is found. */');
3461
- const optionalChaining = compilation.outputOptions.environment.optionalChaining;
3462
- const _let = compilation.outputOptions.environment.const ? 'let' : 'var';
3463
- return Template.asString([
3464
- `${_let} isChrome, runtime;`,
3465
- 'try {',
3466
- Template.indent([
3467
- `if (typeof globalThis === "object" && globalThis && typeof globalThis.browser !== "undefined" && ${optionalChaining ? 'typeof globalThis.browser.runtime?.getURL === "function"' : 'typeof globalThis.browser.runtime === "object" && typeof globalThis.browser.runtime.getURL === "function"'}) {`,
3468
- Template.indent([
3469
- 'runtime = globalThis.browser;'
3470
- ]),
3471
- '}'
3472
- ]),
3473
- '} catch (_) {}',
3474
- 'if (!runtime) {',
3475
- Template.indent([
3476
- 'try {',
3477
- Template.indent([
3478
- `if (typeof globalThis === "object" && globalThis && typeof globalThis.chrome !== "undefined" && ${optionalChaining ? 'typeof globalThis.chrome.runtime?.getURL === "function"' : 'typeof globalThis.chrome.runtime === "object" && typeof globalThis.chrome.runtime.getURL === "function"'}) {`,
3479
- Template.indent([
3480
- 'isChrome = true;',
3481
- 'runtime = globalThis.chrome;'
3482
- ]),
3483
- '}'
3484
- ]),
3485
- '} catch (_) {}'
3486
- ]),
3487
- '}',
3488
- `${RuntimeGlobalIsBrowser} = !isChrome;`,
3489
- `${RuntimeGlobal} = runtime || { runtime: null };`,
3490
- acceptWeak ? 'if (!runtime && (typeof self !== "object" || !self.addEventListener)) {' : '',
3491
- acceptWeak ? Template.indent([
3492
- `${RuntimeGlobal} = { runtime: { getURL: String } };`
3493
- ]) : '',
3494
- acceptWeak ? '}' : ''
3495
- ].filter(Boolean));
3496
- }
3497
- }
3498
- return new BrowserRuntime();
3499
- }
3500
- const DYNAMIC_IMPORT_LOADER = 'dynamicImportLoader';
3501
- const DOM_LOADER = "scriptLoader";
3502
- const WORKER_LOADER = 'workerLoader';
3503
- const CLASSIC_LOADER = 'classicLoader';
3504
- const MAIN_CLASSIC_LOADER = 'classicLoaderMainWorld';
3505
- const FALLBACK_LOADER = 'fallbackLoader';
3506
- function LoadScriptRuntimeModule(webpack, supportDynamicImport, classicLoaderEnabled, contentScriptsMeta) {
3507
- const { Template, RuntimeGlobals, RuntimeModule } = webpack;
3508
- class LoadScriptRuntime extends RuntimeModule {
3509
- supportDynamicImport;
3510
- classicLoaderEnabled;
3511
- contentScriptsMeta;
3512
- constructor(){
3513
- super("load script", RuntimeModule.STAGE_BASIC);
3514
- this.supportDynamicImport = Boolean(supportDynamicImport);
3515
- this.classicLoaderEnabled = Boolean(classicLoaderEnabled);
3516
- this.contentScriptsMeta = contentScriptsMeta || {};
3517
- }
3518
- generate() {
3519
- const { compilation } = this;
3520
- if (!compilation) return Template.asString('/* [webpack-target-webextension] ChunkLoaderFallbackRuntimeModule skipped because no compilation is found. */');
3521
- const { f } = TemplateFn(compilation, Template);
3522
- const _const = compilation.outputOptions.environment.const ? 'const' : 'var';
3523
- const _let = compilation.outputOptions.environment.const ? 'let' : 'var';
3524
- const chunkName = (this.chunk?.name ? this.chunk.name : '') || (compilation && compilation.chunk && compilation.chunk.name ? compilation.chunk.name : '');
3525
- const bundleId = chunkName ? `${chunkName}.js` : '';
3526
- const world = bundleId && this.contentScriptsMeta[bundleId] ? this.contentScriptsMeta[bundleId].world : void 0;
3527
- const isMainWorld = 'main' === world;
3528
- const HasExtensionRuntime = `${_const} hasExtensionRuntime = (function(){ try {return ((typeof globalThis === "object" && globalThis && globalThis.browser && globalThis.browser.runtime && typeof globalThis.browser.runtime.sendMessage === "function") || (typeof globalThis === "object" && globalThis && globalThis.chrome && globalThis.chrome.runtime && typeof globalThis.chrome.runtime.sendMessage === "function"));} catch (e) { return false; } })();`;
3529
- const DynamicImportLoader = `${_const} ${DYNAMIC_IMPORT_LOADER} = ` + f('url, done, key, chunkId', `import(url).then(${f('', [
3530
- 'if (isNotIframe) return done();',
3531
- 'try {',
3532
- Template.indent([
3533
- "// It's a Chrome bug, if the import() is called in a sandboxed iframe, it _fails_ the script loading but _resolve_ the Promise.",
3534
- `// we call ${RuntimeGlobals.ensureChunkHandlers}.j(chunkId) to check if it is loaded.`,
3535
- '// if it is, this is a no-op. if it is not, it will throw a TypeError because this function requires 2 parameters.',
3536
- '// This call will not trigger the chunk loading because it is already loading.',
3537
- '// see https://github.com/awesome-webextension/webpack-target-webextension/issues/41',
3538
- `chunkId !== undefined && ${RuntimeGlobals.ensureChunkHandlers}.j(chunkId);`,
3539
- 'done();'
3540
- ]),
3541
- '}',
3542
- 'catch (_) {',
3543
- Template.indent([
3544
- 'if (!bug816121warned) {',
3545
- Template.indent([
3546
- 'console.warn("Chrome bug https://crbug.com/816121 hit.");',
3547
- 'bug816121warned = true;'
3548
- ]),
3549
- '}',
3550
- `return ${FALLBACK_LOADER}(url, done, key, chunkId);`
3551
- ]),
3552
- '}'
3553
- ])}, ${f('e', [
3554
- 'console.warn("Dynamic import loader failed. Using fallback loader (see https://github.com/awesome-webextension/webpack-target-webextension#content-script).", e);',
3555
- `${FALLBACK_LOADER}(url, done, key, chunkId);`
3556
- ])});`);
3557
- const DOMLoader = `${_const} ${DOM_LOADER} = ` + f('url, done', [
3558
- `${_const} script = document.createElement('script');`,
3559
- "script.src = url;",
3560
- "script.onload = done;",
3561
- "script.onerror = done;",
3562
- "document.body.appendChild(script);"
3563
- ]);
3564
- const WorkerLoader = `${_const} ${WORKER_LOADER} = ` + f('url, done', [
3565
- 'try {',
3566
- Template.indent([
3567
- 'importScripts(url);',
3568
- 'done();'
3569
- ]),
3570
- '} catch (e) {',
3571
- Template.indent([
3572
- 'done(e);'
3573
- ]),
3574
- '}'
3575
- ]);
3576
- const ClassicLoader = `${_const} ${CLASSIC_LOADER} = ` + f('url, done', Template.asString([
3577
- `${_const} msg = { type: "WTW_INJECT", file: url };`,
3578
- `${_const} onError = ${f('e', 'done(Object.assign(e, { type: "missing" }))')};`,
3579
- 'try {',
3580
- `if (${RuntimeGlobalIsBrowser}) {`,
3581
- Template.indent([
3582
- `${RuntimeGlobal}.runtime.sendMessage(msg).then(done, onError);`
3583
- ]),
3584
- '} else {',
3585
- Template.indent([
3586
- `${RuntimeGlobal}.runtime.sendMessage(msg, ${f('', [
3587
- `${_const} error = ${RuntimeGlobal}.runtime.lastError;`,
3588
- 'if (error) onError(error);',
3589
- 'else done();'
3590
- ])});`
3591
- ]),
3592
- '}',
3593
- '} catch (e) { onError(e); }'
3594
- ])) + ';';
3595
- const ClassicLoaderDisabled = `${_const} ${CLASSIC_LOADER} = ` + f('', [
3596
- "throw new Error(\"[webpack-target-webextension] Failed to load async chunk in the content script. No script loader is found. You can either\\n - Set output.environment.dynamicImport to true if your environment supports native ES Module\\n - Specify the background entry to enable the fallback loader\\n - Set module.parser.javascript.dynamicImportMode to 'eager' to inline all async chunks.\");"
3597
- ]);
3598
- return Template.asString([
3599
- this.supportDynamicImport ? `${_let} bug816121warned, isNotIframe;` : '',
3600
- this.supportDynamicImport ? Template.asString([
3601
- 'try {',
3602
- Template.indent([
3603
- 'isNotIframe = typeof window === "object" ? window.top === window : true;'
3604
- ]),
3605
- '} catch(e) {',
3606
- Template.indent([
3607
- 'isNotIframe = false /* CORS error */;'
3608
- ]),
3609
- '}'
3610
- ]) : '',
3611
- HasExtensionRuntime,
3612
- this.classicLoaderEnabled ? ClassicLoader : ClassicLoaderDisabled,
3613
- this.supportDynamicImport ? DynamicImportLoader : '',
3614
- DOMLoader,
3615
- WorkerLoader,
3616
- `${_const} isWorker = typeof importScripts === 'function'`,
3617
- "if (typeof location === 'object' && location.protocol.includes('-extension:')) {",
3618
- Template.indent([
3619
- `${RuntimeGlobals.loadScript} = isWorker ? ${WORKER_LOADER} : ${DOM_LOADER};`
3620
- ]),
3621
- "} else {",
3622
- Template.indent(isMainWorld ? Template.asString([
3623
- `${_const} __extjsMark = "__extjs__";`,
3624
- `${_const} __extjsReqType = "EXTJS_WTW_LOAD";`,
3625
- `${_const} __extjsResType = "EXTJS_WTW_LOADED";`,
3626
- `${_const} ${MAIN_CLASSIC_LOADER} = ` + f('url, done', [
3627
- `${_const} requestId = String(Date.now()) + "-" + Math.random().toString(16).slice(2);`,
3628
- `${_const} onMessage = ${f('event', [
3629
- 'try {',
3630
- Template.indent([
3631
- 'if (!event || event.source !== window) return;',
3632
- `${_const} data = event.data || null;`,
3633
- "if (!data || data[__extjsMark] !== true) return;",
3634
- "if (data.type !== __extjsResType || data.requestId !== requestId) return;",
3635
- 'window.removeEventListener("message", onMessage);',
3636
- 'if (data.ok) done();',
3637
- 'else done(Object.assign(new Error(data.error || "Bridge failed"), { type: "missing" }));'
3638
- ]),
3639
- '} catch (e) {',
3640
- Template.indent([
3641
- 'window.removeEventListener("message", onMessage);',
3642
- 'done(Object.assign(e, { type: "missing" }));'
3643
- ]),
3644
- '}'
3645
- ])};`,
3646
- 'window.addEventListener("message", onMessage);',
3647
- 'try {',
3648
- Template.indent([
3649
- 'window.postMessage({ [__extjsMark]: true, type: __extjsReqType, requestId: requestId, url: url }, "*");'
3650
- ]),
3651
- '} catch (e) {',
3652
- Template.indent([
3653
- 'window.removeEventListener("message", onMessage);',
3654
- 'done(Object.assign(e, { type: "missing" }));'
3655
- ]),
3656
- '}',
3657
- 'setTimeout(function(){ try { window.removeEventListener("message", onMessage); } catch(_){} done(Object.assign(new Error("Bridge timeout"), { type: "missing" })); }, 5000);'
3658
- ]) + ';',
3659
- `if (!isWorker) ${RuntimeGlobals.loadScript} = ${MAIN_CLASSIC_LOADER};`
3660
- ]) : Template.asString([
3661
- `if (!isWorker && hasExtensionRuntime) ${RuntimeGlobals.loadScript} = ${CLASSIC_LOADER};`,
3662
- `else if (!isWorker) ${RuntimeGlobals.loadScript} = ${DOM_LOADER};`
3663
- ])),
3664
- "}",
3665
- this.supportDynamicImport ? `${_const} ${FALLBACK_LOADER} = ${RuntimeGlobals.loadScript};` : '',
3666
- this.supportDynamicImport ? `${RuntimeGlobals.loadScript} = ${DYNAMIC_IMPORT_LOADER};` : ''
3667
- ].filter(Boolean));
3668
- }
3669
- }
3670
- return new LoadScriptRuntime();
3671
- }
3672
- function PublicPathRuntimeModule(webpack) {
3673
- const { RuntimeGlobals, RuntimeModule, Template } = webpack;
3674
- class PublicPathRuntime extends RuntimeModule {
3675
- constructor(){
3676
- super('publicPath', RuntimeModule.STAGE_BASIC);
3677
- }
3678
- generate() {
3679
- const { compilation } = this;
3680
- if (!compilation) return Template.asString('/* [webpack-target-webextension] PublicPathRuntimeModule skipped because no compilation is found. */');
3681
- const { publicPath } = compilation.outputOptions;
3682
- const path = JSON.stringify(compilation.getPath(publicPath || '', {
3683
- hash: compilation.hash || 'XXXX'
3684
- }));
3685
- return Template.asString([
3686
- 'var __extjsBase = (typeof globalThis === "object" && globalThis && globalThis.__EXTJS_EXTENSION_BASE__) ? String(globalThis.__EXTJS_EXTENSION_BASE__) : "";',
3687
- 'if (!__extjsBase && typeof document === "object" && document && document.documentElement) {',
3688
- Template.indent([
3689
- 'try { __extjsBase = document.documentElement.getAttribute("data-extjs-extension-base") || ""; } catch(_) { __extjsBase = ""; }'
3690
- ]),
3691
- "}",
3692
- 'var __extjsRuntimePath = "";',
3693
- `if (${RuntimeGlobal} && ${RuntimeGlobal}.runtime && typeof ${RuntimeGlobal}.runtime.getURL === "function") {`,
3694
- Template.indent([
3695
- `try { __extjsRuntimePath = ${RuntimeGlobal}.runtime.getURL(${path}); } catch (_) { __extjsRuntimePath = ""; }`
3696
- ]),
3697
- "}",
3698
- "if (__extjsRuntimePath) {",
3699
- Template.indent([
3700
- `${RuntimeGlobals.publicPath} = __extjsRuntimePath;`
3701
- ]),
3702
- "} else if (__extjsBase) {",
3703
- Template.indent([
3704
- `${RuntimeGlobals.publicPath} = __extjsBase.replace(/\\/+$/, "/") + String(${path}).replace(/^\\/+/, "");`
3705
- ]),
3706
- "} else {",
3707
- Template.indent([
3708
- `${RuntimeGlobals.publicPath} = "";`
3709
- ]),
3710
- "}",
3711
- 'if (!__extjsBase && typeof document === "object" && document && document.documentElement) {',
3712
- Template.indent([
3713
- "try {",
3714
- Template.indent([
3715
- "var __extjsRetries = 0;",
3716
- "var __extjsUpdateBase = function(){",
3717
- Template.indent([
3718
- 'var base = "";',
3719
- 'try { base = document.documentElement.getAttribute("data-extjs-extension-base") || ""; } catch(_) { base = ""; }',
3720
- "if (base) {",
3721
- Template.indent([
3722
- `${RuntimeGlobals.publicPath} = base.replace(/\\/+$/, "/") + String(${path}).replace(/^\\/+/, "");`
3723
- ]),
3724
- "} else if (__extjsRetries++ < 50) {",
3725
- Template.indent([
3726
- "setTimeout(__extjsUpdateBase, 100);"
3727
- ]),
3728
- "}"
3729
- ]),
3730
- "};",
3731
- "__extjsUpdateBase();"
3732
- ]),
3733
- "} catch (_) {}"
3734
- ]),
3735
- "}"
3736
- ]);
3737
- }
3738
- }
3739
- return new PublicPathRuntime();
3740
- }
3741
- function AutoPublicPathRuntimeModule(webpack) {
3742
- const { RuntimeModule, RuntimeGlobals, Template, javascript: { JavascriptModulesPlugin }, HotUpdateChunk } = webpack;
3743
- const getChunkFilenameTemplate = JavascriptModulesPlugin.getChunkFilenameTemplate || function(chunk, outputOptions) {
3744
- if (chunk.filenameTemplate) return chunk.filenameTemplate;
3745
- if ('function' == typeof HotUpdateChunk && chunk instanceof HotUpdateChunk) return outputOptions.hotUpdateChunkFilename;
3746
- if (chunk.canBeInitial()) return outputOptions.filename;
3747
- return outputOptions.chunkFilename;
3748
- };
3749
- class AutoPublicPathRuntime extends RuntimeModule {
3750
- constructor(){
3751
- super('publicPath', RuntimeModule.STAGE_BASIC);
3752
- }
3753
- generate() {
3754
- const { compilation } = this;
3755
- if (!compilation || !this.chunk) return Template.asString('/* [webpack-target-webextension] AutoPublicPathRuntimeModule skipped because no compilation/chunk is found. */');
3756
- const { scriptType, importMetaName } = compilation.outputOptions;
3757
- const chunkName = compilation.getPath(getChunkFilenameTemplate(this.chunk, compilation.outputOptions), {
3758
- chunk: this.chunk,
3759
- contentHashType: "javascript"
3760
- });
3761
- const outputPath = compilation.outputOptions.path;
3762
- if (!outputPath) return Template.asString('/* [webpack-target-webextension] AutoPublicPathRuntimeModule skipped because no output path is found. */');
3763
- const undoPath = getUndoPath(chunkName, outputPath, false);
3764
- const optionalChaining = compilation.outputOptions.environment.optionalChaining;
3765
- const _const = compilation.outputOptions.environment.const ? 'const' : 'var';
3766
- const _let = compilation.outputOptions.environment.const ? 'let' : 'var';
3767
- return Template.asString([
3768
- `${_let} scriptUrl;`,
3769
- 'module' === scriptType ? `if (typeof ${importMetaName}.url === "string") scriptUrl = ${importMetaName}.url;` : Template.asString([
3770
- `if (${RuntimeGlobals.global}.importScripts) scriptUrl = ${RuntimeGlobals.global}.location + "";`,
3771
- `${_const} document = ${RuntimeGlobals.global}.document;`,
3772
- `if (!scriptUrl && ${optionalChaining ? 'document?.currentScript' : 'document && document.currentScript'}) {`,
3773
- Template.indent("scriptUrl = document.currentScript.src;"),
3774
- '}'
3775
- ]),
3776
- '// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration',
3777
- '// or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.',
3778
- 'var __extjsBase = (typeof globalThis === "object" && globalThis && globalThis.__EXTJS_EXTENSION_BASE__) ? String(globalThis.__EXTJS_EXTENSION_BASE__) : "";',
3779
- 'if (!__extjsBase && typeof document === "object" && document && document.documentElement) {',
3780
- Template.indent([
3781
- 'try { __extjsBase = document.documentElement.getAttribute("data-extjs-extension-base") || ""; } catch(_) { __extjsBase = ""; }'
3782
- ]),
3783
- "}",
3784
- "if (__extjsBase && scriptUrl && !/^((chrome|moz)-extension):///.test(scriptUrl)) {",
3785
- Template.indent([
3786
- "scriptUrl = __extjsBase;"
3787
- ]),
3788
- "}",
3789
- "if (!scriptUrl) {",
3790
- Template.indent([
3791
- 'var __extjsRuntimeRoot = "";',
3792
- `if (${RuntimeGlobal} && ${RuntimeGlobal}.runtime && typeof ${RuntimeGlobal}.runtime.getURL === "function") {`,
3793
- Template.indent(`try { __extjsRuntimeRoot = ${RuntimeGlobal}.runtime.getURL("/"); } catch (_) { __extjsRuntimeRoot = ""; }`),
3794
- Template.indent("scriptUrl = __extjsRuntimeRoot || scriptUrl;"),
3795
- "} else {",
3796
- Template.indent('scriptUrl = __extjsBase || "";'),
3797
- "}"
3798
- ]),
3799
- '}',
3800
- 'scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\\?.*$/, "").replace(/\\/[^\\/]+$/, "/");',
3801
- undoPath ? `${RuntimeGlobals.publicPath} = scriptUrl + ${JSON.stringify(undoPath)};` : `${RuntimeGlobals.publicPath} = scriptUrl;`,
3802
- 'if (!scriptUrl && typeof document === "object" && document && document.documentElement) {',
3803
- Template.indent([
3804
- "try {",
3805
- Template.indent([
3806
- "var __extjsRetries = 0;",
3807
- "var __extjsUpdateBase = function(){",
3808
- Template.indent([
3809
- 'var base = "";',
3810
- 'try { base = document.documentElement.getAttribute("data-extjs-extension-base") || ""; } catch(_) { base = ""; }',
3811
- "if (base) {",
3812
- Template.indent([
3813
- 'var normalized = base.replace(/\\/+$/, "/");',
3814
- undoPath ? `${RuntimeGlobals.publicPath} = normalized + ${JSON.stringify(undoPath)};` : `${RuntimeGlobals.publicPath} = normalized;`
3815
- ]),
3816
- "} else if (__extjsRetries++ < 50) {",
3817
- Template.indent([
3818
- "setTimeout(__extjsUpdateBase, 100);"
3819
- ]),
3820
- "}"
3821
- ]),
3822
- "};",
3823
- "__extjsUpdateBase();"
3824
- ]),
3825
- "} catch (_) {}"
3826
- ]),
3827
- "}"
3828
- ]);
3829
- }
3830
- }
3831
- return new AutoPublicPathRuntime();
3832
- }
3833
- function getUndoPath(filename, outputPath, enforceRelative) {
3834
- let depth = -1;
3835
- let append = '';
3836
- outputPath = outputPath.replace(/[\\/]$/, '');
3837
- for (const part of filename.split(/[/\\]+/))if ('..' === part) if (depth > -1) depth--;
3838
- else {
3839
- const i = outputPath.lastIndexOf('/');
3840
- const j = outputPath.lastIndexOf('\\');
3841
- const pos = i < 0 ? j : j < 0 ? i : Math.max(i, j);
3842
- if (pos < 0) return `${outputPath}/`;
3843
- append = `${outputPath.slice(pos + 1)}/${append}`;
3844
- outputPath = outputPath.slice(0, pos);
3845
- }
3846
- else if ('.' !== part) depth++;
3847
- return depth > 0 ? `${'../'.repeat(depth)}${append}` : enforceRelative ? `./${append}` : append;
3848
- }
3849
- function BaseUriRuntimeModule(webpack) {
3850
- const { RuntimeGlobals, RuntimeModule, Template } = webpack;
3851
- class BaseUriRuntime extends RuntimeModule {
3852
- constructor(){
3853
- super('baseURI', RuntimeModule.STAGE_BASIC);
3854
- }
3855
- generate() {
3856
- return Template.asString([
3857
- 'var __extjsBase = "";',
3858
- "try {",
3859
- Template.indent([
3860
- `if (${RuntimeGlobal} && ${RuntimeGlobal}.runtime && typeof ${RuntimeGlobal}.runtime.getURL === "function") {`,
3861
- Template.indent([
3862
- `__extjsBase = String(${RuntimeGlobal}.runtime.getURL("/"));`
3863
- ]),
3864
- "}"
3865
- ]),
3866
- "} catch (_) {}",
3867
- 'if (!__extjsBase && typeof globalThis === "object" && globalThis && globalThis.__EXTJS_EXTENSION_BASE__) {',
3868
- Template.indent([
3869
- '__extjsBase = String(globalThis.__EXTJS_EXTENSION_BASE__ || "");'
3870
- ]),
3871
- "}",
3872
- 'if (!__extjsBase && typeof document === "object" && document && document.documentElement) {',
3873
- Template.indent([
3874
- 'try { __extjsBase = String(document.documentElement.getAttribute("data-extjs-extension-base") || ""); } catch (_) { __extjsBase = ""; }'
3875
- ]),
3876
- "}",
3877
- "if (__extjsBase) {",
3878
- Template.indent([
3879
- `${RuntimeGlobals.baseURI} = __extjsBase;`
3880
- ]),
3881
- '} else if (typeof document !== "undefined" && document.baseURI) {',
3882
- Template.indent([
3883
- `${RuntimeGlobals.baseURI} = document.baseURI;`
3884
- ]),
3885
- "} else {",
3886
- Template.indent([
3887
- `${RuntimeGlobals.baseURI} = self.location.href;`
3888
- ]),
3889
- "}"
3890
- ]);
3891
- }
3892
- }
3893
- return new BaseUriRuntime();
3894
- }
3895
- function ChunkLoaderFallbackRuntimeModule(webpack) {
3896
- const { RuntimeModule, Template } = webpack;
3897
- class ChunkLoaderFallbackRuntime extends RuntimeModule {
3898
- constructor(){
3899
- super('chunk loader fallback', RuntimeModule.STAGE_TRIGGER);
3900
- }
3901
- generate() {
3902
- const { compilation } = this;
3903
- if (!compilation) return Template.asString('/* [webpack-target-webextension] ChunkLoaderFallbackRuntimeModule skipped because no compilation is found. */');
3904
- const { f } = TemplateFn(compilation, Template);
3905
- const optionalChain = compilation.outputOptions.environment.optionalChaining;
3906
- const _const = compilation.outputOptions.environment.const ? 'const' : 'var';
3907
- const _let = compilation.outputOptions.environment.const ? 'let' : 'var';
3908
- return `if (${RuntimeGlobal} && ${RuntimeGlobal}.runtime && ${RuntimeGlobal}.runtime.onMessage) {${RuntimeGlobal}.runtime.onMessage.addListener(` + f('message, sender, sendResponse', [
3909
- optionalChain ? 'if (message?.type != "WTW_INJECT" || typeof sender?.tab?.id != "number") return;' : 'if (!message || message.type != "WTW_INJECT" || !sender || !sender.tab || sender.tab.id == null) return;',
3910
- `${_let} file = message.file;`,
3911
- 'try {',
3912
- Template.indent([
3913
- 'file = new URL(file).pathname;'
3914
- ]),
3915
- '} catch (_) {}',
3916
- 'if (!file) return;',
3917
- `if (${RuntimeGlobal}.scripting) {`,
3918
- Template.indent([
3919
- `${RuntimeGlobal}.scripting.executeScript({`,
3920
- Template.indent([
3921
- 'target: { tabId: sender.tab.id, frameIds: [sender.frameId] },',
3922
- 'files: [file],'
3923
- ]),
3924
- '}).then(sendResponse);'
3925
- ]),
3926
- "} else {",
3927
- Template.indent([
3928
- `${_const} details = { frameId: sender.frameId, file, matchAboutBlank: true };`,
3929
- `if (${RuntimeGlobalIsBrowser}) {`,
3930
- `${RuntimeGlobal}.tabs.executeScript(sender.tab.id, details).then(sendResponse);`,
3931
- '} else {',
3932
- `${RuntimeGlobal}.tabs.executeScript(sender.tab.id, details, sendResponse);`,
3933
- '}'
3934
- ]),
3935
- '}',
3936
- 'return true;'
3937
- ]) + ");}";
3938
- }
3939
- }
3940
- return new ChunkLoaderFallbackRuntime();
3941
- }
3942
- class WebExtensionChuckLoaderRuntimePlugin {
3943
- pluginOptions;
3944
- options;
3945
- contentScriptsMeta;
3946
- rspackAutoPublicPath = false;
3947
- constructor(options){
3948
- this.pluginOptions = options || {};
3949
- this.options = this.pluginOptions.background || this.pluginOptions || {};
3950
- this.contentScriptsMeta = this.pluginOptions.contentScriptsMeta || {};
3951
- }
3952
- apply(compiler) {
3953
- if ('rspack' in compiler) {
3954
- const { output } = compiler.options;
3955
- if (void 0 === output.publicPath || 'auto' === output.publicPath) {
3956
- output.publicPath = '';
3957
- this.rspackAutoPublicPath = true;
3958
- }
3959
- }
3960
- compiler.hooks.compilation.tap(WebExtensionChuckLoaderRuntimePlugin.name, (compilation)=>{
3961
- this.tap(compiler, compilation);
3962
- });
3963
- }
3964
- tap(compiler, compilation) {
3965
- const { RuntimeGlobals } = compiler.webpack;
3966
- const { options } = this;
3967
- compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.loadScript).tap(WebExtensionChuckLoaderRuntimePlugin.name, (_chunk, set)=>{
3968
- set.add(RuntimeGlobal);
3969
- compilation.addRuntimeModule(_chunk, LoadScriptRuntimeModule(compiler.webpack, false !== compilation.outputOptions.environment.dynamicImport, false !== options.classicLoader, this.contentScriptsMeta));
3970
- return true;
3971
- });
3972
- compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.baseURI).tap(WebExtensionChuckLoaderRuntimePlugin.name, (chunk, set)=>{
3973
- set.add(RuntimeGlobal);
3974
- compilation.addRuntimeModule(chunk, BaseUriRuntimeModule(compiler.webpack));
3975
- return true;
3976
- });
3977
- compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.publicPath).tap(WebExtensionChuckLoaderRuntimePlugin.name, (chunk, set)=>{
3978
- const { outputOptions } = compilation;
3979
- const { publicPath, scriptType } = outputOptions;
3980
- if ('auto' === publicPath || '' === publicPath && this.rspackAutoPublicPath) {
3981
- const module = AutoPublicPathRuntimeModule(compiler.webpack);
3982
- set.add(RuntimeGlobal);
3983
- if ('module' !== scriptType) set.add(RuntimeGlobals.global);
3984
- compilation.addRuntimeModule(chunk, module);
3985
- } else {
3986
- set.add(RuntimeGlobal);
3987
- const module = PublicPathRuntimeModule(compiler.webpack);
3988
- if ('string' != typeof publicPath || /\[(full)?hash\]/.test(publicPath)) module.fullHash = true;
3989
- compilation.addRuntimeModule(chunk, module);
3990
- }
3991
- return true;
3992
- });
3993
- if (false !== options.classicLoader) compilation.hooks.afterOptimizeModules.tap(WebExtensionChuckLoaderRuntimePlugin.name, ()=>{
3994
- const { pageEntry, serviceWorkerEntry } = options;
3995
- const entryPoint1 = pageEntry && compilation.entrypoints.get(pageEntry);
3996
- const entryPoint2 = serviceWorkerEntry && compilation.entrypoints.get(serviceWorkerEntry);
3997
- if (entryPoint1) compilation.addRuntimeModule(entryPoint1.chunks[0], ChunkLoaderFallbackRuntimeModule(compiler.webpack));
3998
- if (entryPoint2) compilation.addRuntimeModule(entryPoint2.chunks[0], ChunkLoaderFallbackRuntimeModule(compiler.webpack));
3999
- });
4000
- }
4001
- }
4002
- class NoDangerNamePlugin {
4003
- apply(compiler) {
4004
- const { Compilation, WebpackError } = compiler.webpack;
4005
- compiler.hooks.thisCompilation.tap(NoDangerNamePlugin.name, (compilation)=>{
4006
- compilation.hooks.processAssets.tap({
4007
- name: NoDangerNamePlugin.name,
4008
- stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
4009
- }, (assets)=>{
4010
- for (const name of Object.keys(assets || {})){
4011
- if (!name.startsWith('_')) continue;
4012
- if (name.startsWith('_locales/') || name.startsWith('_metadata/')) continue;
4013
- const e = new WebpackError(`[webpack-extension-target] Output file name ${JSON.stringify(name)} starts with "_" which is prohibited.`);
4014
- e.stack = '';
4015
- compilation.errors.push(e);
4016
- }
4017
- });
4018
- });
4019
- }
4020
- }
4021
- function createEagerlyLoadChunksRuntimeModule(webpack) {
4022
- const { RuntimeGlobals, RuntimeModule, Template } = webpack;
4023
- return class extends RuntimeModule {
4024
- chunkIds;
4025
- constructor(chunkIds){
4026
- super('eagerly load chunks', RuntimeModule.STAGE_NORMAL);
4027
- this.chunkIds = chunkIds;
4028
- }
4029
- generate() {
4030
- const { compilation } = this;
4031
- if (!compilation) return Template.asString('/* [webpack-target-webextension] EagerlyLoadChunksRuntimeModule skipped because no compilation is found. */');
4032
- const _const = compilation.outputOptions.environment.const ? 'const' : 'var';
4033
- return Template.asString([
4034
- `${_const} __extjsChunkIds = ${JSON.stringify(this.chunkIds)};`,
4035
- `${_const} __extjsLoad = ${RuntimeGlobals.ensureChunkHandlers};`,
4036
- `for (${_const} __extjsId of __extjsChunkIds) __extjsLoad(__extjsId);`
4037
- ]);
4038
- }
4039
- };
4040
- }
4041
- class WebExtensionServiceWorkerEntryPlugin {
4042
- options;
4043
- backgroundOutputEmitted;
4044
- constructor(options, backgroundOutputEmitted){
4045
- this.options = options;
4046
- this.backgroundOutputEmitted = backgroundOutputEmitted;
4047
- }
4048
- apply(compiler) {
4049
- const { javascript, sources } = compiler.webpack;
4050
- const entry = this.options.serviceWorkerEntry;
4051
- if (void 0 === entry) return;
4052
- if (!(compiler.options.output.module || compiler.options.experiments.outputModule)) {
4053
- const hook = compiler.hooks.entryOption;
4054
- hook.tap(WebExtensionServiceWorkerEntryPlugin.name, (_context, entries)=>{
4055
- if ('function' == typeof entries) {
4056
- if (this.options.noDynamicEntryWarning) return;
4057
- console.warn(`[webpack-extension-target] Dynamic entry points not supported yet.
4058
- You must manually set the chuck loading of entry point ${entry} to "import-scripts".
4059
-
4060
- See https://webpack.js.org/configuration/entry-context/#entry-descriptor
4061
-
4062
- Set background.noDynamicEntryWarning to true to disable this warning.
4063
- `);
4064
- }
4065
- const selectedEntry = entries?.[entry];
4066
- if (!selectedEntry) throw new Error(`[webpack-extension-target] There is no entry called ${entry}.`);
4067
- selectedEntry.chunkLoading = "import-scripts";
4068
- });
4069
- }
4070
- if (false !== this.options.eagerChunkLoading) compiler.hooks.compilation.tap(WebExtensionServiceWorkerEntryPlugin.name, (compilation)=>{
4071
- compilation.hooks.afterOptimizeChunkIds?.tap(WebExtensionServiceWorkerEntryPlugin.name, ()=>{
4072
- const entryPoint = compilation.entrypoints.get(entry);
4073
- if (!entryPoint) return;
4074
- const entryChunk = entryPoint.getEntrypointChunk();
4075
- const reachableChunks = getReachableChunks(entryPoint, new Set(entryPoint.chunks));
4076
- const reachableChunkIds = new Set([
4077
- ...reachableChunks
4078
- ].map((x)=>x.id));
4079
- for (const id of getInitialChunkIds(entryChunk, compilation.chunkGraph, chunkHasJs))reachableChunkIds.delete(id);
4080
- if (reachableChunkIds.size) {
4081
- const EagerlyLoadChunksRuntimeModule = createEagerlyLoadChunksRuntimeModule(compiler.webpack);
4082
- compilation.hooks.additionalTreeRuntimeRequirements.tap(EagerlyLoadChunksRuntimeModule.name, (chunk, set)=>{
4083
- if (chunk.id !== entryChunk.id) return;
4084
- set.add(compiler.webpack.RuntimeGlobals.ensureChunkHandlers);
4085
- compilation.addRuntimeModule(entryChunk, new EagerlyLoadChunksRuntimeModule([
4086
- ...reachableChunkIds
4087
- ]));
4088
- });
4089
- }
4090
- });
4091
- });
4092
- if (false !== this.options.tryCatchWrapper && 'module' !== compiler.options.output.chunkFormat && !this.backgroundOutputEmitted) compiler.hooks.compilation.tap(WebExtensionServiceWorkerEntryPlugin.name, (compilation)=>{
4093
- const hooks = javascript.JavascriptModulesPlugin.getCompilationHooks(compilation);
4094
- if (hooks.renderContent) hooks.renderContent.tap(WebExtensionServiceWorkerEntryPlugin.name, (source, context)=>{
4095
- const entryPoint = compilation.entrypoints.get(entry);
4096
- const entryChunk = entryPoint?.getEntrypointChunk();
4097
- if (entryChunk !== context.chunk) return source;
4098
- return new sources.ConcatSource('/******/ try { // If the initial code of the serviceWorkerEntry throws, the console cannot be opened.\n', source, '\n/******/ } catch (e) {\n/******/ \tPromise.reject(e);\n/******/ }');
4099
- });
4100
- });
4101
- }
4102
- }
4103
- function getInitialChunkIds(chunk, chunkGraph, filterFn) {
4104
- const initialChunkIds = new Set(chunk.ids);
4105
- for (const c of chunk.getAllInitialChunks())if (!(c === chunk || filterFn(c, chunkGraph)) && c.ids) for (const id of c.ids)initialChunkIds.add(id);
4106
- return initialChunkIds;
4107
- }
4108
- function chunkHasJs(chunk, chunkGraph) {
4109
- if (chunkGraph.getNumberOfEntryModules(chunk) > 0) return true;
4110
- return !!chunkGraph.getChunkModulesIterableBySourceType(chunk, "javascript");
4111
- }
4112
- function getReachableChunks(chunkGroup, reachableChunks = new Set(), visitedChunkGroups = new Set()) {
4113
- for (const x of chunkGroup.getChildren())if (!visitedChunkGroups.has(x)) {
4114
- visitedChunkGroups.add(x);
4115
- for (const c of x.chunks)reachableChunks.add(c);
4116
- getReachableChunks(x, reachableChunks, visitedChunkGroups);
4117
- }
4118
- return reachableChunks;
4119
- }
4120
- class WebExtensionContentScriptEntryPlugin {
4121
- options;
4122
- constructor(options){
4123
- this.options = options;
4124
- }
4125
- apply(compiler) {
4126
- const { WebpackError, sources, Template } = compiler.webpack;
4127
- const { experimental_output } = this.options;
4128
- if (!experimental_output) return;
4129
- {
4130
- const sw = this.options.background?.serviceWorkerEntry;
4131
- if (sw && sw in experimental_output && 'function' == typeof experimental_output[sw]) throw new Error(`[webpack-extension-target] options.experimental_output[${JSON.stringify(sw)}] cannot be a function because it is a service worker entry. Use { file, touch(manifest, file) { manifest.background.service_worker = file; } } instead.`);
4132
- }
4133
- compiler.hooks.thisCompilation.tap(WebExtensionContentScriptEntryPlugin.name, (compilation)=>{
4134
- compilation.hooks.processAssets.tap({
4135
- name: WebExtensionContentScriptEntryPlugin.name,
4136
- stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED
4137
- }, (assets)=>{
4138
- let manifest;
4139
- const serviceWorkerEntry = this.options.background?.serviceWorkerEntry;
4140
- if (serviceWorkerEntry && !(serviceWorkerEntry in experimental_output) && (getInitialFiles(compilation, serviceWorkerEntry)?.length || 0) > 1) {
4141
- const e = new WebpackError(`[webpack-extension-target] Entry ${JSON.stringify(serviceWorkerEntry)} is not specified in options.experimental_output.`);
4142
- e.stack = '';
4143
- compilation.warnings.push(e);
4144
- }
4145
- for(const entry in experimental_output){
4146
- const isBackgroundEntry = entry === serviceWorkerEntry;
4147
- const entryOption = experimental_output[entry];
4148
- const initialFiles = getInitialFiles(compilation, entry);
4149
- if (!initialFiles || 0 === initialFiles.length) {
4150
- const name = JSON.stringify(entry);
4151
- const e = new WebpackError(initialFiles ? `[webpack-extension-target] Entry ${name} does not emit any initial file (specified in options.experimental_output).` : `[webpack-extension-target] Entry ${name} does not exist (specified in options.experimental_output).`);
4152
- e.stack = '';
4153
- compilation.errors.push(e);
4154
- continue;
4155
- }
4156
- if (false === entryOption) {
4157
- if (initialFiles.length > 1) {
4158
- const name = JSON.stringify(entry);
4159
- const e = new WebpackError(`[webpack-extension-target] Entry ${name} emits more than one initial file which is prohibited (specified in options.experimental_output).`);
4160
- e.stack = '';
4161
- compilation.errors.push(e);
4162
- }
4163
- continue;
4164
- }
4165
- const emitFile = (entryOptionFile, files)=>{
4166
- if (entryOptionFile in assets) {
4167
- const e = new WebpackError(`[webpack-extension-target] Cannot override an existing file ${JSON.stringify(entryOptionFile)} (specified by options.experimental_output[${JSON.stringify(entry)}]).`);
4168
- e.stack = '';
4169
- compilation.errors.push(e);
4170
- return;
4171
- }
4172
- let code;
4173
- if (isBackgroundEntry) {
4174
- const asyncAndSyncFiles = getInitialAndAsyncFiles(compilation, entry);
4175
- code = 'module' === compilation.outputOptions.chunkFormat ? asyncAndSyncFiles.map((file)=>`import ${JSON.stringify(`./${file}`)};`) : [
4176
- 'try {',
4177
- Template.indent('importScripts(' + asyncAndSyncFiles.map((file)=>JSON.stringify(file)).join(', ') + ');'),
4178
- '} catch (e) {',
4179
- Template.indent('Promise.reject(e);'),
4180
- '}'
4181
- ];
4182
- } else code = [
4183
- ';(() => {',
4184
- Template.indent([
4185
- 'const getURL = typeof globalThis === "object" && globalThis && typeof globalThis.browser === "object" ? globalThis.browser.runtime.getURL : globalThis.chrome.runtime.getURL;',
4186
- `${JSON.stringify(files)}.forEach(file => import(getURL(file)));`
4187
- ]),
4188
- '})();',
4189
- 'null;'
4190
- ];
4191
- const source = new compiler.webpack.sources.RawSource(Template.asString(code));
4192
- compilation.emitAsset(entryOptionFile, source);
4193
- };
4194
- if ('string' == typeof entryOption) {
4195
- emitFile(entryOption, initialFiles);
4196
- continue;
4197
- }
4198
- if (!manifest) {
4199
- const manifestAsset = assets['manifest.json'];
4200
- const name = JSON.stringify(entry);
4201
- if (!manifestAsset) {
4202
- const e = new WebpackError(`[webpack-extension-target] A manifest.json is required (required by options.experimental_output[${name}]). You can emit this file by using CopyPlugin or any other plugins.`);
4203
- e.stack = '';
4204
- compilation.errors.push(e);
4205
- continue;
4206
- }
4207
- try {
4208
- const source = manifestAsset.source();
4209
- manifest = 'string' == typeof source ? JSON.parse(source) : JSON.parse(source.toString('utf-8'));
4210
- } catch {
4211
- const e = new WebpackError(`[webpack-extension-target] Failed to parse manifest.json (required by options.experimental_output[${name}]).`);
4212
- e.stack = '';
4213
- e.file = 'manifest.json';
4214
- compilation.errors.push(e);
4215
- continue;
4216
- }
4217
- }
4218
- if ('function' == typeof entryOption) entryOption(manifest, initialFiles);
4219
- else if ('object' == typeof entryOption) {
4220
- emitFile(entryOption.file, initialFiles);
4221
- entryOption.touch(manifest, entryOption.file);
4222
- }
4223
- }
4224
- if (manifest) compilation.updateAsset('manifest.json', new sources.RawSource(JSON.stringify(manifest, void 0, 4)));
4225
- });
4226
- });
4227
- }
4228
- }
4229
- function getInitialFiles(compilation, entry) {
4230
- const entryPoint = compilation.entrypoints.get(entry);
4231
- if (!entryPoint) return;
4232
- const files = new Set();
4233
- const runtimeChunk = entryPoint.getRuntimeChunk();
4234
- if (runtimeChunk) runtimeChunk.files.forEach((file)=>{
4235
- if (!isJSFile(file)) return;
4236
- const asset = compilation.getAsset(file);
4237
- if (!asset) return;
4238
- if (!asset.info.hotModuleReplacement) files.add(file);
4239
- });
4240
- entryPoint.getFiles().forEach((file)=>{
4241
- if (!isJSFile(file)) return;
4242
- const asset = compilation.getAsset(file);
4243
- if (!asset) return;
4244
- if (!asset.info.hotModuleReplacement) files.add(file);
4245
- });
4246
- return [
4247
- ...files
4248
- ];
4249
- }
4250
- function getInitialAndAsyncFiles(compilation, entry) {
4251
- const entryPoint = compilation.entrypoints.get(entry);
4252
- if (!entryPoint) return [];
4253
- const files = new Set();
4254
- const visitedChunk = new Set();
4255
- function visit(chunk) {
4256
- const chunkId = 'rspack' in compilation.compiler ? chunk.id && chunk.hash ? chunk.id + chunk.hash : Array.from(chunk.files).join('') : chunk;
4257
- if (visitedChunk.has(chunkId)) return;
4258
- visitedChunk.add(chunkId);
4259
- chunk.files.forEach((file)=>{
4260
- if (!isJSFile(file)) return;
4261
- const asset = compilation.getAsset(file);
4262
- if (!asset) return;
4263
- if (!asset.info.hotModuleReplacement) files.add(file);
4264
- });
4265
- for (const child of chunk.getAllAsyncChunks())visit(child);
4266
- }
4267
- entryPoint.chunks.forEach(visit);
4268
- const allFiles = [
4269
- ...files
4270
- ].filter((file)=>isJSFile(file) && compilation.getAsset(file));
4271
- return allFiles;
4272
- }
4273
- function isJSFile(file) {
4274
- return file.endsWith('.js') || file.endsWith('.mjs');
4275
- }
4276
- class DevServerConfigPlugin {
4277
- apply(compiler) {
4278
- if (!compiler.options.devServer) compiler.options.devServer = {};
4279
- const devServer = compiler.options.devServer;
4280
- setDefault(devServer, 'devMiddleware', {});
4281
- setDefault(devServer.devMiddleware, 'writeToDisk', true);
4282
- if (void 0 === devServer.hot) devServer.hot = 'only';
4283
- if (!devServer.hot) return;
4284
- setDefault(devServer, 'host', '127.0.0.1');
4285
- setDefault(devServer, 'client', {
4286
- overlay: false,
4287
- progress: false,
4288
- webSocketURL: {
4289
- protocol: 'ws'
4290
- }
4291
- });
4292
- setDefault(devServer.client, 'overlay', false);
4293
- setDefault(devServer.client, 'progress', false);
4294
- setDefault(devServer.client, 'webSocketURL', {
4295
- protocol: 'ws'
4296
- });
4297
- setDefault(devServer.client.webSocketURL, 'protocol', 'ws');
4298
- const connectableHost = resolveClientHost(process.env.EXTENSION_DEV_SERVER_CONNECTABLE_HOST, devServer.host);
4299
- if (connectableHost) setDefault(devServer.client.webSocketURL, 'hostname', connectableHost);
4300
- if (devServer.port) setDefault(devServer.client.webSocketURL, 'port', devServer.port);
4301
- setDefault(devServer, 'allowedHosts', 'all');
4302
- setDefault(devServer, 'headers', {
4303
- 'Access-Control-Allow-Origin': '*'
4304
- });
4305
- setDefault(devServer, 'static', {
4306
- watch: {
4307
- ignored: /\bnode_modules\b/
4308
- }
4309
- });
4310
- setDefault(devServer.static, 'watch', {
4311
- ignored: /\bnode_modules\b/
4312
- });
4313
- isObject(devServer.static) && 'watch' in devServer.static && isObject(devServer.static.watch) && setDefault(devServer.static.watch, 'ignored', /\bnode_modules\b/);
4314
- }
4315
- }
4316
- function setDefault(obj, key, val) {
4317
- if (isObject(obj) && void 0 === obj[key]) obj[key] = val;
4318
- }
4319
- const WILDCARD_HOSTS = new Set([
4320
- '0.0.0.0',
4321
- '::',
4322
- '[::]',
4323
- '::0',
4324
- '*',
4325
- ''
4326
- ]);
4327
- function resolveClientHost(connectable, bindHost) {
4328
- const resolved = String(connectable ?? '').trim();
4329
- if (resolved) return resolved;
4330
- const bind = 'string' == typeof bindHost ? bindHost.trim() : '';
4331
- if (!bind) return;
4332
- return WILDCARD_HOSTS.has(bind) ? '127.0.0.1' : bind;
4333
- }
4334
- function isObject(x) {
4335
- return 'object' == typeof x && null !== x;
4336
- }
4337
- class WebExtensionPlugin {
4338
- options;
4339
- constructor(options = {}){
4340
- const { background } = options;
4341
- if (background) {
4342
- const { serviceWorkerEntry, pageEntry } = background;
4343
- if ('entry' in background || 'manifest' in background) throw new Error('[webpack-extension-target] background.entry and background.manifest has been removed. Use background.pageEntry and/or background.serviceWorkerEntry instead.');
4344
- if (serviceWorkerEntry && serviceWorkerEntry === pageEntry) throw new Error(`[webpack-extension-target] background.serviceWorkerEntry must not be the same as background.pageEntry. Service Worker entry only supports importScript, but importScript does not exist in background page (mv2) or limited event page (mv3).
4345
- A possible fix is to create two new files to be the service worker entry and the page entry, then those two files imports the background entry.`);
4346
- }
4347
- this.options = options;
4348
- }
4349
- apply(compiler) {
4350
- const { background, experimental_output } = this.options;
4351
- if (background?.serviceWorkerEntry) {
4352
- const serviceWorkerEntry = this.options.background?.serviceWorkerEntry;
4353
- const output = serviceWorkerEntry ? this.options.experimental_output?.[serviceWorkerEntry] : false;
4354
- new WebExtensionServiceWorkerEntryPlugin(background, !!output).apply(compiler);
4355
- }
4356
- if (experimental_output) new WebExtensionContentScriptEntryPlugin(this.options).apply(compiler);
4357
- if (false !== this.options.hmrConfig) new DevServerConfigPlugin().apply(compiler);
4358
- new WebExtensionChuckLoaderRuntimePlugin(this.options).apply(compiler);
4359
- new NoDangerNamePlugin().apply(compiler);
4360
- compiler.hooks.environment.tap(WebExtensionPlugin.name, ()=>{
4361
- const { output } = compiler.options;
4362
- if (false !== output.environment.dynamicImport) output.environment.dynamicImport = true;
4363
- if (void 0 !== output.hotUpdateChunkFilename) output.hotUpdateChunkFilename = 'hot/[id].[fullhash].js';
4364
- if (void 0 !== output.hotUpdateMainFilename) output.hotUpdateMainFilename = 'hot/[runtime].[fullhash].json';
4365
- });
4366
- compiler.hooks.compilation.tap(WebExtensionPlugin.name, (compilation)=>{
4367
- compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobal).tap(WebExtensionPlugin.name, (chunk)=>{
4368
- compilation.addRuntimeModule(chunk, BrowserRuntimeModule(compiler.webpack, !!this.options.weakRuntimeCheck));
4369
- return true;
4370
- });
4371
- });
4372
- }
4373
- }
4374
- const webpack_target_webextension_fork = WebExtensionPlugin;
4375
- class SetupReloadStrategy {
4376
- manifestPath;
4377
- browser;
4378
- constructor(options){
4379
- this.manifestPath = options.manifestPath;
4380
- this.browser = options.browser || 'chrome';
4381
- }
4382
- getEntryName(manifest) {
4383
- if (manifest.background) {
4384
- if (isGeckoBasedBrowser(String(this.browser))) return {
4385
- pageEntry: "background/script",
4386
- tryCatchWrapper: true,
4387
- eagerChunkLoading: false
4388
- };
4389
- if (3 === manifest.manifest_version) return {
4390
- serviceWorkerEntry: 'background/service_worker',
4391
- tryCatchWrapper: true,
4392
- eagerChunkLoading: false
4393
- };
4394
- if (2 === manifest.manifest_version) return {
4395
- pageEntry: "background/script",
4396
- tryCatchWrapper: true,
4397
- eagerChunkLoading: false
4398
- };
4399
- }
4400
- return {
4401
- pageEntry: 'background',
4402
- tryCatchWrapper: true,
4403
- eagerChunkLoading: false
4404
- };
4405
- }
4406
- apply(compiler) {
4407
- const manifest = JSON.parse(stripBom(__rspack_external_node_fs_5ea92f0c.readFileSync(this.manifestPath, 'utf-8')));
4408
- const patchedManifest = filterKeysForThisBrowser(manifest, this.browser);
4409
- const contentScriptsMeta = {};
4410
- try {
4411
- const csList = Array.isArray(patchedManifest.content_scripts) ? patchedManifest.content_scripts : [];
4412
- const originalCount = csList.length;
4413
- let bridgeOrdinal = 0;
4414
- for(let i = 0; i < csList.length; i++){
4415
- const cs = csList[i];
4416
- const bundleId = getCanonicalContentScriptJsAssetName(i);
4417
- const isMain = cs?.world === 'MAIN';
4418
- if (isMain) {
4419
- const bridgeIndex = originalCount + bridgeOrdinal++;
4420
- const bridgeBundleId = getCanonicalContentScriptJsAssetName(bridgeIndex);
4421
- contentScriptsMeta[bundleId] = {
4422
- index: i,
4423
- bundleId,
4424
- world: 'main',
4425
- bridgeBundleId
4426
- };
4427
- contentScriptsMeta[bridgeBundleId] = {
4428
- index: bridgeIndex,
4429
- bundleId: bridgeBundleId,
4430
- world: 'extension',
4431
- role: 'main_world_bridge',
4432
- mainBundleId: bundleId
4433
- };
4434
- } else contentScriptsMeta[bundleId] = {
4435
- index: i,
4436
- bundleId,
4437
- world: 'extension'
4438
- };
4439
- }
4440
- } catch {}
4441
- new SetupBackgroundEntry({
4442
- manifestPath: this.manifestPath,
4443
- browser: this.browser
4444
- }).apply(compiler);
4445
- new webpack_target_webextension_fork({
4446
- background: this.getEntryName(patchedManifest),
4447
- hmrConfig: false,
4448
- weakRuntimeCheck: true,
4449
- contentScriptsMeta
4450
- }).apply(compiler);
4451
- }
4452
- }
4453
- const DEV_SERVER_STRONG_MARKERS = [
4454
- '@rspack/dev-server/client/index.js?',
4455
- '@rspack/dev-server/client/utils/ansiHTML.js',
4456
- '[HMR] Waiting for update signal from WDS...'
4457
- ];
4458
- const DEV_SERVER_WEAK_MARKERS = [
4459
- 'webpack-dev-server',
4460
- 'WebSocketClient',
4461
- '.hot.check()',
4462
- '[HMR] Cannot find update. Need to do a full reload!'
4463
- ];
4464
- function contentScriptRetainsDevServerRuntime(source) {
4465
- return source.includes('@rspack/dev-server/client');
4466
- }
4467
- function stripDevServerStartupFromContentScript(source) {
4468
- let nextSource = source;
4469
- const startupModuleIds = getStartupModuleIds(source);
4470
- for (const moduleId of startupModuleIds){
4471
- const moduleBody = getModuleBody(source, moduleId);
4472
- if (!moduleBody) continue;
4473
- const strongHit = DEV_SERVER_STRONG_MARKERS.some((marker)=>moduleBody.includes(marker));
4474
- const weakHits = DEV_SERVER_WEAK_MARKERS.filter((marker)=>moduleBody.includes(marker)).length;
4475
- if (strongHit || weakHits >= 2) nextSource = stripStartupRequire(nextSource, moduleId);
4476
- }
4477
- return nextSource;
4478
- }
4479
- function getStartupModuleIds(source) {
4480
- const startupIndex = source.indexOf('// startup');
4481
- if (-1 === startupIndex) return [];
4482
- const startupSection = source.slice(startupIndex);
4483
- const requirePattern = /__webpack_require__\((\d+)\);/g;
4484
- const ids = [];
4485
- let match = null;
4486
- while(match = requirePattern.exec(startupSection))ids.push(match[1]);
4487
- return ids;
4488
- }
4489
- function getModuleBody(source, moduleId) {
4490
- const moduleHeaderPattern = new RegExp(`(?:^|\\n)${moduleId}\\([^)]*\\)\\s*\\{`, 'm');
4491
- const headerMatch = moduleHeaderPattern.exec(source);
4492
- if (!headerMatch) return null;
4493
- const moduleStart = headerMatch.index;
4494
- const nextHeaderPattern = /(?:^|\n)\d+\([^)]*\)\s*\{/g;
4495
- nextHeaderPattern.lastIndex = moduleStart + headerMatch[0].length;
4496
- const nextHeaderMatch = nextHeaderPattern.exec(source);
4497
- return source.slice(moduleStart, nextHeaderMatch ? nextHeaderMatch.index : source.length);
4498
- }
4499
- function stripStartupRequire(source, moduleId) {
4500
- const startupRequirePattern = new RegExp(`^\\s*__webpack_require__\\(${moduleId}\\);\\n?`, 'm');
4501
- return source.replace(startupRequirePattern, '');
4502
- }
4503
- const CONTENT_SCRIPT_ASSET = /(^|\/)content_scripts\/content-\d+(?:\.[a-f0-9]+)?\.js$/i;
4504
- class StripContentScriptDevServerRuntime {
4505
- apply(compiler) {
4506
- compiler.hooks.thisCompilation.tap(StripContentScriptDevServerRuntime.name, (compilation)=>{
4507
- compilation.hooks.processAssets.tap({
4508
- name: StripContentScriptDevServerRuntime.name,
4509
- stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT
4510
- }, ()=>{
4511
- for (const asset of compilation.getAssets()){
4512
- if (!CONTENT_SCRIPT_ASSET.test(asset.name)) continue;
4513
- const originalSource = asset.source.source().toString();
4514
- const strippedSource = stripDevServerStartupFromContentScript(originalSource);
4515
- if (strippedSource !== originalSource) compilation.updateAsset(asset.name, new core_sources.RawSource(strippedSource));
4516
- if (contentScriptRetainsDevServerRuntime(strippedSource)) {
4517
- const warning = new core_WebpackError(`Could not strip the dev-server runtime from ${asset.name}. This usually means the bundler's output format changed and Extension.js needs an update. The content script may try to open a dev-server connection on the host page. Please report this at https://github.com/extension-js/extension.js/issues.`);
4518
- warning.name = 'ContentScriptDevServerRuntimeWarning';
4519
- warning.file = asset.name;
4520
- compilation.warnings ||= [];
4521
- compilation.warnings.push(warning);
4522
- }
4523
- }
4524
- });
4525
- });
4526
- }
4527
- }
4528
- function formatReloadContextLabel(context, files) {
4529
- if (!files.length) return context;
4530
- const shown = files.slice(0, 2).join(', ');
4531
- const extra = files.length > 2 ? ` +${files.length - 2} more` : '';
4532
- return `${context} (${shown}${extra})`;
4533
- }
4534
- function pageContextFromSources(changedSources) {
4535
- const rules = [
4536
- [
4537
- /(^|\/)(sidebar|side[-_]?panel)(\.|\/)/i,
4538
- 'sidebar page'
4539
- ],
4540
- [
4541
- /(^|\/)(action|popup)(\.|\/)/i,
4542
- 'popup page'
4543
- ],
4544
- [
4545
- /(^|\/)options(\.|\/)/i,
4546
- 'options page'
4547
- ],
4548
- [
4549
- /(^|\/)devtools(\.|\/)/i,
4550
- 'devtools page'
4551
- ],
4552
- [
4553
- /(^|\/)(newtab|new[-_]?tab)(\.|\/)/i,
4554
- 'new tab page'
4555
- ],
4556
- [
4557
- /(^|\/)(history|bookmarks)(\.|\/)/i,
4558
- 'page'
4559
- ]
4560
- ];
4561
- for (const rel of changedSources)for (const [re, name] of rules)if (re.test(rel)) return name;
4562
- return 'page';
4563
- }
4564
- function moduleResourcesFromIdentifier(identifier, contextDir) {
4565
- const afterLoaders = identifier.slice(identifier.lastIndexOf('!') + 1);
4566
- const noLayer = afterLoaders.split('|')[0];
4567
- const queryIndex = noLayer.indexOf('?');
4568
- const resourcePath = -1 === queryIndex ? noLayer : noLayer.slice(0, queryIndex);
4569
- const query = -1 === queryIndex ? '' : noLayer.slice(queryIndex);
4570
- const out = [];
4571
- const push = (absolute)=>{
4572
- if (!absolute || !__rspack_external_node_path_c5b9b54f.isAbsolute(absolute)) return;
4573
- out.push(__rspack_external_node_path_c5b9b54f.relative(contextDir, absolute).replace(/\\/g, '/'));
4574
- };
4575
- push(resourcePath);
4576
- const concatMatch = query.match(/[?&]__extensionjs_classic_concat__=([^&]+)/);
4577
- if (concatMatch) try {
4578
- const data = JSON.parse(decodeURIComponent(concatMatch[1]));
4579
- for (const file of data?.js || [])push(String(file));
4580
- } catch {}
4581
- return out;
4582
- }
4583
- function buildSourceFeatureIndex(compilation, contextDir) {
4584
- const index = {
4585
- swSources: new Set(),
4586
- contentEntriesBySource: new Map(),
4587
- pageSources: new Set()
4588
- };
4589
- const chunkGraph = compilation.chunkGraph;
4590
- for (const chunk of compilation.chunks || []){
4591
- const name = String(chunk?.name || '');
4592
- if (!name) continue;
4593
- const isBackground = /^background\//.test(name);
4594
- const isContent = /^content_scripts\//.test(name);
4595
- for (const module of chunkGraph.getChunkModulesIterable(chunk)){
4596
- let identifier = '';
4597
- try {
4598
- identifier = String(module.identifier());
4599
- } catch {
4600
- continue;
4601
- }
4602
- for (const rel of moduleResourcesFromIdentifier(identifier, contextDir))if (isBackground) index.swSources.add(rel);
4603
- else if (isContent) {
4604
- let entries = index.contentEntriesBySource.get(rel);
4605
- if (!entries) {
4606
- entries = new Set();
4607
- index.contentEntriesBySource.set(rel, entries);
4608
- }
4609
- entries.add(name);
4610
- } else index.pageSources.add(rel);
4611
- }
4612
- }
4613
- return index;
4614
- }
4615
- function classifyReloadFromSources(opts) {
4616
- const { changedSources, forcedFull, getContentScriptCount, getSourceFeatureIndex, outputPath } = opts;
4617
- if (0 === changedSources.length) return;
4618
- if (forcedFull) return {
4619
- type: 'full',
4620
- changedAssets: changedSources,
4621
- label: formatReloadContextLabel('extension', changedSources)
4622
- };
4623
- let index = null;
4624
- try {
4625
- index = getSourceFeatureIndex ? getSourceFeatureIndex() : null;
4626
- } catch {
4627
- index = null;
4628
- }
4629
- const swChanged = [];
4630
- const contentEntries = new Set();
4631
- const contentChanged = [];
4632
- const pageChanged = [];
4633
- const unknown = [];
4634
- for (const rel of changedSources){
4635
- let known = false;
4636
- if (index?.swSources.has(rel)) {
4637
- swChanged.push(rel);
4638
- known = true;
4639
- }
4640
- if (index?.contentEntriesBySource.has(rel)) {
4641
- contentChanged.push(rel);
4642
- for (const entry of index.contentEntriesBySource.get(rel))contentEntries.add(entry);
4643
- known = true;
4644
- }
4645
- if (!known && index?.pageSources.has(rel)) {
4646
- pageChanged.push(rel);
4647
- known = true;
4648
- }
4649
- if (!known) unknown.push(rel);
4650
- }
4651
- const staticAssetChanged = outputPath && unknown.some((rel)=>{
4652
- try {
4653
- return __rspack_external_node_fs_5ea92f0c.existsSync(__rspack_external_node_path_c5b9b54f.join(outputPath, rel));
4654
- } catch {
4655
- return false;
1269
+ return __rspack_external_node_fs_5ea92f0c.existsSync(__rspack_external_node_path_c5b9b54f.join(outputPath, rel));
1270
+ } catch {
1271
+ return false;
4656
1272
  }
4657
1273
  });
4658
1274
  if (swChanged.length > 0) return {
@@ -4725,77 +1341,6 @@ function readContentScriptCount(compilation, outputPath) {
4725
1341
  } catch {}
4726
1342
  return 0;
4727
1343
  }
4728
- function viaBroker(broker, instruction) {
4729
- return broker.broadcastReload({
4730
- type: instruction.type,
4731
- changedContentScriptEntries: instruction.changedContentScriptEntries,
4732
- label: instruction.label,
4733
- changedFiles: instruction.changedAssets
4734
- });
4735
- }
4736
- function formatReloadingLine(label) {
4737
- return `Reloading ${pintor.brightBlue(label)}…`;
4738
- }
4739
- async function dispatchReload(instruction, executor) {
4740
- if (!instruction) return;
4741
- if ('true' === process.env.EXTENSION_NO_RELOAD) return;
4742
- if (executor.broker) {
4743
- const notified = viaBroker(executor.broker, instruction);
4744
- if (notified > 0) {
4745
- if (instruction.label) console.log(formatReloadingLine(instruction.label));
4746
- return;
4747
- }
4748
- const warning = executor.broker.undeliveredReloadWarning?.();
4749
- if (warning) console.warn(warning);
4750
- }
4751
- }
4752
- function createChangedSourcesTracker(compiler) {
4753
- let forcedFull = false;
4754
- let changedSources = [];
4755
- compiler.hooks.watchRun.tap('extjs-reload-changed-sources', ()=>{
4756
- forcedFull = false;
4757
- changedSources = [];
4758
- const modifiedFiles = compiler.modifiedFiles;
4759
- if (!modifiedFiles || 0 === modifiedFiles.size) return;
4760
- const contextDir = compiler.options.context || '';
4761
- for (const file of modifiedFiles){
4762
- const normalized = __rspack_external_node_path_c5b9b54f.relative(contextDir, file).replace(/\\/g, '/');
4763
- if (normalized) {
4764
- changedSources.push(normalized);
4765
- if (normalized.includes('manifest.json') || normalized.includes('_locales/')) forcedFull = true;
4766
- }
4767
- }
4768
- });
4769
- return {
4770
- snapshot: ()=>({
4771
- forcedFull,
4772
- changedSources
4773
- })
4774
- };
4775
- }
4776
- class ReloadPlugin {
4777
- static name = 'plugin-reload';
4778
- manifestPath;
4779
- browser;
4780
- constructor(options){
4781
- this.manifestPath = options.manifestPath;
4782
- this.browser = options.browser || 'chrome';
4783
- }
4784
- apply(compiler) {
4785
- if ('production' === compiler.options.mode || 'true' === process.env.EXTENSION_NO_RELOAD) return;
4786
- const hasValidManifest = !!this.manifestPath && __rspack_external_node_fs_5ea92f0c.existsSync(this.manifestPath) && __rspack_external_node_fs_5ea92f0c.lstatSync(this.manifestPath).isFile();
4787
- if (!hasValidManifest) return;
4788
- new StripContentScriptDevServerRuntime().apply(compiler);
4789
- new SetupReloadStrategy({
4790
- manifestPath: this.manifestPath,
4791
- browser: this.browser
4792
- }).apply(compiler);
4793
- new InjectScriptsReplayShim().apply(compiler);
4794
- new InjectBridgeProducer().apply(compiler);
4795
- new InjectBridgeRelay().apply(compiler);
4796
- new PruneStaleHotUpdates().apply(compiler);
4797
- }
4798
- }
4799
1344
  class BuildEmitter extends EventEmitter {
4800
1345
  constructor(){
4801
1346
  super();
@@ -5067,4 +1612,4 @@ async function extensionDev(pathOrRemoteUrl, devOptions) {
5067
1612
  process.exit(1);
5068
1613
  }
5069
1614
  }
5070
- export { BuildEmitter, ReloadPlugin, attachLifecycleStream, buildCanonicalManifest, buildSourceFeatureIndex, classifyReloadFromSources, collectRootAbsoluteRefs, createChangedSourcesTracker, createLifecycleStream, dispatchReload, ensureTypeScriptConfig, extensionBuild, extensionDev, filterKeysForThisBrowser, getCurrentManifestContent, getManifestContent, getManifestOverrides, getUserTypeScriptConfigFile, hasDependency, humanLine, isFromFilepathList, isStaticThemeSource, isUsingCustomLoader, isUsingIntegration, isUsingTypeScript, jsFrameworksConfigsDetected, jsFrameworksHmrSummary, jsFrameworksIntegrationsEnabled, normalizeManifestOutputPath, readContentScriptCount, reportToCompilation, resolveDevelopDistFile, resolveDevelopInstallRoot, resolvePackageManager, resolveRootAbsoluteRef, setCurrentManifestContent, setOriginalManifestContent, unixify };
1615
+ export { BuildEmitter, attachLifecycleStream, buildSourceFeatureIndex, classifyReloadFromSources, createChangedSourcesTracker, createLifecycleStream, dispatchReload, ensureTypeScriptConfig, extensionBuild, extensionDev, getUserTypeScriptConfigFile, hasDependency, humanLine, isUsingCustomLoader, isUsingIntegration, isUsingTypeScript, jsFrameworksConfigsDetected, jsFrameworksHmrSummary, jsFrameworksIntegrationsEnabled, readContentScriptCount, resolveDevelopDistFile, resolveDevelopInstallRoot, resolvePackageManager };