@crxjs/vite-plugin 1.0.1 → 1.0.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @crxjs/vite-plugin
2
2
 
3
+ ## 1.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - b83a4bd: Check for manifest assets first in the project root, then check in
8
+ the public dir. Throw an informative error if the file does not exist in
9
+ either dir.
10
+
11
+ ## 1.0.3
12
+
13
+ ### Patch Changes
14
+
15
+ - 8b2e587: check service worker on interval from extension page
16
+
17
+ ## 1.0.2
18
+
19
+ ### Patch Changes
20
+
21
+ - be8a1de: Remove unused code that throws when web accessible resources contains
22
+ an HTML file.
23
+
3
24
  ## 1.0.1
4
25
 
5
26
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -17,6 +17,7 @@ var vite = require('vite');
17
17
  var module$1 = require('module');
18
18
  var cheerio = require('cheerio');
19
19
  var jsesc = require('jsesc');
20
+ var fs = require('fs');
20
21
  var injector = require('connect-injector');
21
22
 
22
23
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
@@ -2093,7 +2094,7 @@ const pluginFileWriterEvents = () => {
2093
2094
  };
2094
2095
  };
2095
2096
 
2096
- var preControllerScript = "setTimeout(() => location.reload(), 100);\n";
2097
+ var preControllerScript = "const id = setInterval(() => location.reload(), 100);\nsetTimeout(() => clearInterval(id), 5e3);\n";
2097
2098
 
2098
2099
  var preControllerHtml = "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>Waiting for the extension service worker...</title>\n <script src=\"%PATH%\"></script>\n </head>\n <body>\n <h1>Waiting for service worker</h1>\n\n <p>\n If you see this message, it means the service worker has not loaded fully.\n </p>\n\n <p>\n During development, the service worker reroutes HTML requests to the dev\n server, so this file isn't used unless the extension service worker opens\n a page immediately after a full extension reload, and before the service\n worker takes control of fetch (e.g., in the onInstalled event). In that\n case, this page will continuously reload until the service worker is\n ready, always less than 100 ms.\n </p>\n\n <p>This page is never added in production.</p>\n </body>\n</html>\n";
2099
2100
 
@@ -2545,6 +2546,223 @@ const pluginHtmlAuditor = () => {
2545
2546
  };
2546
2547
  };
2547
2548
 
