@crxjs/vite-plugin 2.4.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/client.d.ts +15 -0
- package/dist/index.d.mts +448 -0
- package/dist/index.d.ts +38 -5
- package/dist/index.mjs +754 -147
- package/package.json +19 -10
package/dist/index.mjs
CHANGED
|
@@ -9,12 +9,12 @@ import { rollup } from 'rollup';
|
|
|
9
9
|
import * as lexer from 'es-module-lexer';
|
|
10
10
|
import { readFile as readFile$1 } from 'fs/promises';
|
|
11
11
|
import MagicString from 'magic-string';
|
|
12
|
+
import { build, mergeConfig, createLogger, version } from 'vite';
|
|
12
13
|
import convertSourceMap from 'convert-source-map';
|
|
13
14
|
import pc from 'picocolors';
|
|
14
|
-
import { createLogger, version } from 'vite';
|
|
15
15
|
import { readFileSync, existsSync, promises } from 'fs';
|
|
16
16
|
import { createRequire } from 'module';
|
|
17
|
-
import
|
|
17
|
+
import { glob, isDynamicPattern } from 'tinyglobby';
|
|
18
18
|
import { parse } from 'node-html-parser';
|
|
19
19
|
import jsesc from 'jsesc';
|
|
20
20
|
|
|
@@ -58,7 +58,7 @@ function isCrxPlugin(p) {
|
|
|
58
58
|
return !!p && typeof p === "object" && !(p instanceof Promise) && !Array.isArray(p) && p.name.startsWith("crx:");
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
var workerHmrClient = "const ownOrigin = `chrome-extension://${chrome.runtime.id}`;\nself.addEventListener(\"fetch\", (fetchEvent) => {\n const url = new URL(fetchEvent.request.url);\n if (url.origin === ownOrigin) {\n fetchEvent.respondWith(sendToServer(fetchEvent.request));\n }\n});\nasync function sendToServer(req) {\n const url = new URL(req.url);\n const requestHeaders = new Headers(req.headers);\n url.protocol = __SERVER_PROTO__ + \":\";\n url.host = \"localhost\";\n url.port = __SERVER_PORT__;\n url.searchParams.set(\"t\", Date.now().toString());\n const response = await fetch(url.href.replace(/=$|=(?=&)/g, \"\"), {\n headers: requestHeaders\n });\n const responseHeaders = new Headers(response.headers);\n responseHeaders.set(\n \"Content-Type\",\n responseHeaders.get(\"Content-Type\") ?? \"text/javascript\"\n );\n responseHeaders.set(\n \"Cache-Control\",\n responseHeaders.get(\"Cache-Control\") ?? \"\"\n );\n return new Response(response.body, {\n headers: responseHeaders\n });\n}\nconst ports = /* @__PURE__ */ new Set();\nchrome.runtime.onConnect.addListener((port) => {\n if (port.name === \"@crx/client\") {\n ports.add(port);\n port.onDisconnect.addListener((port2) => {\n if (chrome.runtime.lastError) {\n console.error(chrome.runtime.lastError);\n }\n ports.delete(port2);\n });\n port.onMessage.addListener((message) => {\n });\n port.postMessage({ data: JSON.stringify({ type: \"connected\" }) });\n }\n});\nfunction notifyContentScripts(payload) {\n const data = JSON.stringify(payload);\n for (const port of ports)\n port.postMessage({ data });\n}\nconsole.log(\"[vite] connecting...\");\nconst socketProtocol = __HMR_PROTOCOL__ || (location.protocol === \"https:\" ? \"wss\" : \"ws\");\nconst socketToken = __HMR_TOKEN__;\nconst socketHost = `${__HMR_HOSTNAME__ || location.hostname}:${__HMR_PORT__}`;\nconst socket = new WebSocket(\n `${socketProtocol}://${socketHost}?token=${socketToken}`,\n \"vite-hmr\"\n);\nconst base = __BASE__ || \"/\";\nsocket.addEventListener(\"message\", async ({ data }) => {\n handleSocketMessage(JSON.parse(data));\n});\nfunction isCrxHmrPayload(x) {\n return x.type === \"custom\" && x.event.startsWith(\"crx:\");\n}\nfunction handleSocketMessage(payload) {\n if (isCrxHmrPayload(payload)) {\n handleCrxHmrPayload(payload);\n } else if (payload.type === \"connected\") {\n console.log(`[vite] connected.`);\n const interval = setInterval(() => socket.send(\"ping\"), __HMR_TIMEOUT__);\n socket.addEventListener(\"close\", () => clearInterval(interval));\n }\n}\nfunction handleCrxHmrPayload(payload) {\n notifyContentScripts(payload);\n switch (payload.event) {\n case \"crx:runtime-reload\":\n console.log(\"[crx] runtime reload\");\n chrome.runtime.reload();\n break;\n }\n}\nasync function waitForSuccessfulPing(ms = 1e3) {\n while (true) {\n try {\n await fetch(`${base}__vite_ping`);\n break;\n } catch (e) {\n await new Promise((resolve) => setTimeout(resolve, ms));\n }\n }\n}\nsocket.addEventListener(\"close\", async ({ wasClean }) => {\n if (wasClean)\n return;\n console.log(`[vite] server connection lost. polling for restart...`);\n await waitForSuccessfulPing();\n handleCrxHmrPayload({\n
|
|
61
|
+
var workerHmrClient = "const ownOrigin = `chrome-extension://${chrome.runtime.id}`;\nself.addEventListener(\"fetch\", (fetchEvent) => {\n const url = new URL(fetchEvent.request.url);\n if (url.origin === ownOrigin) {\n fetchEvent.respondWith(sendToServer(fetchEvent.request));\n }\n});\nasync function sendToServer(req) {\n const url = new URL(req.url);\n const requestHeaders = new Headers(req.headers);\n url.protocol = __SERVER_PROTO__ + \":\";\n url.host = \"localhost\";\n url.port = __SERVER_PORT__;\n url.searchParams.set(\"t\", Date.now().toString());\n const response = await fetch(url.href.replace(/=$|=(?=&)/g, \"\"), {\n headers: requestHeaders\n });\n const responseHeaders = new Headers(response.headers);\n responseHeaders.set(\n \"Content-Type\",\n responseHeaders.get(\"Content-Type\") ?? \"text/javascript\"\n );\n responseHeaders.set(\n \"Cache-Control\",\n responseHeaders.get(\"Cache-Control\") ?? \"\"\n );\n return new Response(response.body, {\n headers: responseHeaders\n });\n}\nconst ports = /* @__PURE__ */ new Set();\nchrome.runtime.onConnect.addListener((port) => {\n if (port.name === \"@crx/client\") {\n ports.add(port);\n port.onDisconnect.addListener((port2) => {\n if (chrome.runtime.lastError) {\n console.error(chrome.runtime.lastError);\n }\n ports.delete(port2);\n });\n port.onMessage.addListener((message) => {\n });\n port.postMessage({ data: JSON.stringify({ type: \"connected\" }) });\n }\n});\nfunction notifyContentScripts(payload) {\n const data = JSON.stringify(payload);\n for (const port of ports)\n port.postMessage({ data });\n}\nconsole.log(\"[vite] connecting...\");\nconst socketProtocol = __HMR_PROTOCOL__ || (location.protocol === \"https:\" ? \"wss\" : \"ws\");\nconst socketToken = __HMR_TOKEN__;\nconst socketHost = `${__HMR_HOSTNAME__ || location.hostname}:${__HMR_PORT__}`;\nconst socket = new WebSocket(\n `${socketProtocol}://${socketHost}?token=${socketToken}`,\n \"vite-hmr\"\n);\nconst base = __BASE__ || \"/\";\nsocket.addEventListener(\"message\", async ({ data }) => {\n handleSocketMessage(JSON.parse(data));\n});\nfunction isCrxHmrPayload(x) {\n return x.type === \"custom\" && x.event.startsWith(\"crx:\");\n}\nfunction handleSocketMessage(payload) {\n if (isCrxHmrPayload(payload)) {\n handleCrxHmrPayload(payload);\n } else if (payload.type === \"connected\") {\n console.log(`[vite] connected.`);\n const interval = setInterval(() => socket.send(\"ping\"), __HMR_TIMEOUT__);\n socket.addEventListener(\"close\", () => clearInterval(interval));\n }\n}\nfunction handleCrxHmrPayload(payload) {\n if (!__LIVE_RELOAD__) {\n if (payload.event === \"crx:runtime-reload\") {\n console.log(\"[crx] runtime reload suppressed (liveReload disabled)\");\n }\n return;\n }\n notifyContentScripts(payload);\n switch (payload.event) {\n case \"crx:runtime-reload\":\n console.log(\"[crx] runtime reload\");\n chrome.runtime.reload();\n break;\n }\n}\nasync function waitForSuccessfulPing(ms = 1e3) {\n while (true) {\n try {\n await fetch(`${base}__vite_ping`);\n break;\n } catch (e) {\n await new Promise((resolve) => setTimeout(resolve, ms));\n }\n }\n}\nsocket.addEventListener(\"close\", async ({ wasClean }) => {\n if (wasClean)\n return;\n console.log(`[vite] server connection lost. polling for restart...`);\n await waitForSuccessfulPing();\n if (__LIVE_RELOAD__) {\n handleCrxHmrPayload({\n type: \"custom\",\n event: \"crx:runtime-reload\"\n });\n } else {\n console.log(\n \"[crx] server reconnected, skipping reload (liveReload disabled)\"\n );\n }\n});\n";
|
|
62
62
|
|
|
63
63
|
const _debug = (id) => debug$5("crx").extend(id);
|
|
64
64
|
const hash = (data, length = 5) => createHash("sha1").update(data).digest("base64").replace(/[^A-Za-z0-9]/g, "").slice(0, length);
|
|
@@ -209,7 +209,7 @@ function formatFileData(script) {
|
|
|
209
209
|
return script;
|
|
210
210
|
}
|
|
211
211
|
function getFileName({ type, id }) {
|
|
212
|
-
let fileName = id.replace(/t=\d+&/, "").replace(/\?t=\d+$/, "").replace(/^\//, "").replace(/\?/g, "__").replace(/&/g, "_").replace(/=/g, "--");
|
|
212
|
+
let fileName = id.replace(/t=\d+&/, "").replace(/\?t=\d+$/, "").replace(/^\//, "").replace(/\?/g, "__").replace(/&/g, "_").replace(/=/g, "--").replace(/:/g, "-");
|
|
213
213
|
if (fileName.includes("node_modules/")) {
|
|
214
214
|
fileName = `vendor/${fileName.split("node_modules/").pop().replace(/\//g, "-")}`;
|
|
215
215
|
} else if (fileName.startsWith("@")) {
|
|
@@ -244,11 +244,17 @@ function getOutputPath(server, fileName) {
|
|
|
244
244
|
const target = isAbsolute(outDir) ? join(outDir, fileName) : join(root, outDir, fileName);
|
|
245
245
|
return target;
|
|
246
246
|
}
|
|
247
|
-
function getViteUrl({ type, id }) {
|
|
247
|
+
function getViteUrl({ type, id }, { timestamp = false } = {}) {
|
|
248
|
+
if (timestamp && !id.startsWith("/@") && !id.includes("?v=")) {
|
|
249
|
+
const t = `t=${Date.now()}` + (id.includes("?") ? "&" : "");
|
|
250
|
+
const parts = id.split("?");
|
|
251
|
+
parts[1] = typeof parts[1] === "undefined" ? t : t + parts[1];
|
|
252
|
+
id = parts.join("?");
|
|
253
|
+
}
|
|
248
254
|
if (type === "asset") {
|
|
249
255
|
throw new Error(`File type "${type}" not implemented.`);
|
|
250
256
|
} else if (type === "iife") {
|
|
251
|
-
throw new Error(`File type "
|
|
257
|
+
throw new Error(`File type "iife" is handled via dedicated IIFE bundler, not Vite transform.`);
|
|
252
258
|
} else if (type === "loader") {
|
|
253
259
|
throw new Error("Vite does not transform loader files.");
|
|
254
260
|
} else if (type === "module") {
|
|
@@ -293,6 +299,7 @@ function getContentCssIndex(id) {
|
|
|
293
299
|
const pluginBackground = () => {
|
|
294
300
|
let config;
|
|
295
301
|
let browser;
|
|
302
|
+
let liveReload = true;
|
|
296
303
|
return [
|
|
297
304
|
{
|
|
298
305
|
name: "crx:background-client",
|
|
@@ -305,7 +312,7 @@ const pluginBackground = () => {
|
|
|
305
312
|
if (id === workerClientId) {
|
|
306
313
|
const base = `${config.server.https ? "https" : "http"}://localhost:${config.server.port}/`;
|
|
307
314
|
return defineClientValues(
|
|
308
|
-
workerHmrClient.replace("__BASE__", JSON.stringify(base)),
|
|
315
|
+
workerHmrClient.replace("__BASE__", JSON.stringify(base)).replace("__LIVE_RELOAD__", JSON.stringify(liveReload)),
|
|
309
316
|
config
|
|
310
317
|
);
|
|
311
318
|
}
|
|
@@ -318,6 +325,7 @@ const pluginBackground = () => {
|
|
|
318
325
|
async config(config2) {
|
|
319
326
|
const opts = await getOptions(config2);
|
|
320
327
|
browser = opts.browser || "chrome";
|
|
328
|
+
liveReload = opts.liveReload !== false;
|
|
321
329
|
},
|
|
322
330
|
configResolved(_config) {
|
|
323
331
|
config = _config;
|
|
@@ -376,7 +384,53 @@ const pluginBackground = () => {
|
|
|
376
384
|
];
|
|
377
385
|
};
|
|
378
386
|
|
|
379
|
-
|
|
387
|
+
const extensionOrigins = [/^chrome-extension:\/\//, /^moz-extension:\/\//];
|
|
388
|
+
function isExtensionOrigin(origin) {
|
|
389
|
+
return origin ? extensionOrigins.some((pattern) => pattern.test(origin)) : false;
|
|
390
|
+
}
|
|
391
|
+
function addExtensionOrigins(origin) {
|
|
392
|
+
if (origin === true)
|
|
393
|
+
return true;
|
|
394
|
+
if (typeof origin === "function") {
|
|
395
|
+
return (requestOrigin, cb) => {
|
|
396
|
+
if (isExtensionOrigin(requestOrigin)) {
|
|
397
|
+
cb(null, true);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
origin(requestOrigin, cb);
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
if (Array.isArray(origin))
|
|
404
|
+
return [...origin, ...extensionOrigins];
|
|
405
|
+
if (origin)
|
|
406
|
+
return [origin, ...extensionOrigins];
|
|
407
|
+
return extensionOrigins;
|
|
408
|
+
}
|
|
409
|
+
function addExtensionCors(cors) {
|
|
410
|
+
if (cors === true)
|
|
411
|
+
return true;
|
|
412
|
+
if (cors && typeof cors === "object") {
|
|
413
|
+
return {
|
|
414
|
+
...cors,
|
|
415
|
+
origin: addExtensionOrigins(cors.origin)
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
return { origin: extensionOrigins };
|
|
419
|
+
}
|
|
420
|
+
function pluginExtensionCors() {
|
|
421
|
+
return {
|
|
422
|
+
name: "crx:extension-cors",
|
|
423
|
+
apply: "serve",
|
|
424
|
+
config(config) {
|
|
425
|
+
config.server = {
|
|
426
|
+
...config.server,
|
|
427
|
+
cors: addExtensionCors(config.server?.cors)
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
var contentHmrPort = "function isCrxHMRPayload(x) {\n return x.type === \"custom\" && x.event.startsWith(\"crx:\");\n}\nclass HMRPort {\n port;\n callbacks = /* @__PURE__ */ new Map();\n constructor() {\n setInterval(() => {\n try {\n this.port?.postMessage({ data: \"ping\" });\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"Extension context invalidated.\")) {\n location.reload();\n } else\n throw error;\n }\n }, __CRX_HMR_TIMEOUT__);\n setInterval(this.initPort, 5 * 60 * 1e3);\n this.initPort();\n }\n initPort = () => {\n this.port?.disconnect();\n this.port = chrome.runtime.connect({ name: \"@crx/client\" });\n this.port.onDisconnect.addListener(this.handleDisconnect.bind(this));\n this.port.onMessage.addListener(this.handleMessage.bind(this));\n this.port.postMessage({ type: \"connected\" });\n };\n handleDisconnect = () => {\n if (this.callbacks.has(\"close\"))\n for (const cb of this.callbacks.get(\"close\")) {\n cb({ wasClean: true });\n }\n };\n handleMessage = (message) => {\n const forward = (data) => {\n if (this.callbacks.has(\"message\"))\n for (const cb of this.callbacks.get(\"message\")) {\n cb({ data });\n }\n };\n const payload = JSON.parse(message.data);\n if (isCrxHMRPayload(payload)) {\n if (payload.event === \"crx:runtime-reload\") {\n if (__CRX_LIVE_RELOAD__) {\n console.log(\"[crx] runtime reload\");\n setTimeout(() => location.reload(), 500);\n } else {\n console.log(\"[crx] runtime reload suppressed (liveReload disabled)\");\n }\n } else {\n forward(JSON.stringify(payload.data));\n }\n } else {\n forward(message.data);\n }\n };\n addEventListener = (event, callback) => {\n const cbs = this.callbacks.get(event) ?? /* @__PURE__ */ new Set();\n cbs.add(callback);\n this.callbacks.set(event, cbs);\n };\n send = (data) => {\n if (this.port)\n this.port.postMessage({ data });\n else\n throw new Error(\"HMRPort is not initialized\");\n };\n}\n\nexport { HMRPort };\n";
|
|
380
434
|
|
|
381
435
|
var contentDevLoader = "(function () {\n 'use strict';\n\n const injectTime = performance.now();\n (async () => {\n if (__PREAMBLE__)\n await import(\n /* @vite-ignore */\n chrome.runtime.getURL(__PREAMBLE__)\n );\n await import(\n /* @vite-ignore */\n chrome.runtime.getURL(__CLIENT__)\n );\n const { onExecute } = await import(\n /* @vite-ignore */\n chrome.runtime.getURL(__SCRIPT__)\n );\n onExecute?.({ perf: { injectTime, loadTime: performance.now() - injectTime } });\n })().catch(console.error);\n\n})();\n";
|
|
382
436
|
|
|
@@ -396,9 +450,10 @@ contentScripts.change$.pipe(filter(RxMap.isChangeType.set)).subscribe(({ map, va
|
|
|
396
450
|
"resolvedId",
|
|
397
451
|
"scriptId"
|
|
398
452
|
];
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
453
|
+
const keys = keyNames.map((keyName) => value[keyName]);
|
|
454
|
+
keys.push(value.id.replace(/^\//, ""));
|
|
455
|
+
for (const key of keys) {
|
|
456
|
+
if (typeof key === "undefined" || map.get(key) === value) {
|
|
402
457
|
continue;
|
|
403
458
|
} else {
|
|
404
459
|
map.set(key, value);
|
|
@@ -427,6 +482,21 @@ function createProMainLoader({ fileName }) {
|
|
|
427
482
|
return contentProMainLoader.replace(/__SCRIPT__/g, JSON.stringify(fileName));
|
|
428
483
|
}
|
|
429
484
|
|
|
485
|
+
const { outputFile: outputFile$1 } = fsx;
|
|
486
|
+
const getIifeGlobalName = (fileName) => {
|
|
487
|
+
const base = fileName.split("/").pop() ?? fileName;
|
|
488
|
+
const sanitized = base.replace(/\W+/g, "_").replace(/^_+/, "");
|
|
489
|
+
return `crx_${sanitized || "content_script"}`;
|
|
490
|
+
};
|
|
491
|
+
const resolveScriptInput = (server, id) => {
|
|
492
|
+
if (id.startsWith("/@fs/"))
|
|
493
|
+
return id.slice("/@fs/".length);
|
|
494
|
+
if (id.startsWith("/"))
|
|
495
|
+
return join(server.config.root, id.slice(1));
|
|
496
|
+
return id;
|
|
497
|
+
};
|
|
498
|
+
const isOutputChunk = (item) => item.type === "chunk";
|
|
499
|
+
const isOutputAsset = (item) => item.type === "asset";
|
|
430
500
|
const serverEvent$ = new ReplaySubject(1);
|
|
431
501
|
const close$ = serverEvent$.pipe(
|
|
432
502
|
filter((e) => e.type === "close"),
|
|
@@ -448,8 +518,17 @@ fileWriterEvent$.pipe(
|
|
|
448
518
|
const allFilesReady$ = buildEnd$.pipe(
|
|
449
519
|
switchMap(() => outputFiles.change$.pipe(startWith({ type: "start" }))),
|
|
450
520
|
map(() => [...outputFiles.values()]),
|
|
451
|
-
switchMap(
|
|
521
|
+
switchMap(
|
|
522
|
+
(files) => Promise.allSettled(files.map((file) => waitForOutputFile(file)))
|
|
523
|
+
)
|
|
452
524
|
);
|
|
525
|
+
async function waitForOutputFile(file, seen = /* @__PURE__ */ new Set()) {
|
|
526
|
+
if (seen.has(file))
|
|
527
|
+
return;
|
|
528
|
+
seen.add(file);
|
|
529
|
+
const { deps } = await file.file;
|
|
530
|
+
await Promise.all(deps.map((dep) => waitForOutputFile(dep, seen)));
|
|
531
|
+
}
|
|
453
532
|
const timestamp$ = new BehaviorSubject(Date.now());
|
|
454
533
|
allFilesReady$.subscribe(() => {
|
|
455
534
|
timestamp$.next(Date.now());
|
|
@@ -486,11 +565,20 @@ function prepAsset(fileName, { id, source }) {
|
|
|
486
565
|
);
|
|
487
566
|
}
|
|
488
567
|
function prepScript(fileName, script) {
|
|
568
|
+
if (script.type === "iife")
|
|
569
|
+
return prepIifeScript(fileName, script);
|
|
489
570
|
return ($) => $.pipe(
|
|
490
571
|
// get script contents from dev server
|
|
491
572
|
mergeMap(async ({ server }) => {
|
|
492
573
|
const target = getOutputPath(server, fileName);
|
|
493
|
-
const
|
|
574
|
+
const originalViteUrl = getViteUrl(script);
|
|
575
|
+
const isVueSfcQuery = script.id.includes("?vue");
|
|
576
|
+
const viteUrl = getViteUrl(script, { timestamp: isVueSfcQuery });
|
|
577
|
+
if (isVueSfcQuery) {
|
|
578
|
+
const module = await server.moduleGraph.getModuleByUrl(originalViteUrl);
|
|
579
|
+
if (module)
|
|
580
|
+
server.moduleGraph.invalidateModule(module);
|
|
581
|
+
}
|
|
494
582
|
const transformResult = await server.transformRequest(viteUrl);
|
|
495
583
|
if (!transformResult)
|
|
496
584
|
throw new TypeError(`Unable to load "${script.id}" from server.`);
|
|
@@ -543,12 +631,116 @@ ${sourceMap}
|
|
|
543
631
|
})
|
|
544
632
|
);
|
|
545
633
|
}
|
|
634
|
+
async function bundleIife(server, script, fileName) {
|
|
635
|
+
const input = resolveScriptInput(server, script.id);
|
|
636
|
+
const sourcemap = server.config.build.sourcemap === "inline" ? "inline" : false;
|
|
637
|
+
const result = await build({
|
|
638
|
+
root: server.config.root,
|
|
639
|
+
mode: server.config.mode,
|
|
640
|
+
configFile: false,
|
|
641
|
+
// Don't load user's config - use minimal IIFE-specific settings
|
|
642
|
+
logLevel: "silent",
|
|
643
|
+
resolve: {
|
|
644
|
+
// Copy resolve settings from the dev server for consistency
|
|
645
|
+
alias: server.config.resolve.alias,
|
|
646
|
+
extensions: server.config.resolve.extensions,
|
|
647
|
+
conditions: server.config.resolve.conditions
|
|
648
|
+
},
|
|
649
|
+
build: {
|
|
650
|
+
write: false,
|
|
651
|
+
// Don't write to disk, we'll handle that
|
|
652
|
+
manifest: false,
|
|
653
|
+
// Don't generate Vite manifest
|
|
654
|
+
rollupOptions: {
|
|
655
|
+
input,
|
|
656
|
+
output: {
|
|
657
|
+
format: "iife",
|
|
658
|
+
name: getIifeGlobalName(fileName),
|
|
659
|
+
entryFileNames: fileName,
|
|
660
|
+
inlineDynamicImports: true,
|
|
661
|
+
// Required for IIFE format
|
|
662
|
+
sourcemap
|
|
663
|
+
}
|
|
664
|
+
},
|
|
665
|
+
minify: false,
|
|
666
|
+
copyPublicDir: false
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
const outputs = Array.isArray(result) ? result : [result];
|
|
670
|
+
const firstOutput = outputs[0];
|
|
671
|
+
const output = "output" in firstOutput ? firstOutput.output : void 0;
|
|
672
|
+
if (!output) {
|
|
673
|
+
throw new Error(`Unable to generate IIFE bundle for "${script.id}"`);
|
|
674
|
+
}
|
|
675
|
+
const entryChunk = output.find(
|
|
676
|
+
(item) => isOutputChunk(item) && item.isEntry
|
|
677
|
+
);
|
|
678
|
+
if (!entryChunk) {
|
|
679
|
+
throw new Error(`Unable to generate IIFE bundle for "${script.id}"`);
|
|
680
|
+
}
|
|
681
|
+
const assets = output.filter(isOutputAsset).filter(
|
|
682
|
+
// Filter out manifest.json to avoid overwriting extension manifest
|
|
683
|
+
(asset) => asset.fileName !== "manifest.json" && !asset.fileName.startsWith(".vite/")
|
|
684
|
+
);
|
|
685
|
+
const extraChunks = output.filter(
|
|
686
|
+
(item) => isOutputChunk(item) && !item.isEntry
|
|
687
|
+
);
|
|
688
|
+
return {
|
|
689
|
+
code: entryChunk.code,
|
|
690
|
+
assets,
|
|
691
|
+
extraChunks
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
function prepIifeScript(fileName, script) {
|
|
695
|
+
return ($) => $.pipe(
|
|
696
|
+
mergeMap(async ({ server }) => {
|
|
697
|
+
const target = getOutputPath(server, fileName);
|
|
698
|
+
const { code, assets, extraChunks } = await bundleIife(
|
|
699
|
+
server,
|
|
700
|
+
script,
|
|
701
|
+
fileName
|
|
702
|
+
);
|
|
703
|
+
return { target, source: code, deps: [], server, assets, extraChunks };
|
|
704
|
+
}),
|
|
705
|
+
mergeMap(
|
|
706
|
+
async ({ target, source, deps, server, assets, extraChunks }) => {
|
|
707
|
+
const extras = [
|
|
708
|
+
...assets.map((asset) => ({
|
|
709
|
+
fileName: asset.fileName,
|
|
710
|
+
source: asset.source
|
|
711
|
+
})),
|
|
712
|
+
...extraChunks.map((chunk) => ({
|
|
713
|
+
fileName: chunk.fileName,
|
|
714
|
+
source: chunk.code
|
|
715
|
+
}))
|
|
716
|
+
].filter((item) => item.fileName !== fileName);
|
|
717
|
+
await Promise.all(
|
|
718
|
+
extras.map(async (item) => {
|
|
719
|
+
const outputPath = getOutputPath(server, item.fileName);
|
|
720
|
+
if (typeof item.source === "undefined" || item.source === null)
|
|
721
|
+
return;
|
|
722
|
+
if (item.source instanceof Uint8Array)
|
|
723
|
+
await outputFile$1(outputPath, item.source);
|
|
724
|
+
else
|
|
725
|
+
await outputFile$1(outputPath, item.source, { encoding: "utf8" });
|
|
726
|
+
})
|
|
727
|
+
);
|
|
728
|
+
return { target, source, deps };
|
|
729
|
+
}
|
|
730
|
+
)
|
|
731
|
+
);
|
|
732
|
+
}
|
|
546
733
|
async function allFilesReady() {
|
|
547
734
|
await firstValueFrom(allFilesReady$);
|
|
548
735
|
}
|
|
549
736
|
|
|
550
737
|
const { outputFile } = fsx;
|
|
551
738
|
const debug$4 = _debug("file-writer");
|
|
739
|
+
function queueWrite(script, previous) {
|
|
740
|
+
if (!previous)
|
|
741
|
+
return write(script);
|
|
742
|
+
return previous.file.catch(() => void 0).then(() => write(script));
|
|
743
|
+
}
|
|
552
744
|
async function start({
|
|
553
745
|
server
|
|
554
746
|
}) {
|
|
@@ -590,18 +782,24 @@ function add(script) {
|
|
|
590
782
|
file = formatFileData({
|
|
591
783
|
...script,
|
|
592
784
|
fileName,
|
|
593
|
-
file:
|
|
785
|
+
file: queueWrite(script)
|
|
594
786
|
});
|
|
595
787
|
outputFiles.set(file.fileName, file);
|
|
596
788
|
debug$4("add: stored new file %s", file.fileName);
|
|
597
789
|
} else {
|
|
598
790
|
const isVirtualModule = script.id.startsWith("/@id/") || script.id.startsWith("/__");
|
|
599
|
-
|
|
791
|
+
const isTimestampedModule = script.type === "module" && /[?&]t=\d+/.test(script.id);
|
|
792
|
+
if (isVirtualModule || isTimestampedModule) {
|
|
600
793
|
debug$4(
|
|
601
|
-
"add:
|
|
794
|
+
"add: module already exists, triggering re-write for %s",
|
|
602
795
|
fileName
|
|
603
796
|
);
|
|
604
|
-
file
|
|
797
|
+
file = formatFileData({
|
|
798
|
+
...file,
|
|
799
|
+
...script,
|
|
800
|
+
fileName,
|
|
801
|
+
file: queueWrite(script, file)
|
|
802
|
+
});
|
|
605
803
|
outputFiles.set(fileName, file);
|
|
606
804
|
}
|
|
607
805
|
}
|
|
@@ -618,7 +816,7 @@ function update(_id) {
|
|
|
618
816
|
const scriptFile = outputFiles.get(fileName);
|
|
619
817
|
if (scriptFile) {
|
|
620
818
|
debug$4("update: found file, calling write()");
|
|
621
|
-
scriptFile.file =
|
|
819
|
+
scriptFile.file = queueWrite({ id, type }, scriptFile);
|
|
622
820
|
updatedFiles.push(scriptFile);
|
|
623
821
|
outputFiles.set(fileName, scriptFile);
|
|
624
822
|
}
|
|
@@ -663,6 +861,7 @@ const pluginContentScripts = () => {
|
|
|
663
861
|
let server;
|
|
664
862
|
let preambleCode;
|
|
665
863
|
let hmrTimeout;
|
|
864
|
+
let liveReload = true;
|
|
666
865
|
let sub = new Subscription();
|
|
667
866
|
const worldMainIds = /* @__PURE__ */ new Set();
|
|
668
867
|
const findWorldMainIds = async (config, env) => {
|
|
@@ -690,9 +889,11 @@ const pluginContentScripts = () => {
|
|
|
690
889
|
apply: "serve",
|
|
691
890
|
async config(config, env) {
|
|
692
891
|
await findWorldMainIds(config, env);
|
|
693
|
-
const
|
|
892
|
+
const opts = await getOptions(config);
|
|
893
|
+
const { contentScripts: contentScripts2 = {} } = opts;
|
|
694
894
|
hmrTimeout = contentScripts2.hmrTimeout ?? 5e3;
|
|
695
895
|
preambleCode = preambleCode ?? contentScripts2.preambleCode;
|
|
896
|
+
liveReload = opts.liveReload !== false;
|
|
696
897
|
},
|
|
697
898
|
async configureServer(_server) {
|
|
698
899
|
server = _server;
|
|
@@ -702,7 +903,7 @@ const pluginContentScripts = () => {
|
|
|
702
903
|
try {
|
|
703
904
|
const react = await import('@vitejs/plugin-react');
|
|
704
905
|
preambleCode = react.default.preambleCode;
|
|
705
|
-
} catch
|
|
906
|
+
} catch {
|
|
706
907
|
preambleCode = false;
|
|
707
908
|
}
|
|
708
909
|
}
|
|
@@ -728,7 +929,8 @@ const pluginContentScripts = () => {
|
|
|
728
929
|
});
|
|
729
930
|
script.fileName = loader.fileName;
|
|
730
931
|
} else if (type === "iife") {
|
|
731
|
-
|
|
932
|
+
const file = add({ type: "iife", id });
|
|
933
|
+
script.fileName = file.fileName;
|
|
732
934
|
} else {
|
|
733
935
|
const file = add({ type: "module", id });
|
|
734
936
|
script.fileName = file.fileName;
|
|
@@ -748,10 +950,7 @@ const pluginContentScripts = () => {
|
|
|
748
950
|
return defined;
|
|
749
951
|
}
|
|
750
952
|
if (id === contentHmrPortId) {
|
|
751
|
-
const defined = contentHmrPort.replace(
|
|
752
|
-
"__CRX_HMR_TIMEOUT__",
|
|
753
|
-
JSON.stringify(hmrTimeout)
|
|
754
|
-
);
|
|
953
|
+
const defined = contentHmrPort.replace("__CRX_HMR_TIMEOUT__", JSON.stringify(hmrTimeout)).replace("__CRX_LIVE_RELOAD__", JSON.stringify(liveReload));
|
|
755
954
|
return defined;
|
|
756
955
|
}
|
|
757
956
|
},
|
|
@@ -779,41 +978,50 @@ const pluginContentScripts = () => {
|
|
|
779
978
|
};
|
|
780
979
|
},
|
|
781
980
|
generateBundle(_options, bundle) {
|
|
782
|
-
|
|
783
|
-
if (key === script.refId) {
|
|
784
|
-
if (script.type === "module") {
|
|
785
|
-
const fileName = this.getFileName(script.refId);
|
|
786
|
-
script.fileName = fileName;
|
|
787
|
-
} else if (script.type === "loader") {
|
|
788
|
-
const fileName = this.getFileName(script.refId);
|
|
789
|
-
script.fileName = fileName;
|
|
790
|
-
const bundleFileInfo = bundle[fileName];
|
|
791
|
-
const shouldUseLoader = !(bundleFileInfo.type === "chunk" && bundleFileInfo.imports.length === 0 && bundleFileInfo.dynamicImports.length === 0 && bundleFileInfo.exports.length === 0);
|
|
792
|
-
if (shouldUseLoader) {
|
|
793
|
-
const refId = this.emitFile({
|
|
794
|
-
type: "asset",
|
|
795
|
-
name: getFileName({
|
|
796
|
-
type: "loader",
|
|
797
|
-
id: basename(script.id)
|
|
798
|
-
}),
|
|
799
|
-
source: worldMainIds.has(script.id) ? createProMainLoader({
|
|
800
|
-
fileName: `./${fileName.split("/").at(-1)}`
|
|
801
|
-
}) : createProLoader({ fileName })
|
|
802
|
-
});
|
|
803
|
-
script.loaderName = this.getFileName(refId);
|
|
804
|
-
} else {
|
|
805
|
-
bundleFileInfo.code = `(function(){${bundleFileInfo.code}})()
|
|
806
|
-
`;
|
|
807
|
-
}
|
|
808
|
-
} else if (script.type === "iife") {
|
|
809
|
-
throw new Error("IIFE content scripts are not implemented");
|
|
810
|
-
}
|
|
811
|
-
contentScripts.set(script.refId, formatFileData(script));
|
|
812
|
-
}
|
|
981
|
+
finalizeBuildContentScripts(this, bundle, worldMainIds);
|
|
813
982
|
}
|
|
814
983
|
}
|
|
815
984
|
];
|
|
816
985
|
};
|
|
986
|
+
function finalizeBuildContentScripts(context, bundle, worldMainIds = /* @__PURE__ */ new Set()) {
|
|
987
|
+
const processed = /* @__PURE__ */ new Set();
|
|
988
|
+
for (const [key, script] of contentScripts) {
|
|
989
|
+
if (key !== script.refId || processed.has(script))
|
|
990
|
+
continue;
|
|
991
|
+
processed.add(script);
|
|
992
|
+
if (script.type === "module") {
|
|
993
|
+
script.fileName = script.fileName ?? context.getFileName(script.refId);
|
|
994
|
+
} else if (script.type === "loader") {
|
|
995
|
+
const fileName = script.fileName ?? context.getFileName(script.refId);
|
|
996
|
+
script.fileName = fileName;
|
|
997
|
+
const bundleFileInfo = bundle[fileName];
|
|
998
|
+
if (bundleFileInfo?.type !== "chunk")
|
|
999
|
+
continue;
|
|
1000
|
+
const shouldUseLoader = !(bundleFileInfo.imports.length === 0 && bundleFileInfo.dynamicImports.length === 0 && bundleFileInfo.exports.length === 0);
|
|
1001
|
+
if (shouldUseLoader) {
|
|
1002
|
+
if (typeof script.loaderName === "undefined") {
|
|
1003
|
+
const refId = context.emitFile({
|
|
1004
|
+
type: "asset",
|
|
1005
|
+
name: getFileName({
|
|
1006
|
+
type: "loader",
|
|
1007
|
+
id: basename(script.id)
|
|
1008
|
+
}),
|
|
1009
|
+
source: worldMainIds.has(script.id) ? createProMainLoader({
|
|
1010
|
+
fileName: `./${fileName.split("/").at(-1)}`
|
|
1011
|
+
}) : createProLoader({ fileName })
|
|
1012
|
+
});
|
|
1013
|
+
script.loaderName = context.getFileName(refId);
|
|
1014
|
+
}
|
|
1015
|
+
} else if (typeof script.loaderName === "undefined" && !bundleFileInfo.code.startsWith("(function(){")) {
|
|
1016
|
+
bundleFileInfo.code = `(function(){${bundleFileInfo.code}})()
|
|
1017
|
+
`;
|
|
1018
|
+
}
|
|
1019
|
+
} else if (script.type === "iife") {
|
|
1020
|
+
continue;
|
|
1021
|
+
}
|
|
1022
|
+
contentScripts.set(script.refId, formatFileData(script));
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
817
1025
|
|
|
818
1026
|
const pluginContentScriptsCss = () => {
|
|
819
1027
|
let injectCss;
|
|
@@ -890,6 +1098,196 @@ const pluginDeclaredContentScripts = () => {
|
|
|
890
1098
|
};
|
|
891
1099
|
};
|
|
892
1100
|
|
|
1101
|
+
function isIifeContentScript(file) {
|
|
1102
|
+
return /\.iife\.(ts|tsx|js|jsx|mjs|cjs)$/.test(file);
|
|
1103
|
+
}
|
|
1104
|
+
const pluginContentScriptsIife = () => {
|
|
1105
|
+
const pluginName = "crx:content-scripts-iife";
|
|
1106
|
+
let config;
|
|
1107
|
+
return [
|
|
1108
|
+
{
|
|
1109
|
+
name: `${pluginName}-config`,
|
|
1110
|
+
enforce: "pre",
|
|
1111
|
+
configResolved(resolvedConfig) {
|
|
1112
|
+
config = resolvedConfig;
|
|
1113
|
+
}
|
|
1114
|
+
},
|
|
1115
|
+
{
|
|
1116
|
+
name: pluginName,
|
|
1117
|
+
apply: "build",
|
|
1118
|
+
enforce: "post",
|
|
1119
|
+
async generateBundle() {
|
|
1120
|
+
const opts = await getOptions({ plugins: config.plugins });
|
|
1121
|
+
const _manifest = opts.manifest;
|
|
1122
|
+
const manifest = typeof _manifest === "function" ? await _manifest({ command: "build", mode: config.mode }) : await Promise.resolve(_manifest);
|
|
1123
|
+
const standaloneFiles = (opts.contentScripts?.standaloneFiles || []).map(
|
|
1124
|
+
normalizeContentScriptPath
|
|
1125
|
+
);
|
|
1126
|
+
const iifeEntries = collectIifeEntries(
|
|
1127
|
+
manifest,
|
|
1128
|
+
standaloneFiles,
|
|
1129
|
+
config.root
|
|
1130
|
+
);
|
|
1131
|
+
if (iifeEntries.length === 0)
|
|
1132
|
+
return;
|
|
1133
|
+
console.log(
|
|
1134
|
+
pc.cyan(`
|
|
1135
|
+
[${pluginName}] Building ${iifeEntries.length} content script(s) as IIFE...`)
|
|
1136
|
+
);
|
|
1137
|
+
for (const entry of iifeEntries) {
|
|
1138
|
+
const outputFileName = getIifeOutputPath(entry.file);
|
|
1139
|
+
try {
|
|
1140
|
+
const iifeConfig = createIifeConfig(config, entry.id, outputFileName);
|
|
1141
|
+
const result = await build(iifeConfig);
|
|
1142
|
+
const outputs = getBuildOutputs(result, entry.file);
|
|
1143
|
+
emitIifeOutputs(this, outputs, entry.file, outputFileName);
|
|
1144
|
+
registerIifeContentScript(entry, outputFileName);
|
|
1145
|
+
console.log(
|
|
1146
|
+
pc.green(` \u2713 ${basename(entry.file)} \u2192 ${outputFileName}`)
|
|
1147
|
+
);
|
|
1148
|
+
} catch (error) {
|
|
1149
|
+
console.error(
|
|
1150
|
+
pc.red(` \u2717 Failed to build ${entry.file}:`),
|
|
1151
|
+
error
|
|
1152
|
+
);
|
|
1153
|
+
throw error;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
console.log(pc.cyan(`[${pluginName}] IIFE build complete
|
|
1157
|
+
`));
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
];
|
|
1161
|
+
};
|
|
1162
|
+
function normalizeContentScriptPath(file) {
|
|
1163
|
+
return file.replace(/^\//, "");
|
|
1164
|
+
}
|
|
1165
|
+
function isStandaloneFile(file, standaloneFiles) {
|
|
1166
|
+
return standaloneFiles.includes(normalizeContentScriptPath(file));
|
|
1167
|
+
}
|
|
1168
|
+
function collectIifeEntries(manifest, standaloneFiles, root) {
|
|
1169
|
+
const entries = [];
|
|
1170
|
+
const entryIds = /* @__PURE__ */ new Set();
|
|
1171
|
+
const addEntry = (entry) => {
|
|
1172
|
+
if (entryIds.has(entry.id))
|
|
1173
|
+
return;
|
|
1174
|
+
entryIds.add(entry.id);
|
|
1175
|
+
entries.push(entry);
|
|
1176
|
+
};
|
|
1177
|
+
for (const { js = [], matches = [] } of manifest.content_scripts ?? []) {
|
|
1178
|
+
for (const file of js) {
|
|
1179
|
+
if (isIifeContentScript(file) || isStandaloneFile(file, standaloneFiles)) {
|
|
1180
|
+
addEntry({ file, id: join(root, file), matches });
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
for (const [, script] of contentScripts.entries()) {
|
|
1185
|
+
if (script.type !== "iife" || !script.isDynamicScript)
|
|
1186
|
+
continue;
|
|
1187
|
+
addEntry({
|
|
1188
|
+
file: script.id,
|
|
1189
|
+
id: join(root, script.id),
|
|
1190
|
+
matches: script.matches ?? []
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
return entries;
|
|
1194
|
+
}
|
|
1195
|
+
function getBuildOutputs(result, entryFile) {
|
|
1196
|
+
if ("on" in result) {
|
|
1197
|
+
throw new Error(`Unexpected watcher result for "${entryFile}"`);
|
|
1198
|
+
}
|
|
1199
|
+
return Array.isArray(result) ? result.flatMap((r) => r.output) : result.output;
|
|
1200
|
+
}
|
|
1201
|
+
function emitIifeOutputs(context, outputs, entryFile, outputFileName) {
|
|
1202
|
+
const entryChunk = outputs.find(
|
|
1203
|
+
(output) => output.type === "chunk" && output.isEntry
|
|
1204
|
+
);
|
|
1205
|
+
if (!entryChunk) {
|
|
1206
|
+
throw new Error(`Unable to generate IIFE bundle for "${entryFile}"`);
|
|
1207
|
+
}
|
|
1208
|
+
context.emitFile({
|
|
1209
|
+
type: "asset",
|
|
1210
|
+
fileName: outputFileName,
|
|
1211
|
+
source: entryChunk.code
|
|
1212
|
+
});
|
|
1213
|
+
for (const output of outputs) {
|
|
1214
|
+
if (output.type === "asset") {
|
|
1215
|
+
if (output.fileName !== outputFileName && output.fileName !== "manifest.json" && !output.fileName.startsWith(".vite/")) {
|
|
1216
|
+
context.emitFile({
|
|
1217
|
+
type: "asset",
|
|
1218
|
+
fileName: output.fileName,
|
|
1219
|
+
source: output.source
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
} else if (!output.isEntry) {
|
|
1223
|
+
context.emitFile({
|
|
1224
|
+
type: "asset",
|
|
1225
|
+
fileName: output.fileName,
|
|
1226
|
+
source: output.code
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
function registerIifeContentScript(entry, outputFileName) {
|
|
1232
|
+
const existingScript = contentScripts.get(entry.file);
|
|
1233
|
+
const script = existingScript ? { ...existingScript, fileName: outputFileName } : {
|
|
1234
|
+
type: "iife",
|
|
1235
|
+
id: entry.file,
|
|
1236
|
+
refId: entry.file,
|
|
1237
|
+
matches: entry.matches,
|
|
1238
|
+
fileName: outputFileName
|
|
1239
|
+
};
|
|
1240
|
+
contentScripts.set(entry.file, formatFileData(script));
|
|
1241
|
+
}
|
|
1242
|
+
function getIifeOutputPath(file) {
|
|
1243
|
+
const normalizedFile = file.replace(/^\//, "");
|
|
1244
|
+
const dir = dirname(normalizedFile);
|
|
1245
|
+
const name = basename(normalizedFile).replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, "");
|
|
1246
|
+
return dir && dir !== "." ? `${dir}/${name}.js` : `${name}.js`;
|
|
1247
|
+
}
|
|
1248
|
+
function createIifeConfig(parentConfig, entryPath, outputFileName) {
|
|
1249
|
+
const entryName = basename(outputFileName, ".js");
|
|
1250
|
+
const baseConfig = {
|
|
1251
|
+
// Inherit relevant settings from parent config
|
|
1252
|
+
root: parentConfig.root,
|
|
1253
|
+
mode: parentConfig.mode,
|
|
1254
|
+
resolve: parentConfig.resolve,
|
|
1255
|
+
define: parentConfig.define,
|
|
1256
|
+
esbuild: parentConfig.esbuild
|
|
1257
|
+
};
|
|
1258
|
+
const iifeConfig = {
|
|
1259
|
+
configFile: false,
|
|
1260
|
+
logLevel: "warn",
|
|
1261
|
+
plugins: [],
|
|
1262
|
+
build: {
|
|
1263
|
+
write: false,
|
|
1264
|
+
// Don't write to disk, we'll add to bundle
|
|
1265
|
+
emptyOutDir: false,
|
|
1266
|
+
lib: {
|
|
1267
|
+
entry: entryPath,
|
|
1268
|
+
formats: ["iife"],
|
|
1269
|
+
name: safeVarName(entryName),
|
|
1270
|
+
fileName: () => outputFileName
|
|
1271
|
+
},
|
|
1272
|
+
rollupOptions: {
|
|
1273
|
+
output: {
|
|
1274
|
+
entryFileNames: outputFileName,
|
|
1275
|
+
// Ensure all dependencies are inlined
|
|
1276
|
+
inlineDynamicImports: true
|
|
1277
|
+
}
|
|
1278
|
+
},
|
|
1279
|
+
// Match parent config settings
|
|
1280
|
+
minify: parentConfig.build.minify,
|
|
1281
|
+
sourcemap: parentConfig.build.sourcemap,
|
|
1282
|
+
target: parentConfig.build.target
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
return mergeConfig(baseConfig, iifeConfig);
|
|
1286
|
+
}
|
|
1287
|
+
function safeVarName(name) {
|
|
1288
|
+
return name.replace(/[^a-zA-Z0-9_$]/g, "_").replace(/^(\d)/, "_$1").replace(/^$/, "_");
|
|
1289
|
+
}
|
|
1290
|
+
|
|
893
1291
|
const _dynamicScriptRegEx = /\b(import.meta).CRX_DYNAMIC_SCRIPT_(.+?)[,;]/gm;
|
|
894
1292
|
const dynamicScriptRegEx = () => {
|
|
895
1293
|
_dynamicScriptRegEx.lastIndex = 0;
|
|
@@ -897,12 +1295,48 @@ const dynamicScriptRegEx = () => {
|
|
|
897
1295
|
};
|
|
898
1296
|
const pluginDynamicContentScripts = () => {
|
|
899
1297
|
let config;
|
|
1298
|
+
let standaloneFiles = [];
|
|
900
1299
|
return [
|
|
901
1300
|
{
|
|
902
1301
|
name: "crx:dynamic-content-scripts-loader",
|
|
903
1302
|
enforce: "pre",
|
|
904
1303
|
configResolved(_config) {
|
|
905
1304
|
config = _config;
|
|
1305
|
+
getOptions({ plugins: _config.plugins }).then((opts) => {
|
|
1306
|
+
standaloneFiles = (opts.contentScripts?.standaloneFiles || []).map(
|
|
1307
|
+
(f) => f.replace(/^\//, "")
|
|
1308
|
+
);
|
|
1309
|
+
}).catch(() => void 0);
|
|
1310
|
+
},
|
|
1311
|
+
buildStart() {
|
|
1312
|
+
if (config.command === "build") {
|
|
1313
|
+
const dynamicScripts = [];
|
|
1314
|
+
for (const [key, script] of contentScripts) {
|
|
1315
|
+
if (script.isDynamicScript && key === script.scriptId) {
|
|
1316
|
+
dynamicScripts.push(script);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
contentScripts.clear();
|
|
1320
|
+
for (const script of dynamicScripts) {
|
|
1321
|
+
const absoluteId = script.id.startsWith("/") ? `${config.root}${script.id}` : `${config.root}/${script.id}`;
|
|
1322
|
+
const refId = this.emitFile({
|
|
1323
|
+
type: "chunk",
|
|
1324
|
+
id: absoluteId,
|
|
1325
|
+
name: basename(script.id)
|
|
1326
|
+
});
|
|
1327
|
+
contentScripts.set(
|
|
1328
|
+
script.id,
|
|
1329
|
+
formatFileData({
|
|
1330
|
+
type: script.type,
|
|
1331
|
+
id: script.id,
|
|
1332
|
+
isDynamicScript: true,
|
|
1333
|
+
refId,
|
|
1334
|
+
scriptId: script.scriptId,
|
|
1335
|
+
matches: script.matches
|
|
1336
|
+
})
|
|
1337
|
+
);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
906
1340
|
},
|
|
907
1341
|
configureServer(server) {
|
|
908
1342
|
return () => {
|
|
@@ -933,9 +1367,9 @@ const pluginDynamicContentScripts = () => {
|
|
|
933
1367
|
};
|
|
934
1368
|
},
|
|
935
1369
|
async resolveId(_source, importer) {
|
|
936
|
-
if (importer && _source.includes("?script")) {
|
|
1370
|
+
if (importer && (_source.includes("?script") || _source.includes("?iife"))) {
|
|
937
1371
|
const url = new URL(_source, "stub://stub");
|
|
938
|
-
if (url.searchParams.has("script")) {
|
|
1372
|
+
if (url.searchParams.has("script") || url.searchParams.has("iife")) {
|
|
939
1373
|
const [source] = _source.split("?");
|
|
940
1374
|
const resolved = await this.resolve(source, importer, {
|
|
941
1375
|
skipSelf: true
|
|
@@ -945,8 +1379,11 @@ const pluginDynamicContentScripts = () => {
|
|
|
945
1379
|
`Could not resolve dynamic script: "${_source}" from "${importer}"`
|
|
946
1380
|
);
|
|
947
1381
|
const { id } = resolved;
|
|
1382
|
+
const relId = relative(config.root, id).replace(/^\//, "");
|
|
948
1383
|
let type = "loader";
|
|
949
|
-
if (
|
|
1384
|
+
if (isIifeContentScript(relId) || standaloneFiles.includes(relId)) {
|
|
1385
|
+
type = "iife";
|
|
1386
|
+
} else if (url.searchParams.has("module")) {
|
|
950
1387
|
type = "module";
|
|
951
1388
|
} else if (url.searchParams.has("iife")) {
|
|
952
1389
|
type = "iife";
|
|
@@ -962,17 +1399,20 @@ const pluginDynamicContentScripts = () => {
|
|
|
962
1399
|
refId = this.emitFile({
|
|
963
1400
|
type: "chunk",
|
|
964
1401
|
id,
|
|
965
|
-
name: basename(id)
|
|
1402
|
+
name: basename(id),
|
|
1403
|
+
// Preserve content script entry exports so the build finalizer
|
|
1404
|
+
// can decide whether the script needs a loader wrapper.
|
|
1405
|
+
preserveSignature: "exports-only"
|
|
966
1406
|
});
|
|
967
1407
|
} else {
|
|
968
1408
|
refId = scriptId;
|
|
969
|
-
const
|
|
1409
|
+
const relId2 = relative(config.root, id);
|
|
970
1410
|
fileName = getFileName({
|
|
971
1411
|
type: type === "iife" ? "iife" : "module",
|
|
972
|
-
id:
|
|
1412
|
+
id: relId2
|
|
973
1413
|
});
|
|
974
1414
|
if (type === "loader")
|
|
975
|
-
loaderName = getFileName({ type, id:
|
|
1415
|
+
loaderName = getFileName({ type, id: relId2 });
|
|
976
1416
|
}
|
|
977
1417
|
script = formatFileData({
|
|
978
1418
|
type,
|
|
@@ -996,9 +1436,29 @@ const pluginDynamicContentScripts = () => {
|
|
|
996
1436
|
const index = id.indexOf("?scriptId=");
|
|
997
1437
|
if (index > -1) {
|
|
998
1438
|
const scriptId = id.slice(index + "?scriptId=".length);
|
|
999
|
-
|
|
1439
|
+
let script = contentScripts.get(scriptId);
|
|
1440
|
+
if (!script && config.command === "build") {
|
|
1441
|
+
const fileId = id.slice(0, index);
|
|
1442
|
+
const refId = this.emitFile({
|
|
1443
|
+
type: "chunk",
|
|
1444
|
+
id: fileId,
|
|
1445
|
+
name: basename(fileId)
|
|
1446
|
+
});
|
|
1447
|
+
script = formatFileData({
|
|
1448
|
+
type: "loader",
|
|
1449
|
+
id: relative(config.root, fileId),
|
|
1450
|
+
isDynamicScript: true,
|
|
1451
|
+
refId,
|
|
1452
|
+
scriptId,
|
|
1453
|
+
matches: []
|
|
1454
|
+
});
|
|
1455
|
+
contentScripts.set(script.id, script);
|
|
1456
|
+
}
|
|
1457
|
+
if (!script) {
|
|
1458
|
+
throw new Error(`Content script not found for scriptId: "${scriptId}"`);
|
|
1459
|
+
}
|
|
1000
1460
|
if (config.command === "build") {
|
|
1001
|
-
return `export default import.meta.CRX_DYNAMIC_SCRIPT_${script.
|
|
1461
|
+
return `export default import.meta.CRX_DYNAMIC_SCRIPT_${script.scriptId};`;
|
|
1002
1462
|
} else if (typeof script.fileName === "string") {
|
|
1003
1463
|
return `export default ${JSON.stringify(script.fileName)};`;
|
|
1004
1464
|
} else {
|
|
@@ -1018,6 +1478,7 @@ const pluginDynamicContentScripts = () => {
|
|
|
1018
1478
|
* Can't use `renderChunk` b/c pre plugin crx:content-scripts uses
|
|
1019
1479
|
* `generateBundle` to emit loaders. Must come after "enforce: pre".
|
|
1020
1480
|
*/
|
|
1481
|
+
enforce: "post",
|
|
1021
1482
|
generateBundle(options, bundle) {
|
|
1022
1483
|
for (const chunk of Object.values(bundle))
|
|
1023
1484
|
if (chunk.type === "chunk") {
|
|
@@ -1034,9 +1495,9 @@ const pluginDynamicContentScripts = () => {
|
|
|
1034
1495
|
throw new Error(
|
|
1035
1496
|
`Content script fileName is undefined: "${script.id}"`
|
|
1036
1497
|
);
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
)}${match.split(scriptKey)[1]}`;
|
|
1498
|
+
const fileName = script.loaderName ?? script.fileName;
|
|
1499
|
+
const path = fileName;
|
|
1500
|
+
return `${JSON.stringify(path)}${match.split(scriptKey)[1]}`;
|
|
1040
1501
|
}
|
|
1041
1502
|
);
|
|
1042
1503
|
chunk.code = replaced;
|
|
@@ -1121,7 +1582,7 @@ const pluginFileWriterPolyfill = () => {
|
|
|
1121
1582
|
async function manifestFiles(manifest, options = {}) {
|
|
1122
1583
|
let locales = [];
|
|
1123
1584
|
if (manifest.default_locale)
|
|
1124
|
-
locales = await
|
|
1585
|
+
locales = await glob("_locales/**/messages.json", options);
|
|
1125
1586
|
const rulesets = manifest.declarative_net_request?.rule_resources.flatMap(
|
|
1126
1587
|
({ path }) => path
|
|
1127
1588
|
) ?? [];
|
|
@@ -1145,8 +1606,8 @@ async function manifestFiles(manifest, options = {}) {
|
|
|
1145
1606
|
manifest.web_accessible_resources.flatMap(({ resources: resources2 }) => resources2).map(async (r) => {
|
|
1146
1607
|
if (["*", "**/*"].includes(r))
|
|
1147
1608
|
return void 0;
|
|
1148
|
-
if (
|
|
1149
|
-
return
|
|
1609
|
+
if (isDynamicPattern(r))
|
|
1610
|
+
return glob(r, options);
|
|
1150
1611
|
return r;
|
|
1151
1612
|
})
|
|
1152
1613
|
);
|
|
@@ -1164,7 +1625,7 @@ async function manifestFiles(manifest, options = {}) {
|
|
|
1164
1625
|
};
|
|
1165
1626
|
}
|
|
1166
1627
|
async function dirFiles(dir) {
|
|
1167
|
-
const files = await
|
|
1628
|
+
const files = await glob(join(dir, "**", "*"));
|
|
1168
1629
|
return files;
|
|
1169
1630
|
}
|
|
1170
1631
|
function htmlFiles(manifest) {
|
|
@@ -1205,6 +1666,89 @@ const isCustomPayload = (p) => {
|
|
|
1205
1666
|
return p.type === "custom";
|
|
1206
1667
|
};
|
|
1207
1668
|
const hmrPayload$ = new Subject();
|
|
1669
|
+
function withTimestamp(id, timestamp) {
|
|
1670
|
+
if (id.includes("?t=") || id.includes("&t="))
|
|
1671
|
+
return id;
|
|
1672
|
+
const t = `t=${timestamp}` + (id.includes("?") ? "&" : "");
|
|
1673
|
+
const parts = id.split("?");
|
|
1674
|
+
parts[1] = typeof parts[1] === "undefined" ? t : t + parts[1];
|
|
1675
|
+
return parts.join("?");
|
|
1676
|
+
}
|
|
1677
|
+
function getUpdatePayloadFileIds(p, { timestamp = false } = {}) {
|
|
1678
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1679
|
+
for (const u of p.updates) {
|
|
1680
|
+
for (const id of [u.path, u.acceptedPath]) {
|
|
1681
|
+
const isVirtualModule = id.startsWith("/@id/") || id.startsWith("/__");
|
|
1682
|
+
const isQueryModule = id.includes("?");
|
|
1683
|
+
if (isVirtualModule || isQueryModule)
|
|
1684
|
+
ids.add(timestamp ? withTimestamp(id, u.timestamp) : id);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
return [...ids];
|
|
1688
|
+
}
|
|
1689
|
+
function mapVitePayloadForCrx(p) {
|
|
1690
|
+
switch (p.type) {
|
|
1691
|
+
case "full-reload": {
|
|
1692
|
+
const fullReload = {
|
|
1693
|
+
type: "full-reload",
|
|
1694
|
+
path: p.path && getViteUrl({ id: p.path, type: "module" })
|
|
1695
|
+
};
|
|
1696
|
+
return fullReload;
|
|
1697
|
+
}
|
|
1698
|
+
case "prune": {
|
|
1699
|
+
const prune = {
|
|
1700
|
+
type: "prune",
|
|
1701
|
+
paths: p.paths.map((id) => getViteUrl({ id, type: "module" }))
|
|
1702
|
+
};
|
|
1703
|
+
return prune;
|
|
1704
|
+
}
|
|
1705
|
+
case "update": {
|
|
1706
|
+
debug$3("update payload with %d updates", p.updates.length);
|
|
1707
|
+
for (const u of p.updates) {
|
|
1708
|
+
debug$3(
|
|
1709
|
+
"update item: path=%s acceptedPath=%s type=%s",
|
|
1710
|
+
u.path,
|
|
1711
|
+
u.acceptedPath,
|
|
1712
|
+
u.type
|
|
1713
|
+
);
|
|
1714
|
+
}
|
|
1715
|
+
const update_ = {
|
|
1716
|
+
type: "update",
|
|
1717
|
+
updates: p.updates.map(({ acceptedPath: ap, path: p2, ...rest }) => ({
|
|
1718
|
+
...rest,
|
|
1719
|
+
acceptedPath: prefix$1("/", getFileName({ id: ap, type: "module" })),
|
|
1720
|
+
path: prefix$1("/", getFileName({ id: p2, type: "module" }))
|
|
1721
|
+
}))
|
|
1722
|
+
};
|
|
1723
|
+
return update_;
|
|
1724
|
+
}
|
|
1725
|
+
default:
|
|
1726
|
+
return p;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
async function prepareVitePayloadForCrx(p) {
|
|
1730
|
+
if (p.type === "update") {
|
|
1731
|
+
const pendingFiles = getUpdatePayloadFileIds(p, {
|
|
1732
|
+
timestamp: true
|
|
1733
|
+
}).flatMap((id) => {
|
|
1734
|
+
debug$3("updating payload module: %s", id);
|
|
1735
|
+
return update(id).map((file) => file.file);
|
|
1736
|
+
});
|
|
1737
|
+
await Promise.all(pendingFiles);
|
|
1738
|
+
await firstValueFrom(allFilesReady$);
|
|
1739
|
+
}
|
|
1740
|
+
return mapVitePayloadForCrx(p);
|
|
1741
|
+
}
|
|
1742
|
+
function shouldForwardCrxPayload(p) {
|
|
1743
|
+
switch (p.type) {
|
|
1744
|
+
case "prune":
|
|
1745
|
+
return p.paths.length > 0;
|
|
1746
|
+
case "update":
|
|
1747
|
+
return p.updates.length > 0;
|
|
1748
|
+
default:
|
|
1749
|
+
return true;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1208
1752
|
const crxHMRPayload$ = hmrPayload$.pipe(
|
|
1209
1753
|
filter((p) => !isCustomPayload(p)),
|
|
1210
1754
|
buffer(allFilesReady$),
|
|
@@ -1221,63 +1765,8 @@ const crxHMRPayload$ = hmrPayload$.pipe(
|
|
|
1221
1765
|
payloads.push(fullReload);
|
|
1222
1766
|
return payloads;
|
|
1223
1767
|
}),
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
case "full-reload": {
|
|
1227
|
-
const fullReload = {
|
|
1228
|
-
type: "full-reload",
|
|
1229
|
-
path: p.path && getViteUrl({ id: p.path, type: "module" })
|
|
1230
|
-
};
|
|
1231
|
-
return fullReload;
|
|
1232
|
-
}
|
|
1233
|
-
case "prune": {
|
|
1234
|
-
const prune = {
|
|
1235
|
-
type: "prune",
|
|
1236
|
-
paths: p.paths.map((id) => getViteUrl({ id, type: "module" }))
|
|
1237
|
-
};
|
|
1238
|
-
return prune;
|
|
1239
|
-
}
|
|
1240
|
-
case "update": {
|
|
1241
|
-
debug$3("update payload with %d updates", p.updates.length);
|
|
1242
|
-
for (const u of p.updates) {
|
|
1243
|
-
debug$3(
|
|
1244
|
-
"update item: path=%s acceptedPath=%s type=%s",
|
|
1245
|
-
u.path,
|
|
1246
|
-
u.acceptedPath,
|
|
1247
|
-
u.type
|
|
1248
|
-
);
|
|
1249
|
-
const isVirtualModule = u.path.startsWith("/@id/") || u.path.startsWith("/__");
|
|
1250
|
-
if (isVirtualModule) {
|
|
1251
|
-
debug$3("updating virtual module: %s", u.path);
|
|
1252
|
-
update(u.path);
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
const update_ = {
|
|
1256
|
-
type: "update",
|
|
1257
|
-
updates: p.updates.map(({ acceptedPath: ap, path: p2, ...rest }) => ({
|
|
1258
|
-
...rest,
|
|
1259
|
-
acceptedPath: prefix$1("/", getFileName({ id: ap, type: "module" })),
|
|
1260
|
-
path: prefix$1("/", getFileName({ id: p2, type: "module" }))
|
|
1261
|
-
}))
|
|
1262
|
-
};
|
|
1263
|
-
return update_;
|
|
1264
|
-
}
|
|
1265
|
-
default:
|
|
1266
|
-
return p;
|
|
1267
|
-
}
|
|
1268
|
-
}),
|
|
1269
|
-
filter((p) => {
|
|
1270
|
-
switch (p.type) {
|
|
1271
|
-
case "full-reload":
|
|
1272
|
-
return typeof p.path === "undefined";
|
|
1273
|
-
case "prune":
|
|
1274
|
-
return p.paths.length > 0;
|
|
1275
|
-
case "update":
|
|
1276
|
-
return p.updates.length > 0;
|
|
1277
|
-
default:
|
|
1278
|
-
return true;
|
|
1279
|
-
}
|
|
1280
|
-
}),
|
|
1768
|
+
mergeMap(prepareVitePayloadForCrx),
|
|
1769
|
+
filter(shouldForwardCrxPayload),
|
|
1281
1770
|
map((data) => {
|
|
1282
1771
|
debug$3(`hmr payload`, data);
|
|
1283
1772
|
return {
|
|
@@ -1309,11 +1798,29 @@ const crxRuntimeReload = {
|
|
|
1309
1798
|
type: "custom",
|
|
1310
1799
|
event: "crx:runtime-reload"
|
|
1311
1800
|
};
|
|
1801
|
+
function getChangedFilePath(root, file) {
|
|
1802
|
+
if (!file)
|
|
1803
|
+
return null;
|
|
1804
|
+
const normalizedRoot = normalize(root);
|
|
1805
|
+
const normalizedFile = normalize(file);
|
|
1806
|
+
const relativeFile = relative(normalizedRoot, normalizedFile);
|
|
1807
|
+
if (!relativeFile || relativeFile.startsWith("..") || isAbsolute(relativeFile)) {
|
|
1808
|
+
return null;
|
|
1809
|
+
}
|
|
1810
|
+
return prefix$1("/", relativeFile);
|
|
1811
|
+
}
|
|
1812
|
+
function stripTimestamp(id) {
|
|
1813
|
+
return id.replace(/([?&])t=\d+&/, "$1").replace(/[?&]t=\d+$/, "").replace(/\?$/, "");
|
|
1814
|
+
}
|
|
1815
|
+
function escapeRegExp(text) {
|
|
1816
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1817
|
+
}
|
|
1312
1818
|
const pluginHMR = () => {
|
|
1313
1819
|
let inputManifestFiles;
|
|
1314
1820
|
let decoratedSend;
|
|
1315
1821
|
let config;
|
|
1316
1822
|
let subs;
|
|
1823
|
+
let liveReload = true;
|
|
1317
1824
|
return [
|
|
1318
1825
|
{
|
|
1319
1826
|
name: "crx:hmr",
|
|
@@ -1321,6 +1828,8 @@ const pluginHMR = () => {
|
|
|
1321
1828
|
enforce: "pre",
|
|
1322
1829
|
// server hmr host should be localhost
|
|
1323
1830
|
async config({ server = {}, ...config2 }) {
|
|
1831
|
+
const opts = await getOptions({ ...config2, server });
|
|
1832
|
+
liveReload = opts.liveReload !== false;
|
|
1324
1833
|
if (server.hmr === false)
|
|
1325
1834
|
return;
|
|
1326
1835
|
if (server.hmr === true)
|
|
@@ -1359,7 +1868,9 @@ const pluginHMR = () => {
|
|
|
1359
1868
|
subs.add(fileWriterError$.subscribe(send));
|
|
1360
1869
|
subs.add(
|
|
1361
1870
|
crxHMRPayload$.subscribe((payload) => {
|
|
1362
|
-
|
|
1871
|
+
if (liveReload) {
|
|
1872
|
+
send(payload);
|
|
1873
|
+
}
|
|
1363
1874
|
})
|
|
1364
1875
|
);
|
|
1365
1876
|
}
|
|
@@ -1368,14 +1879,16 @@ const pluginHMR = () => {
|
|
|
1368
1879
|
subs.unsubscribe();
|
|
1369
1880
|
},
|
|
1370
1881
|
// background changes require a full extension reload
|
|
1371
|
-
handleHotUpdate({ modules, server }) {
|
|
1882
|
+
handleHotUpdate({ file, modules, server }) {
|
|
1372
1883
|
const { root } = server.config;
|
|
1884
|
+
const changedFilePath = getChangedFilePath(root, file);
|
|
1373
1885
|
const relFiles = /* @__PURE__ */ new Set();
|
|
1374
1886
|
const fsFiles = /* @__PURE__ */ new Set();
|
|
1375
1887
|
const virtualModules = /* @__PURE__ */ new Set();
|
|
1376
1888
|
for (const m of modules) {
|
|
1377
|
-
|
|
1378
|
-
|
|
1889
|
+
const relFile = getChangedFilePath(root, m.id);
|
|
1890
|
+
if (relFile) {
|
|
1891
|
+
relFiles.add(relFile);
|
|
1379
1892
|
} else if (m.url?.startsWith("/@fs")) {
|
|
1380
1893
|
fsFiles.add(m.url);
|
|
1381
1894
|
} else if (m.id?.startsWith("\0") || m.url?.startsWith("/@id/__x00__")) {
|
|
@@ -1386,13 +1899,17 @@ const pluginHMR = () => {
|
|
|
1386
1899
|
}
|
|
1387
1900
|
}
|
|
1388
1901
|
}
|
|
1389
|
-
fsFiles.forEach((
|
|
1390
|
-
virtualModules.forEach((
|
|
1902
|
+
fsFiles.forEach((file2) => update(file2));
|
|
1903
|
+
virtualModules.forEach((file2) => update(file2));
|
|
1391
1904
|
if (inputManifestFiles.background.length) {
|
|
1392
1905
|
const background = prefix$1("/", inputManifestFiles.background[0]);
|
|
1393
1906
|
if (relFiles.has(background) || modules.some(isImporter(join(server.config.root, background)))) {
|
|
1394
|
-
|
|
1395
|
-
|
|
1907
|
+
if (liveReload) {
|
|
1908
|
+
debug$2("sending runtime reload");
|
|
1909
|
+
server.ws.send(crxRuntimeReload);
|
|
1910
|
+
} else {
|
|
1911
|
+
debug$2("skipping runtime reload (liveReload disabled)");
|
|
1912
|
+
}
|
|
1396
1913
|
}
|
|
1397
1914
|
}
|
|
1398
1915
|
for (const [key, script] of contentScripts)
|
|
@@ -1409,15 +1926,26 @@ const pluginHMR = () => {
|
|
|
1409
1926
|
if (changedCssFiles.length > 0) {
|
|
1410
1927
|
changedCssFiles.forEach((relFile) => update(relFile));
|
|
1411
1928
|
update(script.id);
|
|
1412
|
-
virtualModules.forEach((
|
|
1929
|
+
virtualModules.forEach((file2) => update(file2));
|
|
1413
1930
|
}
|
|
1414
1931
|
}
|
|
1415
1932
|
} else {
|
|
1416
1933
|
if (relFiles.has(script.id) || modules.some(isImporter(join(server.config.root, script.id)))) {
|
|
1417
1934
|
relFiles.forEach((relFile) => update(relFile));
|
|
1418
|
-
virtualModules.forEach((
|
|
1935
|
+
virtualModules.forEach((file2) => update(file2));
|
|
1419
1936
|
}
|
|
1420
1937
|
}
|
|
1938
|
+
const scriptPath = prefix$1("/", script.id);
|
|
1939
|
+
if (script.type === "iife" && changedFilePath === scriptPath) {
|
|
1940
|
+
debug$2("IIFE script changed, triggering rebuild: %s", script.id);
|
|
1941
|
+
const updatedFiles = update(scriptPath);
|
|
1942
|
+
Promise.all(updatedFiles.map((f) => f.file)).then(() => {
|
|
1943
|
+
if (liveReload) {
|
|
1944
|
+
debug$2("IIFE rebuild complete, sending runtime reload");
|
|
1945
|
+
server.ws.send(crxRuntimeReload);
|
|
1946
|
+
}
|
|
1947
|
+
});
|
|
1948
|
+
}
|
|
1421
1949
|
}
|
|
1422
1950
|
}
|
|
1423
1951
|
},
|
|
@@ -1432,13 +1960,14 @@ const pluginHMR = () => {
|
|
|
1432
1960
|
},
|
|
1433
1961
|
renderCrxDevScript(code, { id: _id, type }) {
|
|
1434
1962
|
if (type === "module" && _id !== "/@vite/client" && code.includes("createHotContext")) {
|
|
1435
|
-
const id = _id
|
|
1436
|
-
const escaped = id
|
|
1963
|
+
const id = stripTimestamp(_id);
|
|
1964
|
+
const escaped = escapeRegExp(id);
|
|
1437
1965
|
const regexp = new RegExp(
|
|
1438
|
-
`(
|
|
1966
|
+
`(createHotContext\\(")${escaped}("\\))`,
|
|
1967
|
+
"g"
|
|
1439
1968
|
);
|
|
1440
1969
|
const fileUrl = prefix$1("/", getFileName({ id, type }));
|
|
1441
|
-
const replaced = code.replace(regexp, fileUrl);
|
|
1970
|
+
const replaced = code.replace(regexp, `$1${fileUrl}$2`);
|
|
1442
1971
|
return replaced;
|
|
1443
1972
|
} else {
|
|
1444
1973
|
return code;
|
|
@@ -1614,14 +2143,50 @@ const pluginHtmlInlineScripts = () => {
|
|
|
1614
2143
|
};
|
|
1615
2144
|
};
|
|
1616
2145
|
|
|
1617
|
-
var loadingPageScript = "const VITE_URL = \"%PROTO%://localhost:%PORT%\";\ndocument.body.innerHTML = /* html */\n`\n<style>\n :root {\n color-scheme: light;\n --ink: #111827;\n --muted: #5f6b7a;\n --muted-subtle: rgba(95, 107, 122, 0.7);\n --muted-hint: rgba(95, 107, 122, 0.6);\n --card: #ffffff;\n --badge-bg: rgba(17, 24, 39, 0.04);\n --accent: #ff6b2c;\n --accent-2: #2563eb;\n --link-underline: rgba(37, 99, 235, 0.45);\n --glow-1: rgba(37, 99, 235, 0.12);\n --glow-2: rgba(255, 107, 44, 0.1);\n --button-grad-1: #ff7a43;\n --button-grad-2: #ff9a73;\n --button-shadow: rgba(255, 107, 44, 0.18);\n --button-shadow-hover: rgba(255, 107, 44, 0.22);\n --pulse: rgba(255, 107, 44, 0.6);\n --pulse-ring: rgba(255, 107, 44, 0.5);\n }\n\n @media (prefers-color-scheme: dark) {\n :root {\n color-scheme: dark;\n --ink: #e5e7eb;\n --muted: #a3aab5;\n --muted-subtle: rgba(163, 170, 181, 0.78);\n --muted-hint: rgba(163, 170, 181, 0.6);\n --card: #0f172a;\n --badge-bg: rgba(148, 163, 184, 0.16);\n --accent: #ff8a5a;\n --accent-2: #7aa2ff;\n --link-underline: rgba(122, 162, 255, 0.45);\n --glow-1: rgba(96, 165, 250, 0.14);\n --glow-2: rgba(251, 146, 60, 0.16);\n --button-grad-1: #f07b4d;\n --button-grad-2: #f39a76;\n --button-shadow: rgba(240, 123, 77, 0.16);\n --button-shadow-hover: rgba(240, 123, 77, 0.2);\n --pulse: rgba(255, 138, 90, 0.65);\n --pulse-ring: rgba(255, 138, 90, 0.5);\n }\n }\n\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n\n body {\n font-family: \"IBM Plex Sans\", \"Inter\", -apple-system, system-ui, sans-serif;\n color: var(--ink);\n background: transparent;\n width: 420px;\n height: 250px;\n margin: 0;\n padding: 0 !important;\n }\n\n #app {\n background: var(--card);\n position: relative;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n min-height: 100%;\n padding: 24px 24px 20px;\n gap: 14px;\n justify-content: center;\n }\n\n #app::before {\n content: \"\";\n position: absolute;\n inset: 0;\n background:\n radial-gradient(240px 140px at 100% 0%, var(--glow-1), transparent 70%),\n radial-gradient(220px 140px at 0% 100%, var(--glow-2), transparent 70%);\n pointer-events: none;\n }\n\n .header {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 6px;\n align-items: flex-start;\n padding-right: 96px;\n }\n\n .header-text {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .badge {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 10px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--muted);\n background: var(--badge-bg);\n border-radius: 999px;\n padding: 6px 10px;\n white-space: nowrap;\n position: absolute;\n top: 0;\n right: 0;\n }\n\n .pulse {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--accent);\n box-shadow: 0 0 0 0 var(--pulse);\n animation: pulse 1.6s ease-in-out infinite;\n }\n\n h1 {\n font-size: clamp(18px, 4vw, 22px);\n letter-spacing: -0.02em;\n line-height: 1.25;\n }\n\n p {\n margin: 0;\n color: var(--muted);\n font-size: 13px;\n line-height: 1.6;\n }\n\n .subtle {\n color: var(--muted-subtle);\n font-size: 12px;\n }\n\n a {\n color: var(--accent-2);\n text-decoration: none;\n border-bottom: 1px dashed var(--link-underline);\n }\n\n .content {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .footer {\n display: flex;\n flex-direction: column;\n gap: 10px;\n align-items: center;\n }\n\n .actions {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n justify-content: center;\n }\n\n button {\n appearance: none;\n border: none;\n border-radius: 999px;\n background: linear-gradient(135deg, var(--button-grad-1), var(--button-grad-2));\n color: white;\n padding: 8px 14px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n box-shadow: 0 8px 16px var(--button-shadow);\n transition: transform 160ms ease, box-shadow 160ms ease;\n }\n\n button:hover {\n transform: translateY(-1px);\n box-shadow: 0 10px 20px var(--button-shadow-hover);\n }\n\n button:focus-visible {\n outline: 2px solid var(--accent-2);\n outline-offset: 2px;\n }\n\n .hint {\n font-size: 11px;\n color: var(--muted-hint);\n text-align: center;\n }\n\n @keyframes pulse {\n 0% { box-shadow: 0 0 0 0 var(--pulse-ring); }\n 70% { box-shadow: 0 0 0 10px rgba(255, 107, 44, 0); }\n 100% { box-shadow: 0 0 0 0 rgba(255, 107, 44, 0); }\n }\n</style>\n\n<div id=\"app\">\n <div class=\"header\">\n <span class=\"badge\"><span class=\"pulse\"></span>dev server</span>\n <div class=\"header-text\">\n <h1>CRXJS DEV MODE</h1>\n <p class=\"subtle\">Connecting to the Vite dev server\\u2026</p>\n </div>\n </div>\n\n <div class=\"content\">\n <p>\n Cannot connect to <a href=\"${
|
|
2146
|
+
var loadingPageScript = "const VITE_URL = \"%PROTO%://localhost:%PORT%\";\nconst READY_PATH = \"%READY_PATH%\";\nconst VITE_PAGE_URL = new URL(location.pathname + location.search, VITE_URL).href;\nconst RELOAD_DELAY = 100;\nfunction getReadyUrl() {\n const url = new URL(READY_PATH, VITE_URL);\n url.searchParams.set(\"path\", location.pathname + location.search);\n url.searchParams.set(\"t\", Date.now().toString());\n return url.href;\n}\ndocument.body.innerHTML = /* html */\n`\n<style>\n :root {\n color-scheme: light;\n --ink: #111827;\n --muted: #5f6b7a;\n --muted-subtle: rgba(95, 107, 122, 0.7);\n --muted-hint: rgba(95, 107, 122, 0.6);\n --card: #ffffff;\n --badge-bg: rgba(17, 24, 39, 0.04);\n --accent: #ff6b2c;\n --accent-2: #2563eb;\n --link-underline: rgba(37, 99, 235, 0.45);\n --glow-1: rgba(37, 99, 235, 0.12);\n --glow-2: rgba(255, 107, 44, 0.1);\n --button-grad-1: #ff7a43;\n --button-grad-2: #ff9a73;\n --button-shadow: rgba(255, 107, 44, 0.18);\n --button-shadow-hover: rgba(255, 107, 44, 0.22);\n --pulse: rgba(255, 107, 44, 0.6);\n --pulse-ring: rgba(255, 107, 44, 0.5);\n }\n\n @media (prefers-color-scheme: dark) {\n :root {\n color-scheme: dark;\n --ink: #e5e7eb;\n --muted: #a3aab5;\n --muted-subtle: rgba(163, 170, 181, 0.78);\n --muted-hint: rgba(163, 170, 181, 0.6);\n --card: #0f172a;\n --badge-bg: rgba(148, 163, 184, 0.16);\n --accent: #ff8a5a;\n --accent-2: #7aa2ff;\n --link-underline: rgba(122, 162, 255, 0.45);\n --glow-1: rgba(96, 165, 250, 0.14);\n --glow-2: rgba(251, 146, 60, 0.16);\n --button-grad-1: #f07b4d;\n --button-grad-2: #f39a76;\n --button-shadow: rgba(240, 123, 77, 0.16);\n --button-shadow-hover: rgba(240, 123, 77, 0.2);\n --pulse: rgba(255, 138, 90, 0.65);\n --pulse-ring: rgba(255, 138, 90, 0.5);\n }\n }\n\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n\n body {\n font-family: \"IBM Plex Sans\", \"Inter\", -apple-system, system-ui, sans-serif;\n color: var(--ink);\n background: transparent;\n width: 420px;\n height: 250px;\n margin: 0;\n padding: 0 !important;\n }\n\n #app {\n background: var(--card);\n position: relative;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n min-height: 100%;\n padding: 24px 24px 20px;\n gap: 14px;\n justify-content: center;\n }\n\n #app::before {\n content: \"\";\n position: absolute;\n inset: 0;\n background:\n radial-gradient(240px 140px at 100% 0%, var(--glow-1), transparent 70%),\n radial-gradient(220px 140px at 0% 100%, var(--glow-2), transparent 70%);\n pointer-events: none;\n }\n\n .header {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 6px;\n align-items: flex-start;\n padding-right: 96px;\n }\n\n .header-text {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .badge {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 10px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--muted);\n background: var(--badge-bg);\n border-radius: 999px;\n padding: 6px 10px;\n white-space: nowrap;\n position: absolute;\n top: 0;\n right: 0;\n }\n\n .pulse {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--accent);\n box-shadow: 0 0 0 0 var(--pulse);\n animation: pulse 1.6s ease-in-out infinite;\n }\n\n h1 {\n font-size: clamp(18px, 4vw, 22px);\n letter-spacing: -0.02em;\n line-height: 1.25;\n }\n\n p {\n margin: 0;\n color: var(--muted);\n font-size: 13px;\n line-height: 1.6;\n }\n\n .subtle {\n color: var(--muted-subtle);\n font-size: 12px;\n }\n\n a {\n color: var(--accent-2);\n text-decoration: none;\n border-bottom: 1px dashed var(--link-underline);\n }\n\n .content {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .footer {\n display: flex;\n flex-direction: column;\n gap: 10px;\n align-items: center;\n }\n\n .actions {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n justify-content: center;\n }\n\n button {\n appearance: none;\n border: none;\n border-radius: 999px;\n background: linear-gradient(135deg, var(--button-grad-1), var(--button-grad-2));\n color: white;\n padding: 8px 14px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n box-shadow: 0 8px 16px var(--button-shadow);\n transition: transform 160ms ease, box-shadow 160ms ease;\n }\n\n button:hover {\n transform: translateY(-1px);\n box-shadow: 0 10px 20px var(--button-shadow-hover);\n }\n\n button:focus-visible {\n outline: 2px solid var(--accent-2);\n outline-offset: 2px;\n }\n\n .hint {\n font-size: 11px;\n color: var(--muted-hint);\n text-align: center;\n }\n\n @keyframes pulse {\n 0% { box-shadow: 0 0 0 0 var(--pulse-ring); }\n 70% { box-shadow: 0 0 0 10px rgba(255, 107, 44, 0); }\n 100% { box-shadow: 0 0 0 0 rgba(255, 107, 44, 0); }\n }\n</style>\n\n<div id=\"app\">\n <div class=\"header\">\n <span class=\"badge\"><span class=\"pulse\"></span>dev server</span>\n <div class=\"header-text\">\n <h1>CRXJS DEV MODE</h1>\n <p class=\"subtle\">Connecting to the Vite dev server\\u2026</p>\n </div>\n </div>\n\n <div class=\"content\">\n <p>\n Cannot connect to <a href=\"${VITE_PAGE_URL}\">${VITE_PAGE_URL}</a>.\n Make sure Vite is running, then reload the extension.\n </p>\n <p>This page will close automatically after the extension reloads.</p>\n </div>\n\n <div class=\"footer\">\n <div class=\"actions\">\n <button>Reload Extension</button>\n </div>\n <div class=\"hint\">Tip: if the URL is wrong, restart Vite so it picks the right port.</div>\n </div>\n</div>\n`;\ndocument.body.querySelector(\"button\")?.addEventListener(\"click\", () => {\n chrome.runtime.reload();\n});\nlet tries = 0;\nlet ready = false;\ndo {\n try {\n const response = await fetch(getReadyUrl());\n if (!response.ok)\n throw new Error(`HTTP ${response.status}`);\n ready = true;\n } catch {\n const timeout = Math.min(100 * Math.pow(2, ++tries), 5e3);\n console.log(`[CRXJS] Vite Dev Server is not available on ${VITE_PAGE_URL}`);\n console.log(`[CRXJS] Retrying in ${timeout}ms...`);\n await new Promise((resolve) => setTimeout(resolve, timeout));\n }\n} while (!ready);\nconsole.log(`[CRXJS] Vite Dev Server is ready on ${VITE_PAGE_URL}`);\nconsole.log(`[CRXJS] Reloading in ${RELOAD_DELAY}ms...`);\nawait new Promise((resolve) => setTimeout(resolve, RELOAD_DELAY));\nlocation.reload();\n";
|
|
1618
2147
|
|
|
1619
2148
|
var loadingPageHtml = "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>CRXJS DEV MODE</title>\n <script src=\"%SCRIPT%\" type=\"module\"></script>\n </head>\n <body>\n <p>An unknown error occurred. Failed to load the script.</p>\n </body>\n</html>\n";
|
|
1620
2149
|
|
|
1621
2150
|
const { readFile } = promises;
|
|
2151
|
+
const loadingPageReadyPath = "/@crx/dev-ready";
|
|
2152
|
+
function normalizeHtmlPath(pathname) {
|
|
2153
|
+
let decoded;
|
|
2154
|
+
try {
|
|
2155
|
+
decoded = decodeURIComponent(pathname);
|
|
2156
|
+
} catch {
|
|
2157
|
+
return null;
|
|
2158
|
+
}
|
|
2159
|
+
const normalized = normalize(decoded.replace(/^\/+/, "") || "index.html");
|
|
2160
|
+
if (normalized.startsWith("..") || isAbsolute(normalized))
|
|
2161
|
+
return null;
|
|
2162
|
+
return normalized;
|
|
2163
|
+
}
|
|
2164
|
+
function getLoadingPageReadyHtmlPath(requestUrl) {
|
|
2165
|
+
if (!requestUrl)
|
|
2166
|
+
return null;
|
|
2167
|
+
let url;
|
|
2168
|
+
try {
|
|
2169
|
+
url = new URL(requestUrl, "http://crxjs.local");
|
|
2170
|
+
} catch {
|
|
2171
|
+
return null;
|
|
2172
|
+
}
|
|
2173
|
+
if (url.pathname !== loadingPageReadyPath)
|
|
2174
|
+
return void 0;
|
|
2175
|
+
const path = url.searchParams.get("path");
|
|
2176
|
+
if (!path)
|
|
2177
|
+
return null;
|
|
2178
|
+
let pageUrl;
|
|
2179
|
+
try {
|
|
2180
|
+
pageUrl = new URL(path, "http://crxjs.local");
|
|
2181
|
+
} catch {
|
|
2182
|
+
return null;
|
|
2183
|
+
}
|
|
2184
|
+
return normalizeHtmlPath(pageUrl.pathname);
|
|
2185
|
+
}
|
|
1622
2186
|
const pluginManifest = () => {
|
|
1623
2187
|
let manifest;
|
|
1624
2188
|
let plugins = [];
|
|
2189
|
+
let devHtmlFiles = /* @__PURE__ */ new Set();
|
|
1625
2190
|
let refId;
|
|
1626
2191
|
let config;
|
|
1627
2192
|
return [
|
|
@@ -1641,6 +2206,7 @@ const pluginManifest = () => {
|
|
|
1641
2206
|
background: sw,
|
|
1642
2207
|
html
|
|
1643
2208
|
} = await manifestFiles(manifest, { cwd: config2.root });
|
|
2209
|
+
devHtmlFiles = new Set(html.map(normalizeHtmlPath).filter(isString));
|
|
1644
2210
|
const { entries = [] } = config2.optimizeDeps ?? {};
|
|
1645
2211
|
let { input = [] } = config2.build?.rollupOptions ?? {};
|
|
1646
2212
|
if (typeof input === "string")
|
|
@@ -1674,6 +2240,25 @@ const pluginManifest = () => {
|
|
|
1674
2240
|
plugins = resolvedConfig.plugins;
|
|
1675
2241
|
}
|
|
1676
2242
|
},
|
|
2243
|
+
configureServer(server) {
|
|
2244
|
+
server.middlewares.use((req, res, next) => {
|
|
2245
|
+
const htmlPath = getLoadingPageReadyHtmlPath(req.url);
|
|
2246
|
+
if (typeof htmlPath === "undefined") {
|
|
2247
|
+
next();
|
|
2248
|
+
return;
|
|
2249
|
+
}
|
|
2250
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
2251
|
+
res.setHeader("Access-Control-Allow-Methods", "GET");
|
|
2252
|
+
if (!htmlPath) {
|
|
2253
|
+
res.statusCode = 400;
|
|
2254
|
+
res.end();
|
|
2255
|
+
return;
|
|
2256
|
+
}
|
|
2257
|
+
const exists = devHtmlFiles.has(htmlPath) && (existsSync(join(server.config.root, htmlPath)) || existsSync(join(server.config.publicDir, htmlPath)));
|
|
2258
|
+
res.statusCode = exists ? 204 : 404;
|
|
2259
|
+
res.end();
|
|
2260
|
+
});
|
|
2261
|
+
},
|
|
1677
2262
|
buildStart(options) {
|
|
1678
2263
|
if (options.plugins)
|
|
1679
2264
|
plugins = options.plugins;
|
|
@@ -1810,14 +2395,27 @@ const pluginManifest = () => {
|
|
|
1810
2395
|
}
|
|
1811
2396
|
}
|
|
1812
2397
|
} else {
|
|
2398
|
+
const opts = await getOptions({ plugins: config.plugins });
|
|
2399
|
+
const standaloneFiles = (opts.contentScripts?.standaloneFiles || []).map(
|
|
2400
|
+
(f) => f.replace(/^\//, "")
|
|
2401
|
+
);
|
|
2402
|
+
const isStandaloneFile = (file) => {
|
|
2403
|
+
const normalized = file.replace(/^\//, "");
|
|
2404
|
+
return standaloneFiles.includes(normalized);
|
|
2405
|
+
};
|
|
1813
2406
|
if (manifest2.content_scripts)
|
|
1814
2407
|
for (const { js = [], matches = [] } of manifest2.content_scripts)
|
|
1815
2408
|
for (const file of js) {
|
|
2409
|
+
if (isIifeContentScript(file) || isStandaloneFile(file))
|
|
2410
|
+
continue;
|
|
1816
2411
|
const id2 = join(config.root, file);
|
|
1817
2412
|
const refId2 = this.emitFile({
|
|
1818
2413
|
type: "chunk",
|
|
1819
2414
|
id: id2,
|
|
1820
|
-
name: basename(file)
|
|
2415
|
+
name: basename(file),
|
|
2416
|
+
// Preserve content script entry exports so the build finalizer
|
|
2417
|
+
// can decide whether the script needs a loader wrapper.
|
|
2418
|
+
preserveSignature: "exports-only"
|
|
1821
2419
|
});
|
|
1822
2420
|
contentScripts.set(
|
|
1823
2421
|
file,
|
|
@@ -1887,6 +2485,7 @@ const pluginManifest = () => {
|
|
|
1887
2485
|
}
|
|
1888
2486
|
}
|
|
1889
2487
|
} else {
|
|
2488
|
+
finalizeBuildContentScripts(this, bundle);
|
|
1890
2489
|
if (manifest2.background && "service_worker" in manifest2.background) {
|
|
1891
2490
|
const ref = manifest2.background.service_worker;
|
|
1892
2491
|
const name = this.getFileName(ref);
|
|
@@ -1901,7 +2500,7 @@ const pluginManifest = () => {
|
|
|
1901
2500
|
({ js = [], ...rest }) => {
|
|
1902
2501
|
return {
|
|
1903
2502
|
js: js.map((id) => {
|
|
1904
|
-
const script = contentScripts.get(id);
|
|
2503
|
+
const script = contentScripts.get(id) ?? contentScripts.get(prefix$1("/", id));
|
|
1905
2504
|
const fileName = script?.loaderName ?? script?.fileName;
|
|
1906
2505
|
if (typeof fileName === "undefined")
|
|
1907
2506
|
throw new Error(
|
|
@@ -1941,6 +2540,11 @@ const pluginManifest = () => {
|
|
|
1941
2540
|
"webAccessibleResources"
|
|
1942
2541
|
];
|
|
1943
2542
|
const files = await manifestFiles(manifest2, { cwd: config.root });
|
|
2543
|
+
if (config.command === "serve") {
|
|
2544
|
+
devHtmlFiles = new Set(
|
|
2545
|
+
files.html.map(normalizeHtmlPath).filter(isString)
|
|
2546
|
+
);
|
|
2547
|
+
}
|
|
1944
2548
|
await Promise.all(
|
|
1945
2549
|
assetTypes.map((k) => files[k]).flat().map(async (f) => {
|
|
1946
2550
|
if (typeof bundle[f] === "undefined") {
|
|
@@ -1972,7 +2576,7 @@ Public dir: "${config.publicDir}"`
|
|
|
1972
2576
|
const refId2 = this.emitFile({
|
|
1973
2577
|
type: "asset",
|
|
1974
2578
|
name: "loading-page.js",
|
|
1975
|
-
source: loadingPageScript.replace("%PROTO%", config.server.https ? "https" : "http").replace("%PORT%", `${config.server.port ?? 0}`)
|
|
2579
|
+
source: loadingPageScript.replace("%PROTO%", config.server.https ? "https" : "http").replace("%PORT%", `${config.server.port ?? 0}`).replace("%READY_PATH%", loadingPageReadyPath)
|
|
1976
2580
|
});
|
|
1977
2581
|
const loadingPageScriptName = this.getFileName(refId2);
|
|
1978
2582
|
files.html.map(
|
|
@@ -2266,6 +2870,9 @@ const crx = (options) => {
|
|
|
2266
2870
|
contentScripts.clear();
|
|
2267
2871
|
return [
|
|
2268
2872
|
pluginOptionsProvider(options),
|
|
2873
|
+
pluginExtensionCors(),
|
|
2874
|
+
pluginContentScriptsIife(),
|
|
2875
|
+
// Must come early so isIifeModeEnabled is set before manifest plugin
|
|
2269
2876
|
pluginBackground(),
|
|
2270
2877
|
pluginContentScripts(),
|
|
2271
2878
|
pluginDeclaredContentScripts(),
|