2549
+ const pluginManifest = (_manifest) => () => {
2550
+ let manifest;
2551
+ let plugins;
2552
+ let refId;
2553
+ let config;
2554
+ return [
2555
+ {
2556
+ name: "crx:manifest-init",
2557
+ enforce: "pre",
2558
+ async config(config2, env) {
2559
+ manifest = await (typeof _manifest === "function" ? _manifest(env) : _manifest);
2560
+ if (manifest.manifest_version !== 3)
2561
+ throw new Error(`Manifest v${manifest.manifest_version} is currently unsupported, please use manifest v3`);
2562
+ if (env.command === "serve") {
2563
+ const {
2564
+ contentScripts: js,
2565
+ background: sw,
2566
+ html
2567
+ } = await manifestFiles(manifest);
2568
+ let { entries = [] } = config2.optimizeDeps ?? {};
2569
+ entries = [entries].flat();
2570
+ const set = new Set(entries);
2571
+ for (const x of [...js, ...sw, ...html])
2572
+ set.add(x);
2573
+ return {
2574
+ ...config2,
2575
+ optimizeDeps: {
2576
+ ...config2.optimizeDeps,
2577
+ entries: [...set]
2578
+ }
2579
+ };
2580
+ }
2581
+ },
2582
+ buildStart(options) {
2583
+ if (options.plugins)
2584
+ plugins = options.plugins;
2585
+ }
2586
+ },
2587
+ {
2588
+ name: "crx:manifest-loader",
2589
+ apply: "build",
2590
+ enforce: "pre",
2591
+ buildStart() {
2592
+ refId = this.emitFile({
2593
+ type: "chunk",
2594
+ id: manifestId,
2595
+ name: "crx-manifest.js",
2596
+ preserveSignature: "strict"
2597
+ });
2598
+ },
2599
+ resolveId(source) {
2600
+ if (source === manifestId)
2601
+ return manifestId;
2602
+ return null;
2603
+ },
2604
+ load(id) {
2605
+ if (id === manifestId)
2606
+ return encodeManifest(manifest);
2607
+ return null;
2608
+ }
2609
+ },
2610
+ {
2611
+ name: "crx:stub-input",
2612
+ apply: "build",
2613
+ enforce: "pre",
2614
+ options({ input, ...options }) {
2615
+ return {
2616
+ input: isString(input) && input.endsWith("index.html") ? stubId : input,
2617
+ ...options
2618
+ };
2619
+ },
2620
+ resolveId(source) {
2621
+ if (source === stubId)
2622
+ return stubId;
2623
+ return null;
2624
+ },
2625
+ load(id) {
2626
+ if (id === stubId)
2627
+ return `console.log('stub')`;
2628
+ return null;
2629
+ },
2630
+ generateBundle(options, bundle) {
2631
+ for (const [key, chunk] of Object.entries(bundle)) {
2632
+ if (chunk.type === "chunk" && chunk.facadeModuleId === stubId) {
2633
+ delete bundle[key];
2634
+ break;
2635
+ }
2636
+ }
2637
+ }
2638
+ },
2639
+ {
2640
+ name: "crx:manifest-post",
2641
+ apply: "build",
2642
+ enforce: "post",
2643
+ configResolved(_config) {
2644
+ config = _config;
2645
+ const plugins2 = config.plugins;
2646
+ const crx = plugins2.findIndex(({ name }) => name === "crx:manifest-post");
2647
+ const [plugin] = plugins2.splice(crx, 1);
2648
+ plugins2.push(plugin);
2649
+ },
2650
+ async transform(code, id) {
2651
+ if (id !== manifestId)
2652
+ return;
2653
+ let manifest2 = decodeManifest.call(this, code);
2654
+ for (const plugin of plugins) {
2655
+ try {
2656
+ const m = structuredClone(manifest2);
2657
+ const result = await plugin.transformCrxManifest?.call(this, m);
2658
+ manifest2 = result ?? manifest2;
2659
+ } catch (error) {
2660
+ if (error instanceof Error)
2661
+ error.message = `[${plugin.name}] ${error.message}`;
2662
+ throw error;
2663
+ }
2664
+ }
2665
+ if (manifest2.content_scripts?.length) {
2666
+ manifest2.content_scripts = manifest2.content_scripts.map(({ js = [], ...rest }) => {
2667
+ const refJS = js.map((file) => this.emitFile({
2668
+ type: "chunk",
2669
+ id: file,
2670
+ name: basename(file)
2671
+ }));
2672
+ return { js: refJS, ...rest };
2673
+ });
2674
+ }
2675
+ if (!this.meta.watchMode) {
2676
+ if (manifest2.background?.service_worker) {
2677
+ const file = manifest2.background.service_worker;
2678
+ const refId2 = this.emitFile({
2679
+ type: "chunk",
2680
+ id: file,
2681
+ name: basename(file)
2682
+ });
2683
+ manifest2.background.service_worker = refId2;
2684
+ }
2685
+ for (const file of htmlFiles(manifest2)) {
2686
+ this.emitFile({
2687
+ type: "chunk",
2688
+ id: file,
2689
+ name: basename(file)
2690
+ });
2691
+ }
2692
+ }
2693
+ const encoded = encodeManifest(manifest2);
2694
+ return encoded;
2695
+ },
2696
+ async generateBundle(options, bundle) {
2697
+ const manifestName = this.getFileName(refId);
2698
+ const manifestJs = bundle[manifestName];
2699
+ let manifest2 = decodeManifest.call(this, manifestJs.code);
2700
+ if (manifest2.background?.service_worker && !this.meta.watchMode) {
2701
+ const ref = manifest2.background.service_worker;
2702
+ const name = this.getFileName(ref);
2703
+ manifest2.background.service_worker = name;
2704
+ }
2705
+ manifest2.content_scripts = manifest2.content_scripts?.map(({ js = [], ...rest }) => {
2706
+ const refJS = js.map((ref) => this.getFileName(ref));
2707
+ return { js: refJS, ...rest };
2708
+ });
2709
+ for (const plugin of plugins) {
2710
+ try {
2711
+ const m = structuredClone(manifest2);
2712
+ const result = await plugin.renderCrxManifest?.call(this, m, bundle);
2713
+ manifest2 = result ?? manifest2;
2714
+ } catch (error) {
2715
+ const name = `[${plugin.name}]`;
2716
+ let message = error;
2717
+ if (error instanceof Error) {
2718
+ message = colors__default["default"].red(`${name} ${error.stack ? error.stack : error.message}`);
2719
+ } else if (typeof error === "string") {
2720
+ message = colors__default["default"].red(`${name} ${error}`);
2721
+ }
2722
+ console.log(message);
2723
+ throw new Error(`Error in ${plugin.name}.renderCrxManifest`);
2724
+ }
2725
+ }
2726
+ const assetTypes = [
2727
+ "icons",
2728
+ "locales",
2729
+ "rulesets",
2730
+ "webAccessibleResources"
2731
+ ];
2732
+ const files = await manifestFiles(manifest2);
2733
+ await Promise.all(assetTypes.map((k) => files[k]).flat().map(async (f) => {
2734
+ if (typeof bundle[f] === "undefined") {
2735
+ let filename = join(config.root, f);
2736
+ if (!fs.existsSync(filename))
2737
+ filename = join(config.publicDir, f);
2738
+ if (!fs.existsSync(filename))
2739
+ throw new Error(`ENOENT: Could not load manifest asset "${f}".
2740
+ Manifest assets must exist in one of these directories:
2741
+ Project root: "${config.root}"
2742
+ Public dir: "${config.publicDir}"`);
2743
+ this.emitFile({
2744
+ type: "asset",
2745
+ fileName: f,
2746
+ source: await fsExtra.readFile(filename)
2747
+ });
2748
+ }
2749
+ }));
2750
+ const manifestJson = bundle["manifest.json"];
2751
+ if (typeof manifestJson === "undefined") {
2752
+ this.emitFile({
2753
+ type: "asset",
2754
+ fileName: "manifest.json",
2755
+ source: JSON.stringify(manifest2, null, 2)
2756
+ });
2757
+ } else {
2758
+ manifestJson.source = JSON.stringify(manifest2, null, 2);
2759
+ }
2760
+ delete bundle[manifestName];
2761
+ }
2762
+ }
2763
+ ];
2764
+ };
2765
+
2548
2766
  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 console.log(\"[crx] runtime reload\");\n setTimeout(() => location.reload(), 500);\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";
2549
2767
 
2550
2768
  var contentDevLoader = "(function () {\n 'use strict';\n\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 await import(\n /* @vite-ignore */\n chrome.runtime.getURL(__SCRIPT__)\n );\n })().catch(console.error);\n\n})();\n";
@@ -2983,220 +3201,6 @@ const pluginResources = ({ contentScripts = {} }) => {
2983
3201
  ];
2984
3202
  };
2985
3203
 
2986
- const pluginManifest = (_manifest) => () => {
2987
- let manifest;
2988
- let plugins;
2989
- let refId;
2990
- let config;
2991
- return [
2992
- {
2993
- name: "crx:manifest-init",
2994
- enforce: "pre",
2995
- async config(config2, env) {
2996
- manifest = await (typeof _manifest === "function" ? _manifest(env) : _manifest);
2997
- if (manifest.manifest_version !== 3)
2998
- throw new Error(`Manifest v${manifest.manifest_version} is currently unsupported, please use manifest v3`);
2999
- if (env.command === "serve") {
3000
- const {
3001
- contentScripts: js,
3002
- background: sw,
3003
- html
3004
- } = await manifestFiles(manifest);
3005
- let { entries = [] } = config2.optimizeDeps ?? {};
3006
- entries = [entries].flat();
3007
- const set = new Set(entries);
3008
- for (const x of [...js, ...sw, ...html])
3009
- set.add(x);
3010
- return {
3011
- ...config2,
3012
- optimizeDeps: {
3013
- ...config2.optimizeDeps,
3014
- entries: [...set]
3015
- }
3016
- };
3017
- }
3018
- },
3019
- buildStart(options) {
3020
- if (options.plugins)
3021
- plugins = options.plugins;
3022
- }
3023
- },
3024
- {
3025
- name: "crx:manifest-loader",
3026
- apply: "build",
3027
- enforce: "pre",
3028
- buildStart() {
3029
- refId = this.emitFile({
3030
- type: "chunk",
3031
- id: manifestId,
3032
- name: "crx-manifest.js",
3033
- preserveSignature: "strict"
3034
- });
3035
- },
3036
- resolveId(source) {
3037
- if (source === manifestId)
3038
- return manifestId;
3039
- return null;
3040
- },
3041
- load(id) {
3042
- if (id === manifestId)
3043
- return encodeManifest(manifest);
3044
- return null;
3045
- }
3046
- },
3047
- {
3048
- name: "crx:stub-input",
3049
- apply: "build",
3050
- enforce: "pre",
3051
- options({ input, ...options }) {
3052
- return {
3053
- input: isString(input) && input.endsWith("index.html") ? stubId : input,
3054
- ...options
3055
- };
3056
- },
3057
- resolveId(source) {
3058
- if (source === stubId)
3059
- return stubId;
3060
- return null;
3061
- },
3062
- load(id) {
3063
- if (id === stubId)
3064
- return `console.log('stub')`;
3065
- return null;
3066
- },
3067
- generateBundle(options, bundle) {
3068
- for (const [key, chunk] of Object.entries(bundle)) {
3069
- if (chunk.type === "chunk" && chunk.facadeModuleId === stubId) {
3070
- delete bundle[key];
3071
- break;
3072
- }
3073
- }
3074
- }
3075
- },
3076
- {
3077
- name: "crx:manifest-post",
3078
- apply: "build",
3079
- enforce: "post",
3080
- configResolved(_config) {
3081
- config = _config;
3082
- const plugins2 = config.plugins;
3083
- const crx = plugins2.findIndex(({ name }) => name === "crx:manifest-post");
3084
- const [plugin] = plugins2.splice(crx, 1);
3085
- plugins2.push(plugin);
3086
- },
3087
- async transform(code, id) {
3088
- if (id !== manifestId)
3089
- return;
3090
- let manifest2 = decodeManifest.call(this, code);
3091
- for (const plugin of plugins) {
3092
- try {
3093
- const m = structuredClone(manifest2);
3094
- const result = await plugin.transformCrxManifest?.call(this, m);
3095
- manifest2 = result ?? manifest2;
3096
- } catch (error) {
3097
- if (error instanceof Error)
3098
- error.message = `[${plugin.name}] ${error.message}`;
3099
- throw error;
3100
- }
3101
- }
3102
- if (manifest2.content_scripts?.length) {
3103
- manifest2.content_scripts = manifest2.content_scripts.map(({ js = [], ...rest }) => {
3104
- const refJS = js.map((file) => this.emitFile({
3105
- type: "chunk",
3106
- id: file,
3107
- name: basename(file)
3108
- }));
3109
- return { js: refJS, ...rest };
3110
- });
3111
- }
3112
- if (!this.meta.watchMode) {
3113
- if (manifest2.background?.service_worker) {
3114
- const file = manifest2.background.service_worker;
3115
- const refId2 = this.emitFile({
3116
- type: "chunk",
3117
- id: file,
3118
- name: basename(file)
3119
- });
3120
- manifest2.background.service_worker = refId2;
3121
- }
3122
- for (const file of htmlFiles(manifest2)) {
3123
- this.emitFile({
3124
- type: "chunk",
3125
- id: file,
3126
- name: basename(file)
3127
- });
3128
- }
3129
- }
3130
- const encoded = encodeManifest(manifest2);
3131
- return encoded;
3132
- },
3133
- async generateBundle(options, bundle) {
3134
- const manifestName = this.getFileName(refId);
3135
- const manifestJs = bundle[manifestName];
3136
- let manifest2 = decodeManifest.call(this, manifestJs.code);
3137
- if (manifest2.background?.service_worker && !this.meta.watchMode) {
3138
- const ref = manifest2.background.service_worker;
3139
- const name = this.getFileName(ref);
3140
- manifest2.background.service_worker = name;
3141
- }
3142
- manifest2.content_scripts = manifest2.content_scripts?.map(({ js = [], ...rest }) => {
3143
- const refJS = js.map((ref) => this.getFileName(ref));
3144
- return { js: refJS, ...rest };
3145
- });
3146
- manifest2.web_accessible_resources = manifest2.web_accessible_resources?.map(({ resources, ...rest }) => ({
3147
- resources: resources.map((r) => fg__default["default"].isDynamicPattern(r) || r === dynamicResourcesName ? r : this.getFileName(r)),
3148
- ...rest
3149
- }));
3150
- for (const plugin of plugins) {
3151
- try {
3152
- const m = structuredClone(manifest2);
3153
- const result = await plugin.renderCrxManifest?.call(this, m, bundle);
3154
- manifest2 = result ?? manifest2;
3155
- } catch (error) {
3156
- const name = `[${plugin.name}]`;
3157
- let message = error;
3158
- if (error instanceof Error) {
3159
- message = colors__default["default"].red(`${name} ${error.stack ? error.stack : error.message}`);
3160
- } else if (typeof error === "string") {
3161
- message = colors__default["default"].red(`${name} ${error}`);
3162
- }
3163
- console.log(message);
3164
- throw new Error(`Error in ${plugin.name}.renderCrxManifest`);
3165
- }
3166
- }
3167
- const assetTypes = [
3168
- "icons",
3169
- "locales",
3170
- "rulesets",
3171
- "webAccessibleResources"
3172
- ];
3173
- const files = await manifestFiles(manifest2);
3174
- await Promise.all(assetTypes.map((k) => files[k]).flat().map(async (f) => {
3175
- if (typeof bundle[f] === "undefined") {
3176
- const filename = join(config.root, f);
3177
- this.emitFile({
3178
- type: "asset",
3179
- fileName: f,
3180
- source: await fsExtra.readFile(filename)
3181
- });
3182
- }
3183
- }));
3184
- const manifestJson = bundle["manifest.json"];
3185
- if (typeof manifestJson === "undefined") {
3186
- this.emitFile({
3187
- type: "asset",
3188
- fileName: "manifest.json",
3189
- source: JSON.stringify(manifest2, null, 2)
3190
- });
3191
- } else {
3192
- manifestJson.source = JSON.stringify(manifest2, null, 2);
3193
- }
3194
- delete bundle[manifestName];
3195
- }
3196
- }
3197
- ];
3198
- };
3199
-
3200
3204
  var workerHmrClient = "const ownOrigin = new URL(chrome.runtime.getURL(\"/\")).origin;\nself.addEventListener(\"fetch\", (fetchEvent) => {\n const url = new URL(fetchEvent.request.url);\n if (url.origin === ownOrigin) {\n fetchEvent.respondWith(sendToServer(url));\n }\n});\nasync function sendToServer(url) {\n url.protocol = \"http:\";\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 return new Response(response.body, {\n headers: {\n \"Content-Type\": response.headers.get(\"Content-Type\") ?? \"text/javascript\"\n }\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) => ports.delete(port2));\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 socketHost = `${__HMR_HOSTNAME__ || location.hostname}:${__HMR_PORT__}`;\nconst socket = new WebSocket(`${socketProtocol}://${socketHost}`, \"vite-hmr\");\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 chrome.runtime.reload();\n});\n";
3201
3205
 
3202
3206
  function defineClientValues(code, config) {
package/dist/index.mjs CHANGED
@@ -13,6 +13,7 @@ import { createLogger } from 'vite';
13
13
  import { createRequire } from 'module';
14
14
  import { load } from 'cheerio';
15
15
  import jsesc from 'jsesc';
16
+ import { existsSync } from 'fs';
16
17
  import injector from 'connect-injector';
17
18
 
18
19
  const _debug = (id) => debug$5("crx").extend(id);
@@ -2060,7 +2061,7 @@ const pluginFileWriterEvents = () => {
2060
2061
  };
2061
2062
  };
2062
2063
 
2063
- var preControllerScript = "setTimeout(() => location.reload(), 100);\n";
2064
+ var preControllerScript = "const id = setInterval(() => location.reload(), 100);\nsetTimeout(() => clearInterval(id), 5e3);\n";
2064
2065
 
2065
2066
  var preControllerHtml = "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>Waiting for the extension service worker...</title>\n <script src=\"%PATH%\"></script>\n </head>\n <body>\n <h1>Waiting for service worker</h1>\n\n <p>\n If you see this message, it means the service worker has not loaded fully.\n </p>\n\n <p>\n During development, the service worker reroutes HTML requests to the dev\n server, so this file isn't used unless the extension service worker opens\n a page immediately after a full extension reload, and before the service\n worker takes control of fetch (e.g., in the onInstalled event). In that\n case, this page will continuously reload until the service worker is\n ready, always less than 100 ms.\n </p>\n\n <p>This page is never added in production.</p>\n </body>\n</html>\n";
2066
2067
 
@@ -2512,6 +2513,223 @@ const pluginHtmlAuditor = () => {
2512
2513
  };
2513
2514
  };
2514
2515
 
2516
+ const pluginManifest = (_manifest) => () => {
2517
+ let manifest;
2518
+ let plugins;
2519
+ let refId;
2520
+ let config;
2521
+ return [
2522
+ {
2523
+ name: "crx:manifest-init",
2524
+ enforce: "pre",
2525
+ async config(config2, env) {
2526
+ manifest = await (typeof _manifest === "function" ? _manifest(env) : _manifest);
2527
+ if (manifest.manifest_version !== 3)
2528
+ throw new Error(`Manifest v${manifest.manifest_version} is currently unsupported, please use manifest v3`);
2529
+ if (env.command === "serve") {
2530
+ const {
2531
+ contentScripts: js,
2532
+ background: sw,
2533
+ html
2534
+ } = await manifestFiles(manifest);
2535
+ let { entries = [] } = config2.optimizeDeps ?? {};
2536
+ entries = [entries].flat();
2537
+ const set = new Set(entries);
2538
+ for (const x of [...js, ...sw, ...html])
2539
+ set.add(x);
2540
+ return {
2541
+ ...config2,
2542
+ optimizeDeps: {
2543
+ ...config2.optimizeDeps,
2544
+ entries: [...set]
2545
+ }
2546
+ };
2547
+ }
2548
+ },
2549
+ buildStart(options) {
2550
+ if (options.plugins)
2551
+ plugins = options.plugins;
2552
+ }
2553
+ },
2554
+ {
2555
+ name: "crx:manifest-loader",
2556
+ apply: "build",
2557
+ enforce: "pre",
2558
+ buildStart() {
2559
+ refId = this.emitFile({
2560
+ type: "chunk",
2561
+ id: manifestId,
2562
+ name: "crx-manifest.js",
2563
+ preserveSignature: "strict"
2564
+ });
2565
+ },
2566
+ resolveId(source) {
2567
+ if (source === manifestId)
2568
+ return manifestId;
2569
+ return null;
2570
+ },
2571
+ load(id) {
2572
+ if (id === manifestId)
2573
+ return encodeManifest(manifest);
2574
+ return null;
2575
+ }
2576
+ },
2577
+ {
2578
+ name: "crx:stub-input",
2579
+ apply: "build",
2580
+ enforce: "pre",
2581
+ options({ input, ...options }) {
2582
+ return {
2583
+ input: isString(input) && input.endsWith("index.html") ? stubId : input,
2584
+ ...options
2585
+ };
2586
+ },
2587
+ resolveId(source) {
2588
+ if (source === stubId)
2589
+ return stubId;
2590
+ return null;
2591
+ },
2592
+ load(id) {
2593
+ if (id === stubId)
2594
+ return `console.log('stub')`;
2595
+ return null;
2596
+ },
2597
+ generateBundle(options, bundle) {
2598
+ for (const [key, chunk] of Object.entries(bundle)) {
2599
+ if (chunk.type === "chunk" && chunk.facadeModuleId === stubId) {
2600
+ delete bundle[key];
2601
+ break;
2602
+ }
2603
+ }
2604
+ }
2605
+ },
2606
+ {
2607
+ name: "crx:manifest-post",
2608
+ apply: "build",
2609
+ enforce: "post",
2610
+ configResolved(_config) {
2611
+ config = _config;
2612
+ const plugins2 = config.plugins;
2613
+ const crx = plugins2.findIndex(({ name }) => name === "crx:manifest-post");
2614
+ const [plugin] = plugins2.splice(crx, 1);
2615
+ plugins2.push(plugin);
2616
+ },
2617
+ async transform(code, id) {
2618
+ if (id !== manifestId)
2619
+ return;
2620
+ let manifest2 = decodeManifest.call(this, code);
2621
+ for (const plugin of plugins) {
2622
+ try {
2623
+ const m = structuredClone(manifest2);
2624
+ const result = await plugin.transformCrxManifest?.call(this, m);
2625
+ manifest2 = result ?? manifest2;
2626
+ } catch (error) {
2627
+ if (error instanceof Error)
2628
+ error.message = `[${plugin.name}] ${error.message}`;
2629
+ throw error;
2630
+ }
2631
+ }
2632
+ if (manifest2.content_scripts?.length) {
2633
+ manifest2.content_scripts = manifest2.content_scripts.map(({ js = [], ...rest }) => {
2634
+ const refJS = js.map((file) => this.emitFile({
2635
+ type: "chunk",
2636
+ id: file,
2637
+ name: basename(file)
2638
+ }));
2639
+ return { js: refJS, ...rest };
2640
+ });
2641
+ }
2642
+ if (!this.meta.watchMode) {
2643
+ if (manifest2.background?.service_worker) {
2644
+ const file = manifest2.background.service_worker;
2645
+ const refId2 = this.emitFile({
2646
+ type: "chunk",
2647
+ id: file,
2648
+ name: basename(file)
2649
+ });
2650
+ manifest2.background.service_worker = refId2;
2651
+ }
2652
+ for (const file of htmlFiles(manifest2)) {
2653
+ this.emitFile({
2654
+ type: "chunk",
2655
+ id: file,
2656
+ name: basename(file)
2657
+ });
2658
+ }
2659
+ }
2660
+ const encoded = encodeManifest(manifest2);
2661
+ return encoded;
2662
+ },
2663
+ async generateBundle(options, bundle) {
2664
+ const manifestName = this.getFileName(refId);
2665
+ const manifestJs = bundle[manifestName];
2666
+ let manifest2 = decodeManifest.call(this, manifestJs.code);
2667
+ if (manifest2.background?.service_worker && !this.meta.watchMode) {
2668
+ const ref = manifest2.background.service_worker;
2669
+ const name = this.getFileName(ref);
2670
+ manifest2.background.service_worker = name;
2671
+ }
2672
+ manifest2.content_scripts = manifest2.content_scripts?.map(({ js = [], ...rest }) => {
2673
+ const refJS = js.map((ref) => this.getFileName(ref));
2674
+ return { js: refJS, ...rest };
2675
+ });
2676
+ for (const plugin of plugins) {
2677
+ try {
2678
+ const m = structuredClone(manifest2);
2679
+ const result = await plugin.renderCrxManifest?.call(this, m, bundle);
2680
+ manifest2 = result ?? manifest2;
2681
+ } catch (error) {
2682
+ const name = `[${plugin.name}]`;
2683
+ let message = error;
2684
+ if (error instanceof Error) {
2685
+ message = colors.red(`${name} ${error.stack ? error.stack : error.message}`);
2686
+ } else if (typeof error === "string") {
2687
+ message = colors.red(`${name} ${error}`);
2688
+ }
2689
+ console.log(message);
2690
+ throw new Error(`Error in ${plugin.name}.renderCrxManifest`);
2691
+ }
2692
+ }
2693
+ const assetTypes = [
2694
+ "icons",
2695
+ "locales",
2696
+ "rulesets",
2697
+ "webAccessibleResources"
2698
+ ];
2699
+ const files = await manifestFiles(manifest2);
2700
+ await Promise.all(assetTypes.map((k) => files[k]).flat().map(async (f) => {
2701
+ if (typeof bundle[f] === "undefined") {
2702
+ let filename = join(config.root, f);
2703
+ if (!existsSync(filename))
2704
+ filename = join(config.publicDir, f);
2705
+ if (!existsSync(filename))
2706
+ throw new Error(`ENOENT: Could not load manifest asset "${f}".
2707
+ Manifest assets must exist in one of these directories:
2708
+ Project root: "${config.root}"
2709
+ Public dir: "${config.publicDir}"`);
2710
+ this.emitFile({
2711
+ type: "asset",
2712
+ fileName: f,
2713
+ source: await readFile(filename)
2714
+ });
2715
+ }
2716
+ }));
2717
+ const manifestJson = bundle["manifest.json"];
2718
+ if (typeof manifestJson === "undefined") {
2719
+ this.emitFile({
2720
+ type: "asset",
2721
+ fileName: "manifest.json",
2722
+ source: JSON.stringify(manifest2, null, 2)
2723
+ });
2724
+ } else {
2725
+ manifestJson.source = JSON.stringify(manifest2, null, 2);
2726
+ }
2727
+ delete bundle[manifestName];
2728
+ }
2729
+ }
2730
+ ];
2731
+ };
2732
+
2515
2733
  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 console.log(\"[crx] runtime reload\");\n setTimeout(() => location.reload(), 500);\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";
2516
2734
 
2517
2735
  var contentDevLoader = "(function () {\n 'use strict';\n\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 await import(\n /* @vite-ignore */\n chrome.runtime.getURL(__SCRIPT__)\n );\n })().catch(console.error);\n\n})();\n";
@@ -2950,220 +3168,6 @@ const pluginResources = ({ contentScripts = {} }) => {
2950
3168
  ];
2951
3169
  };
2952
3170
 
2953
- const pluginManifest = (_manifest) => () => {
2954
- let manifest;
2955
- let plugins;
2956
- let refId;
2957
- let config;
2958
- return [
2959
- {
2960
- name: "crx:manifest-init",
2961
- enforce: "pre",
2962
- async config(config2, env) {
2963
- manifest = await (typeof _manifest === "function" ? _manifest(env) : _manifest);
2964
- if (manifest.manifest_version !== 3)
2965
- throw new Error(`Manifest v${manifest.manifest_version} is currently unsupported, please use manifest v3`);
2966
- if (env.command === "serve") {
2967
- const {
2968
- contentScripts: js,
2969
- background: sw,
2970
- html
2971
- } = await manifestFiles(manifest);
2972
- let { entries = [] } = config2.optimizeDeps ?? {};
2973
- entries = [entries].flat();
2974
- const set = new Set(entries);
2975
- for (const x of [...js, ...sw, ...html])
2976
- set.add(x);
2977
- return {
2978
- ...config2,
2979
- optimizeDeps: {
2980
- ...config2.optimizeDeps,
2981
- entries: [...set]
2982
- }
2983
- };
2984
- }
2985
- },
2986
- buildStart(options) {
2987
- if (options.plugins)
2988
- plugins = options.plugins;
2989
- }
2990
- },
2991
- {
2992
- name: "crx:manifest-loader",
2993
- apply: "build",
2994
- enforce: "pre",
2995
- buildStart() {
2996
- refId = this.emitFile({
2997
- type: "chunk",
2998
- id: manifestId,
2999
- name: "crx-manifest.js",
3000
- preserveSignature: "strict"
3001
- });
3002
- },
3003
- resolveId(source) {
3004
- if (source === manifestId)
3005
- return manifestId;
3006
- return null;
3007
- },
3008
- load(id) {
3009
- if (id === manifestId)
3010
- return encodeManifest(manifest);
3011
- return null;
3012
- }
3013
- },
3014
- {
3015
- name: "crx:stub-input",
3016
- apply: "build",
3017
- enforce: "pre",
3018
- options({ input, ...options }) {
3019
- return {
3020
- input: isString(input) && input.endsWith("index.html") ? stubId : input,
3021
- ...options
3022
- };
3023
- },
3024
- resolveId(source) {
3025
- if (source === stubId)
3026
- return stubId;
3027
- return null;
3028
- },
3029
- load(id) {
3030
- if (id === stubId)
3031
- return `console.log('stub')`;
3032
- return null;
3033
- },
3034
- generateBundle(options, bundle) {
3035
- for (const [key, chunk] of Object.entries(bundle)) {
3036
- if (chunk.type === "chunk" && chunk.facadeModuleId === stubId) {
3037
- delete bundle[key];
3038
- break;
3039
- }
3040
- }
3041
- }
3042
- },
3043
- {
3044
- name: "crx:manifest-post",
3045
- apply: "build",
3046
- enforce: "post",
3047
- configResolved(_config) {
3048
- config = _config;
3049
- const plugins2 = config.plugins;
3050
- const crx = plugins2.findIndex(({ name }) => name === "crx:manifest-post");
3051
- const [plugin] = plugins2.splice(crx, 1);
3052
- plugins2.push(plugin);
3053
- },
3054
- async transform(code, id) {
3055
- if (id !== manifestId)
3056
- return;
3057
- let manifest2 = decodeManifest.call(this, code);
3058
- for (const plugin of plugins) {
3059
- try {
3060
- const m = structuredClone(manifest2);
3061
- const result = await plugin.transformCrxManifest?.call(this, m);
3062
- manifest2 = result ?? manifest2;
3063
- } catch (error) {
3064
- if (error instanceof Error)
3065
- error.message = `[${plugin.name}] ${error.message}`;
3066
- throw error;
3067
- }
3068
- }
3069
- if (manifest2.content_scripts?.length) {
3070
- manifest2.content_scripts = manifest2.content_scripts.map(({ js = [], ...rest }) => {
3071
- const refJS = js.map((file) => this.emitFile({
3072
- type: "chunk",
3073
- id: file,
3074
- name: basename(file)
3075
- }));
3076
- return { js: refJS, ...rest };
3077
- });
3078
- }
3079
- if (!this.meta.watchMode) {
3080
- if (manifest2.background?.service_worker) {
3081
- const file = manifest2.background.service_worker;
3082
- const refId2 = this.emitFile({
3083
- type: "chunk",
3084
- id: file,
3085
- name: basename(file)
3086
- });
3087
- manifest2.background.service_worker = refId2;
3088
- }
3089
- for (const file of htmlFiles(manifest2)) {
3090
- this.emitFile({
3091
- type: "chunk",
3092
- id: file,
3093
- name: basename(file)
3094
- });
3095
- }
3096
- }
3097
- const encoded = encodeManifest(manifest2);
3098
- return encoded;
3099
- },
3100
- async generateBundle(options, bundle) {
3101
- const manifestName = this.getFileName(refId);
3102
- const manifestJs = bundle[manifestName];
3103
- let manifest2 = decodeManifest.call(this, manifestJs.code);
3104
- if (manifest2.background?.service_worker && !this.meta.watchMode) {
3105
- const ref = manifest2.background.service_worker;
3106
- const name = this.getFileName(ref);
3107
- manifest2.background.service_worker = name;
3108
- }
3109
- manifest2.content_scripts = manifest2.content_scripts?.map(({ js = [], ...rest }) => {
3110
- const refJS = js.map((ref) => this.getFileName(ref));
3111
- return { js: refJS, ...rest };
3112
- });
3113
- manifest2.web_accessible_resources = manifest2.web_accessible_resources?.map(({ resources, ...rest }) => ({
3114
- resources: resources.map((r) => fg.isDynamicPattern(r) || r === dynamicResourcesName ? r : this.getFileName(r)),
3115
- ...rest
3116
- }));
3117
- for (const plugin of plugins) {
3118
- try {
3119
- const m = structuredClone(manifest2);
3120
- const result = await plugin.renderCrxManifest?.call(this, m, bundle);
3121
- manifest2 = result ?? manifest2;
3122
- } catch (error) {
3123
- const name = `[${plugin.name}]`;
3124
- let message = error;
3125
- if (error instanceof Error) {
3126
- message = colors.red(`${name} ${error.stack ? error.stack : error.message}`);
3127
- } else if (typeof error === "string") {
3128
- message = colors.red(`${name} ${error}`);
3129
- }
3130
- console.log(message);
3131
- throw new Error(`Error in ${plugin.name}.renderCrxManifest`);
3132
- }
3133
- }
3134
- const assetTypes = [
3135
- "icons",
3136
- "locales",
3137
- "rulesets",
3138
- "webAccessibleResources"
3139
- ];
3140
- const files = await manifestFiles(manifest2);
3141
- await Promise.all(assetTypes.map((k) => files[k]).flat().map(async (f) => {
3142
- if (typeof bundle[f] === "undefined") {
3143
- const filename = join(config.root, f);
3144
- this.emitFile({
3145
- type: "asset",
3146
- fileName: f,
3147
- source: await readFile(filename)
3148
- });
3149
- }
3150
- }));
3151
- const manifestJson = bundle["manifest.json"];
3152
- if (typeof manifestJson === "undefined") {
3153
- this.emitFile({
3154
- type: "asset",
3155
- fileName: "manifest.json",
3156
- source: JSON.stringify(manifest2, null, 2)
3157
- });
3158
- } else {
3159
- manifestJson.source = JSON.stringify(manifest2, null, 2);
3160
- }
3161
- delete bundle[manifestName];
3162
- }
3163
- }
3164
- ];
3165
- };
3166
-
3167
3171
  var workerHmrClient = "const ownOrigin = new URL(chrome.runtime.getURL(\"/\")).origin;\nself.addEventListener(\"fetch\", (fetchEvent) => {\n const url = new URL(fetchEvent.request.url);\n if (url.origin === ownOrigin) {\n fetchEvent.respondWith(sendToServer(url));\n }\n});\nasync function sendToServer(url) {\n url.protocol = \"http:\";\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 return new Response(response.body, {\n headers: {\n \"Content-Type\": response.headers.get(\"Content-Type\") ?? \"text/javascript\"\n }\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) => ports.delete(port2));\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 socketHost = `${__HMR_HOSTNAME__ || location.hostname}:${__HMR_PORT__}`;\nconst socket = new WebSocket(`${socketProtocol}://${socketHost}`, \"vite-hmr\");\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 chrome.runtime.reload();\n});\n";
3168
3172
 
3169
3173
  function defineClientValues(code, config) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crxjs/vite-plugin",
3
- "version": "1.0.1",
3
+ "version": "1.0.4",
4
4
  "description": "Build Chrome Extensions with this Vite plugin.",
5
5
  "keywords": [
6
6
  "rollup-plugin",
@@ -72,7 +72,7 @@
72
72
  "jsesc": "^3.0.2",
73
73
  "magic-string": "^0.26.0",
74
74
  "picocolors": "^1.0.0",
75
- "react-refresh": "^0.12.0",
75
+ "react-refresh": "^0.13.0",
76
76
  "rollup": "^2.70.2"
77
77
  },
78
78
  "devDependencies": {
@@ -95,11 +95,9 @@
95
95
  "@typescript-eslint/parser": "5.21.0",
96
96
  "@vitejs/plugin-react": "1.3.1",
97
97
  "@vitejs/plugin-vue": "2.3.1",
98
- "@vue/compiler-sfc": "3.2.33",
99
- "@vueuse/core": "8.3.1",
100
- "@vueuse/router": "8.2.6",
101
98
  "esbuild": "0.14.38",
102
99
  "esbuild-runner": "2.2.1",
100
+ "eslint": "^8.14.0",
103
101
  "jest": "27.5.1",
104
102
  "jest-image-snapshot": "4.5.1",
105
103
  "playwright-chromium": "1.21.1",
@@ -109,7 +107,6 @@
109
107
  "rollup-plugin-dts": "^4.2.0",
110
108
  "rollup-plugin-esbuild": "4.9.1",
111
109
  "rxjs": "7.5.5",
112
- "typescript": "4.6.3",
113
110
  "vite": "^2.9.5",
114
111
  "vite-plugin-inspect": "0.5.0",
115
112
  "vue": "3.2.33"