@crxjs/vite-plugin 1.0.4 → 1.0.7

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,32 @@
1
1
  # @crxjs/vite-plugin
2
2
 
3
+ ## 1.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - e1604d8: Strip paths from content script resource match patterns
8
+
9
+ ## 1.0.6
10
+
11
+ ### Patch Changes
12
+
13
+ - 63d102f: Automatically ignores `build.outDir` for server HMR, so the file
14
+ writer doesn't trigger a full reload.
15
+
16
+ Fixes flaky HMR updates for content scripts; Tailwind should work fine now 🥳
17
+
18
+ - a1e2728: Fix isImporter recursion
19
+
20
+ ## 1.0.5
21
+
22
+ ### Patch Changes
23
+
24
+ - 86adbec: Sometimes during development, an extension page may open before the
25
+ service worker has a chance to control fetch. The HTML file will load from the
26
+ file system, but the script tag might load from the dev server. This PR adds a
27
+ precontroller loader plugin to the dev server so that the extension page will
28
+ reload and the fetch handler will get the real HTML file from the server.
29
+
3
30
  ## 1.0.4
4
31
 
5
32
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -132,6 +132,12 @@ function encodeManifest(manifest) {
132
132
  const json = JSON.stringify(JSON.stringify(manifest));
133
133
  return `export default ${json}`;
134
134
  }
135
+ const stubMatchPattern = (pattern) => {
136
+ const [schema, rest] = pattern.split("://");
137
+ const [origin, pathname] = rest.split("/");
138
+ const root = `${schema}://${origin}`;
139
+ return pathname ? `${root}/*` : root;
140
+ };
135
141
 
136
142
  const idBySource = /* @__PURE__ */ new Map();
137
143
  const idByUrl = /* @__PURE__ */ new Map();
@@ -185,6 +191,7 @@ const setOutputMeta = ({
185
191
  outputByOwner.set(ownerName, output);
186
192
  outputById.set(id, output);
187
193
  };
194
+ const transformResultByOwner = /* @__PURE__ */ new Map();
188
195
 
189
196
  const {
190
197
  basename,
@@ -231,17 +238,12 @@ function sourceToUrlMeta(source) {
231
238
  }
232
239
  const pluginFileWriterChunks = () => {
233
240
  let server;
234
- const ownerToTransformResultMap = /* @__PURE__ */ new Map();
235
241
  return {
236
242
  name: "crx:file-writer-chunks",
237
243
  apply: "build",
238
244
  fileWriterStart(_server) {
239
245
  server = _server;
240
246
  },
241
- watchChange(fileName) {
242
- for (const owner of ownersByFile.get(fileName) ?? /* @__PURE__ */ new Set())
243
- ownerToTransformResultMap.delete(owner);
244
- },
245
247
  async resolveId(source, importer) {
246
248
  if (this.meta.watchMode) {
247
249
  if (idBySource.has(source)) {
@@ -277,12 +279,12 @@ const pluginFileWriterChunks = () => {
277
279
  if (!serverModule)
278
280
  throw new Error(`Unable to load "${url}" from server.`);
279
281
  const { file, url: owner } = serverModule;
280
- transformResult = transformResult ?? ownerToTransformResultMap.get(owner) ?? serverModule.transformResult;
282
+ transformResult = transformResult ?? transformResultByOwner.get(owner) ?? serverModule.transformResult;
281
283
  if (!transformResult)
282
284
  transformResult = await server.transformRequest(url);
283
285
  if (!transformResult)
284
286
  throw new TypeError(`Unable to load "${url}" from server.`);
285
- ownerToTransformResultMap.set(owner, transformResult);
287
+ transformResultByOwner.set(owner, transformResult);
286
288
  if (file) {
287
289
  setFileMeta({ id, file });
288
290
  this.addWatchFile(file);
@@ -369,7 +371,7 @@ const pluginFileWriterChunks = () => {
369
371
  };
370
372
  };
371
373
 
372
- /*! *****************************************************************************
374
+ /******************************************************************************
373
375
  Copyright (c) Microsoft Corporation.
374
376
 
375
377
  Permission to use, copy, modify, and/or distribute this software for any
@@ -1481,6 +1483,12 @@ function last(arr) {
1481
1483
  function popResultSelector(args) {
1482
1484
  return isFunction(last(args)) ? args.pop() : undefined;
1483
1485
  }
1486
+ function popScheduler(args) {
1487
+ return isScheduler(last(args)) ? args.pop() : undefined;
1488
+ }
1489
+ function popNumber(args, defaultValue) {
1490
+ return typeof last(args) === 'number' ? args.pop() : defaultValue;
1491
+ }
1484
1492
 
1485
1493
  var isArrayLike = (function (x) { return x && typeof x.length === 'number' && typeof x !== 'function'; });
1486
1494
 
@@ -1697,6 +1705,126 @@ function executeSchedule(parentSubscription, scheduler, work, delay, repeat) {
1697
1705
  }
1698
1706
  }
1699
1707
 
1708
+ function observeOn(scheduler, delay) {
1709
+ if (delay === void 0) { delay = 0; }
1710
+ return operate(function (source, subscriber) {
1711
+ source.subscribe(createOperatorSubscriber(subscriber, function (value) { return executeSchedule(subscriber, scheduler, function () { return subscriber.next(value); }, delay); }, function () { return executeSchedule(subscriber, scheduler, function () { return subscriber.complete(); }, delay); }, function (err) { return executeSchedule(subscriber, scheduler, function () { return subscriber.error(err); }, delay); }));
1712
+ });
1713
+ }
1714
+
1715
+ function subscribeOn(scheduler, delay) {
1716
+ if (delay === void 0) { delay = 0; }
1717
+ return operate(function (source, subscriber) {
1718
+ subscriber.add(scheduler.schedule(function () { return source.subscribe(subscriber); }, delay));
1719
+ });
1720
+ }
1721
+
1722
+ function scheduleObservable(input, scheduler) {
1723
+ return innerFrom(input).pipe(subscribeOn(scheduler), observeOn(scheduler));
1724
+ }
1725
+
1726
+ function schedulePromise(input, scheduler) {
1727
+ return innerFrom(input).pipe(subscribeOn(scheduler), observeOn(scheduler));
1728
+ }
1729
+
1730
+ function scheduleArray(input, scheduler) {
1731
+ return new Observable(function (subscriber) {
1732
+ var i = 0;
1733
+ return scheduler.schedule(function () {
1734
+ if (i === input.length) {
1735
+ subscriber.complete();
1736
+ }
1737
+ else {
1738
+ subscriber.next(input[i++]);
1739
+ if (!subscriber.closed) {
1740
+ this.schedule();
1741
+ }
1742
+ }
1743
+ });
1744
+ });
1745
+ }
1746
+
1747
+ function scheduleIterable(input, scheduler) {
1748
+ return new Observable(function (subscriber) {
1749
+ var iterator$1;
1750
+ executeSchedule(subscriber, scheduler, function () {
1751
+ iterator$1 = input[iterator]();
1752
+ executeSchedule(subscriber, scheduler, function () {
1753
+ var _a;
1754
+ var value;
1755
+ var done;
1756
+ try {
1757
+ (_a = iterator$1.next(), value = _a.value, done = _a.done);
1758
+ }
1759
+ catch (err) {
1760
+ subscriber.error(err);
1761
+ return;
1762
+ }
1763
+ if (done) {
1764
+ subscriber.complete();
1765
+ }
1766
+ else {
1767
+ subscriber.next(value);
1768
+ }
1769
+ }, 0, true);
1770
+ });
1771
+ return function () { return isFunction(iterator$1 === null || iterator$1 === void 0 ? void 0 : iterator$1.return) && iterator$1.return(); };
1772
+ });
1773
+ }
1774
+
1775
+ function scheduleAsyncIterable(input, scheduler) {
1776
+ if (!input) {
1777
+ throw new Error('Iterable cannot be null');
1778
+ }
1779
+ return new Observable(function (subscriber) {
1780
+ executeSchedule(subscriber, scheduler, function () {
1781
+ var iterator = input[Symbol.asyncIterator]();
1782
+ executeSchedule(subscriber, scheduler, function () {
1783
+ iterator.next().then(function (result) {
1784
+ if (result.done) {
1785
+ subscriber.complete();
1786
+ }
1787
+ else {
1788
+ subscriber.next(result.value);
1789
+ }
1790
+ });
1791
+ }, 0, true);
1792
+ });
1793
+ });
1794
+ }
1795
+
1796
+ function scheduleReadableStreamLike(input, scheduler) {
1797
+ return scheduleAsyncIterable(readableStreamLikeToAsyncGenerator(input), scheduler);
1798
+ }
1799
+
1800
+ function scheduled(input, scheduler) {
1801
+ if (input != null) {
1802
+ if (isInteropObservable(input)) {
1803
+ return scheduleObservable(input, scheduler);
1804
+ }
1805
+ if (isArrayLike(input)) {
1806
+ return scheduleArray(input, scheduler);
1807
+ }
1808
+ if (isPromise(input)) {
1809
+ return schedulePromise(input, scheduler);
1810
+ }
1811
+ if (isAsyncIterable(input)) {
1812
+ return scheduleAsyncIterable(input, scheduler);
1813
+ }
1814
+ if (isIterable(input)) {
1815
+ return scheduleIterable(input, scheduler);
1816
+ }
1817
+ if (isReadableStreamLike(input)) {
1818
+ return scheduleReadableStreamLike(input, scheduler);
1819
+ }
1820
+ }
1821
+ throw createInvalidObservableTypeError(input);
1822
+ }
1823
+
1824
+ function from(input, scheduler) {
1825
+ return scheduler ? scheduled(input, scheduler) : innerFrom(input);
1826
+ }
1827
+
1700
1828
  var EmptyError = createErrorClass(function (_super) { return function EmptyErrorImpl() {
1701
1829
  _super(this);
1702
1830
  this.name = 'EmptyError';
@@ -1807,6 +1935,11 @@ function mergeMap(project, resultSelector, concurrent) {
1807
1935
  return operate(function (source, subscriber) { return mergeInternals(source, subscriber, project, concurrent); });
1808
1936
  }
1809
1937
 
1938
+ function mergeAll(concurrent) {
1939
+ if (concurrent === void 0) { concurrent = Infinity; }
1940
+ return mergeMap(identity, concurrent);
1941
+ }
1942
+
1810
1943
  function timer(dueTime, intervalOrScheduler, scheduler) {
1811
1944
  if (dueTime === void 0) { dueTime = 0; }
1812
1945
  if (scheduler === void 0) { scheduler = async; }
@@ -1839,13 +1972,22 @@ function timer(dueTime, intervalOrScheduler, scheduler) {
1839
1972
  });
1840
1973
  }
1841
1974
 
1842
- function interval(period, scheduler) {
1843
- if (period === void 0) { period = 0; }
1844
- if (scheduler === void 0) { scheduler = asyncScheduler; }
1845
- if (period < 0) {
1846
- period = 0;
1975
+ function merge() {
1976
+ var args = [];
1977
+ for (var _i = 0; _i < arguments.length; _i++) {
1978
+ args[_i] = arguments[_i];
1847
1979
  }
1848
- return timer(period, period, scheduler);
1980
+ var scheduler = popScheduler(args);
1981
+ var concurrent = popNumber(args, Infinity);
1982
+ var sources = args;
1983
+ return !sources.length
1984
+ ?
1985
+ EMPTY
1986
+ : sources.length === 1
1987
+ ?
1988
+ innerFrom(sources[0])
1989
+ :
1990
+ mergeAll(concurrent)(from(sources, scheduler));
1849
1991
  }
1850
1992
 
1851
1993
  function filter(predicate, thisArg) {
@@ -1873,6 +2015,36 @@ function buffer(closingNotifier) {
1873
2015
  });
1874
2016
  }
1875
2017
 
2018
+ function debounce(durationSelector) {
2019
+ return operate(function (source, subscriber) {
2020
+ var hasValue = false;
2021
+ var lastValue = null;
2022
+ var durationSubscriber = null;
2023
+ var emit = function () {
2024
+ durationSubscriber === null || durationSubscriber === void 0 ? void 0 : durationSubscriber.unsubscribe();
2025
+ durationSubscriber = null;
2026
+ if (hasValue) {
2027
+ hasValue = false;
2028
+ var value = lastValue;
2029
+ lastValue = null;
2030
+ subscriber.next(value);
2031
+ }
2032
+ };
2033
+ source.subscribe(createOperatorSubscriber(subscriber, function (value) {
2034
+ durationSubscriber === null || durationSubscriber === void 0 ? void 0 : durationSubscriber.unsubscribe();
2035
+ hasValue = true;
2036
+ lastValue = value;
2037
+ durationSubscriber = createOperatorSubscriber(subscriber, emit, noop);
2038
+ innerFrom(durationSelector(value)).subscribe(durationSubscriber);
2039
+ }, function () {
2040
+ emit();
2041
+ subscriber.complete();
2042
+ }, undefined, function () {
2043
+ lastValue = durationSubscriber = null;
2044
+ }));
2045
+ });
2046
+ }
2047
+
1876
2048
  function defaultIfEmpty(defaultValue) {
1877
2049
  return operate(function (source, subscriber) {
1878
2050
  var hasValue = false;
@@ -1905,10 +2077,6 @@ function take(count) {
1905
2077
  });
1906
2078
  }
1907
2079
 
1908
- function mapTo(value) {
1909
- return map(function () { return value; });
1910
- }
1911
-
1912
2080
  function throwIfEmpty(errorFactory) {
1913
2081
  if (errorFactory === void 0) { errorFactory = defaultErrorFactory; }
1914
2082
  return operate(function (source, subscriber) {
@@ -2003,7 +2171,7 @@ const filesStart$ = writerEvent$.pipe(filter((x) => {
2003
2171
  const filesStart = () => firstValueFrom(filesStart$);
2004
2172
  const filesReady$ = writerEvent$.pipe(filter((x) => {
2005
2173
  return x.type === "writeBundle";
2006
- }), switchMap((event) => interval(100).pipe(mapTo(event), first(({ bundle, options, timestamp }) => {
2174
+ }), switchMap((event) => timer(0, 100).pipe(map(() => event), first(({ bundle, options, timestamp }) => {
2007
2175
  const result = Object.keys(bundle).every((p) => {
2008
2176
  const stats = statSync(join(options.dir, p));
2009
2177
  return stats.mtimeMs > timestamp;
@@ -2094,27 +2262,40 @@ const pluginFileWriterEvents = () => {
2094
2262
  };
2095
2263
  };
2096
2264
 
2097
- var preControllerScript = "const id = setInterval(() => location.reload(), 100);\nsetTimeout(() => clearInterval(id), 5e3);\n";
2265
+ var precontrollerScript = "const id = setInterval(() => location.reload(), 100);\nsetTimeout(() => clearInterval(id), 5e3);\n";
2098
2266
 
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";
2267
+ 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>This page is never added in production.</p>\n </body>\n</html>\n";
2100
2268
 
2101
2269
  const pluginFileWriterHtml = () => {
2270
+ let precontrollerName;
2102
2271
  return {
2103
2272
  name: "crx:file-writer-html",
2104
2273
  apply: "build",
2274
+ fileWriterStart(server) {
2275
+ const plugins = server.config.plugins;
2276
+ const i = plugins.findIndex(({ name }) => name === "alias");
2277
+ plugins.splice(i, 0, {
2278
+ name: "crx:load-precontroller",
2279
+ apply: "serve",
2280
+ load(id) {
2281
+ if (id === `/${precontrollerName}`)
2282
+ return "location.reload();";
2283
+ }
2284
+ });
2285
+ },
2105
2286
  renderCrxManifest(manifest) {
2106
2287
  if (this.meta.watchMode) {
2107
2288
  const refId = this.emitFile({
2108
2289
  type: "asset",
2109
2290
  name: "precontroller.js",
2110
- source: preControllerScript
2291
+ source: precontrollerScript
2111
2292
  });
2112
- const name = this.getFileName(refId);
2293
+ precontrollerName = this.getFileName(refId);
2113
2294
  for (const fileName of htmlFiles(manifest)) {
2114
2295
  this.emitFile({
2115
2296
  type: "asset",
2116
2297
  fileName,
2117
- source: preControllerHtml.replace("%PATH%", `/${name}`)
2298
+ source: precontrollerHtml.replace("%PATH%", `/${precontrollerName}`)
2118
2299
  });
2119
2300
  }
2120
2301
  }
@@ -2139,7 +2320,6 @@ const pluginFileWriterPublic = () => {
2139
2320
  const source = await fsExtra.readFile(file);
2140
2321
  const fileName = relative(config.publicDir, file);
2141
2322
  this.emitFile({ type: "asset", fileName, source });
2142
- this.addWatchFile(file);
2143
2323
  }
2144
2324
  }
2145
2325
  }
@@ -2174,6 +2354,94 @@ const pluginFileWriterPolyfill = () => {
2174
2354
  };
2175
2355
  };
2176
2356
 
2357
+ function isUpdatePayload(p) {
2358
+ return p.type === "update";
2359
+ }
2360
+ function isFullReloadPayload(p) {
2361
+ return p.type === "full-reload";
2362
+ }
2363
+ function isPrunePayload(p) {
2364
+ return p.type === "prune";
2365
+ }
2366
+ function isCrxHMRPayload(x) {
2367
+ return x.type === "custom" && x.event.startsWith("crx:");
2368
+ }
2369
+ const hmrPayload$ = new Subject();
2370
+ const hmrPrune$ = hmrPayload$.pipe(filter(isPrunePayload));
2371
+ const hmrFullReload$ = hmrPayload$.pipe(filter(isFullReloadPayload));
2372
+ const hmrUpdate$ = hmrPayload$.pipe(filter(isUpdatePayload));
2373
+ const payload$ = merge(hmrFullReload$, hmrPrune$, hmrUpdate$);
2374
+ const rebuildSignal$ = payload$.pipe(buffer(payload$.pipe(debounce(() => filesReady$))), map((payloads) => {
2375
+ if (payloads.every(isUpdatePayload)) {
2376
+ const owners = /* @__PURE__ */ new Set();
2377
+ for (const { updates } of payloads)
2378
+ for (const { path } of updates)
2379
+ if (transformResultByOwner.has(path))
2380
+ owners.add(path);
2381
+ return { type: "partial", owners };
2382
+ }
2383
+ return { type: "full" };
2384
+ }), filter((rebuild) => rebuild.type === "partial" ? rebuild.owners.size > 0 : true));
2385
+ const crxHmrPayload$ = hmrPayload$.pipe(filter((p) => !isCrxHMRPayload(p)), buffer(filesReady$), mergeMap((pps) => {
2386
+ let fullReload;
2387
+ const payloads = [];
2388
+ for (const p of pps.slice(-50))
2389
+ if (p.type === "full-reload") {
2390
+ fullReload = p;
2391
+ } else {
2392
+ payloads.push(p);
2393
+ }
2394
+ if (fullReload)
2395
+ payloads.push(fullReload);
2396
+ return payloads;
2397
+ }), map((p) => {
2398
+ switch (p.type) {
2399
+ case "full-reload": {
2400
+ const path = p.path && outputByOwner.get(p.path);
2401
+ const fullReload = {
2402
+ type: "full-reload",
2403
+ path
2404
+ };
2405
+ return fullReload;
2406
+ }
2407
+ case "prune": {
2408
+ const paths = [];
2409
+ for (const owner of p.paths)
2410
+ if (outputByOwner.has(owner))
2411
+ paths.push(outputByOwner.get(owner));
2412
+ return { type: "prune", paths };
2413
+ }
2414
+ case "update": {
2415
+ const updates = [];
2416
+ for (const { acceptedPath, path, ...rest } of p.updates)
2417
+ if (outputByOwner.has(acceptedPath) && outputByOwner.has(path))
2418
+ updates.push({
2419
+ ...rest,
2420
+ acceptedPath: outputByOwner.get(acceptedPath),
2421
+ path: outputByOwner.get(path)
2422
+ });
2423
+ return { type: "update", updates };
2424
+ }
2425
+ default:
2426
+ return p;
2427
+ }
2428
+ }), withLatestFrom(filesReady$), filter(([p, { bundle }]) => {
2429
+ switch (p.type) {
2430
+ case "full-reload":
2431
+ return typeof p.path === "undefined" || p.path in bundle;
2432
+ case "prune":
2433
+ return p.paths.length > 0;
2434
+ case "update":
2435
+ return p.updates.length > 0;
2436
+ default:
2437
+ return true;
2438
+ }
2439
+ }), map(([p]) => ({
2440
+ type: "custom",
2441
+ event: "crx:content-script-payload",
2442
+ data: p
2443
+ })));
2444
+
2177
2445
  function sortPlugins(plugins, command) {
2178
2446
  const pre = [];
2179
2447
  const mid = [];
@@ -2270,6 +2538,18 @@ const pluginFileWriter = (crxPlugins) => (options) => {
2270
2538
  writerEvent$.next({ type: "error", error, code, frame });
2271
2539
  }
2272
2540
  });
2541
+ const rebuildSub = rebuildSignal$.subscribe((rebuild) => {
2542
+ if (rebuild.type === "partial") {
2543
+ for (const owner of rebuild.owners)
2544
+ transformResultByOwner.delete(owner);
2545
+ } else {
2546
+ transformResultByOwner.clear();
2547
+ }
2548
+ rebuildFiles();
2549
+ });
2550
+ watcher.on("close", () => {
2551
+ rebuildSub.unsubscribe();
2552
+ });
2273
2553
  });
2274
2554
  },
2275
2555
  closeBundle() {
@@ -2278,87 +2558,30 @@ const pluginFileWriter = (crxPlugins) => (options) => {
2278
2558
  };
2279
2559
  };
2280
2560
 
2281
- const debug$2 = _debug("hmr");
2282
2561
  function isImporter(file) {
2283
- const pred = (node) => {
2284
- if (node.file === file)
2562
+ const seen = /* @__PURE__ */ new Set();
2563
+ const pred = (changedNode) => {
2564
+ seen.add(changedNode);
2565
+ if (changedNode.file === file)
2285
2566
  return true;
2286
- for (const node2 of node.importers)
2287
- if (pred(node2))
2567
+ for (const parentNode of changedNode.importers) {
2568
+ const unseen = !seen.has(parentNode);
2569
+ if (unseen && pred(parentNode))
2288
2570
  return true;
2571
+ }
2572
+ return false;
2289
2573
  };
2290
2574
  return pred;
2291
2575
  }
2292
- function isCrxHMRPayload(x) {
2293
- return x.type === "custom" && x.event.startsWith("crx:");
2294
- }
2295
- const hmrPayload$ = new Subject();
2296
- const crxHmrPayload$ = hmrPayload$.pipe(filter((p) => !isCrxHMRPayload(p)), buffer(filesReady$), mergeMap((pps) => {
2297
- let fullReload;
2298
- const payloads = [];
2299
- for (const p of pps.slice(-50))
2300
- if (p.type === "full-reload") {
2301
- fullReload = p;
2302
- } else {
2303
- payloads.push(p);
2304
- }
2305
- if (fullReload)
2306
- payloads.push(fullReload);
2307
- return payloads;
2308
- }), map((p) => {
2309
- switch (p.type) {
2310
- case "full-reload": {
2311
- const path = p.path && outputByOwner.get(p.path);
2312
- const fullReload = {
2313
- type: "full-reload",
2314
- path
2315
- };
2316
- return fullReload;
2317
- }
2318
- case "prune": {
2319
- const paths = [];
2320
- for (const owner of p.paths)
2321
- if (outputByOwner.has(owner))
2322
- paths.push(outputByOwner.get(owner));
2323
- return { type: "prune", paths };
2324
- }
2325
- case "update": {
2326
- const updates = [];
2327
- for (const { acceptedPath, path, ...rest } of p.updates)
2328
- if (outputByOwner.has(acceptedPath) && outputByOwner.has(path))
2329
- updates.push({
2330
- ...rest,
2331
- acceptedPath: outputByOwner.get(acceptedPath),
2332
- path: outputByOwner.get(path)
2333
- });
2334
- return { type: "update", updates };
2335
- }
2336
- default:
2337
- return p;
2338
- }
2339
- }), withLatestFrom(filesReady$), filter(([p, { bundle }]) => {
2340
- switch (p.type) {
2341
- case "full-reload":
2342
- return typeof p.path === "undefined" || p.path in bundle;
2343
- case "prune":
2344
- return p.paths.length > 0;
2345
- case "update":
2346
- return p.updates.length > 0;
2347
- default:
2348
- return true;
2349
- }
2350
- }), map(([p]) => ({
2351
- type: "custom",
2352
- event: "crx:content-script-payload",
2353
- data: p
2354
- })));
2576
+
2577
+ const debug$2 = _debug("hmr");
2355
2578
  const crxRuntimeReload = {
2356
2579
  type: "custom",
2357
2580
  event: "crx:runtime-reload"
2358
2581
  };
2359
2582
  const pluginHMR = () => {
2360
2583
  let files;
2361
- let server;
2584
+ let decoratedSend;
2362
2585
  return [
2363
2586
  {
2364
2587
  name: "crx:hmr",
@@ -2375,38 +2598,41 @@ const pluginHMR = () => {
2375
2598
  name: "crx:hmr",
2376
2599
  apply: "serve",
2377
2600
  enforce: "pre",
2378
- config({ server: server2 = {}, ...config }) {
2379
- if (server2.hmr === false)
2601
+ config({ server = {}, ...config }) {
2602
+ if (server.hmr === false)
2380
2603
  return;
2381
- if (server2.hmr === true)
2382
- server2.hmr = {};
2383
- server2.hmr = server2.hmr ?? {};
2384
- server2.hmr.host = "localhost";
2385
- return { server: server2, ...config };
2604
+ if (server.hmr === true)
2605
+ server.hmr = {};
2606
+ server.hmr = server.hmr ?? {};
2607
+ server.hmr.host = "localhost";
2608
+ return { server, ...config };
2386
2609
  },
2387
2610
  configResolved(config) {
2388
2611
  const { watch = {} } = config.server;
2389
2612
  config.server.watch = watch;
2390
2613
  watch.ignored = watch.ignored ? [...new Set([watch.ignored].flat())] : [];
2391
- watch.ignored.push(config.build.outDir);
2614
+ const outDir = isAbsolute(config.build.outDir) ? config.build.outDir : join(config.root, config.build.outDir, "**/*");
2615
+ watch.ignored.push(outDir);
2392
2616
  },
2393
- configureServer(_server) {
2394
- server = _server;
2395
- const { send } = server.ws;
2396
- server.ws.send = (payload) => {
2397
- hmrPayload$.next(payload);
2398
- send(payload);
2399
- };
2400
- crxHmrPayload$.subscribe((payload) => {
2401
- send(payload);
2402
- });
2617
+ configureServer(server) {
2618
+ if (server.ws.send !== decoratedSend) {
2619
+ const { send } = server.ws;
2620
+ decoratedSend = (payload) => {
2621
+ hmrPayload$.next(payload);
2622
+ send(payload);
2623
+ };
2624
+ server.ws.send = decoratedSend;
2625
+ crxHmrPayload$.subscribe((payload) => {
2626
+ send(payload);
2627
+ });
2628
+ }
2403
2629
  },
2404
- handleHotUpdate({ file, modules, server: server2 }) {
2405
- const background = files.background[0] && join(server2.config.root, files.background[0]);
2630
+ handleHotUpdate({ file, modules, server }) {
2631
+ const background = files.background[0] && join(server.config.root, files.background[0]);
2406
2632
  if (background) {
2407
2633
  if (file === background || modules.some(isImporter(background))) {
2408
2634
  debug$2("sending runtime reload");
2409
- server2.ws.send(crxRuntimeReload);
2635
+ server.ws.send(crxRuntimeReload);
2410
2636
  return [];
2411
2637
  }
2412
2638
  }
@@ -3112,6 +3338,7 @@ const pluginResources = ({ contentScripts = {} }) => {
3112
3338
  resource.resources.push(...css);
3113
3339
  }
3114
3340
  if (resource.resources.length) {
3341
+ resource.matches = resource.matches.map(stubMatchPattern);
3115
3342
  manifest.web_accessible_resources.push(resource);
3116
3343
  }
3117
3344
  }
package/dist/index.mjs CHANGED
@@ -99,6 +99,12 @@ function encodeManifest(manifest) {
99
99
  const json = JSON.stringify(JSON.stringify(manifest));
100
100
  return `export default ${json}`;
101
101
  }
102
+ const stubMatchPattern = (pattern) => {
103
+ const [schema, rest] = pattern.split("://");
104
+ const [origin, pathname] = rest.split("/");
105
+ const root = `${schema}://${origin}`;
106
+ return pathname ? `${root}/*` : root;
107
+ };
102
108
 
103
109
  const idBySource = /* @__PURE__ */ new Map();
104
110
  const idByUrl = /* @__PURE__ */ new Map();
@@ -152,6 +158,7 @@ const setOutputMeta = ({
152
158
  outputByOwner.set(ownerName, output);
153
159
  outputById.set(id, output);
154
160
  };
161
+ const transformResultByOwner = /* @__PURE__ */ new Map();
155
162
 
156
163
  const {
157
164
  basename,
@@ -198,17 +205,12 @@ function sourceToUrlMeta(source) {
198
205
  }
199
206
  const pluginFileWriterChunks = () => {
200
207
  let server;
201
- const ownerToTransformResultMap = /* @__PURE__ */ new Map();
202
208
  return {
203
209
  name: "crx:file-writer-chunks",
204
210
  apply: "build",
205
211
  fileWriterStart(_server) {
206
212
  server = _server;
207
213
  },
208
- watchChange(fileName) {
209
- for (const owner of ownersByFile.get(fileName) ?? /* @__PURE__ */ new Set())
210
- ownerToTransformResultMap.delete(owner);
211
- },
212
214
  async resolveId(source, importer) {
213
215
  if (this.meta.watchMode) {
214
216
  if (idBySource.has(source)) {
@@ -244,12 +246,12 @@ const pluginFileWriterChunks = () => {
244
246
  if (!serverModule)
245
247
  throw new Error(`Unable to load "${url}" from server.`);
246
248
  const { file, url: owner } = serverModule;
247
- transformResult = transformResult ?? ownerToTransformResultMap.get(owner) ?? serverModule.transformResult;
249
+ transformResult = transformResult ?? transformResultByOwner.get(owner) ?? serverModule.transformResult;
248
250
  if (!transformResult)
249
251
  transformResult = await server.transformRequest(url);
250
252
  if (!transformResult)
251
253
  throw new TypeError(`Unable to load "${url}" from server.`);
252
- ownerToTransformResultMap.set(owner, transformResult);
254
+ transformResultByOwner.set(owner, transformResult);
253
255
  if (file) {
254
256
  setFileMeta({ id, file });
255
257
  this.addWatchFile(file);
@@ -336,7 +338,7 @@ const pluginFileWriterChunks = () => {
336
338
  };
337
339
  };
338
340
 
339
- /*! *****************************************************************************
341
+ /******************************************************************************
340
342
  Copyright (c) Microsoft Corporation.
341
343
 
342
344
  Permission to use, copy, modify, and/or distribute this software for any
@@ -1448,6 +1450,12 @@ function last(arr) {
1448
1450
  function popResultSelector(args) {
1449
1451
  return isFunction(last(args)) ? args.pop() : undefined;
1450
1452
  }
1453
+ function popScheduler(args) {
1454
+ return isScheduler(last(args)) ? args.pop() : undefined;
1455
+ }
1456
+ function popNumber(args, defaultValue) {
1457
+ return typeof last(args) === 'number' ? args.pop() : defaultValue;
1458
+ }
1451
1459
 
1452
1460
  var isArrayLike = (function (x) { return x && typeof x.length === 'number' && typeof x !== 'function'; });
1453
1461
 
@@ -1664,6 +1672,126 @@ function executeSchedule(parentSubscription, scheduler, work, delay, repeat) {
1664
1672
  }
1665
1673
  }
1666
1674
 
1675
+ function observeOn(scheduler, delay) {
1676
+ if (delay === void 0) { delay = 0; }
1677
+ return operate(function (source, subscriber) {
1678
+ source.subscribe(createOperatorSubscriber(subscriber, function (value) { return executeSchedule(subscriber, scheduler, function () { return subscriber.next(value); }, delay); }, function () { return executeSchedule(subscriber, scheduler, function () { return subscriber.complete(); }, delay); }, function (err) { return executeSchedule(subscriber, scheduler, function () { return subscriber.error(err); }, delay); }));
1679
+ });
1680
+ }
1681
+
1682
+ function subscribeOn(scheduler, delay) {
1683
+ if (delay === void 0) { delay = 0; }
1684
+ return operate(function (source, subscriber) {
1685
+ subscriber.add(scheduler.schedule(function () { return source.subscribe(subscriber); }, delay));
1686
+ });
1687
+ }
1688
+
1689
+ function scheduleObservable(input, scheduler) {
1690
+ return innerFrom(input).pipe(subscribeOn(scheduler), observeOn(scheduler));
1691
+ }
1692
+
1693
+ function schedulePromise(input, scheduler) {
1694
+ return innerFrom(input).pipe(subscribeOn(scheduler), observeOn(scheduler));
1695
+ }
1696
+
1697
+ function scheduleArray(input, scheduler) {
1698
+ return new Observable(function (subscriber) {
1699
+ var i = 0;
1700
+ return scheduler.schedule(function () {
1701
+ if (i === input.length) {
1702
+ subscriber.complete();
1703
+ }
1704
+ else {
1705
+ subscriber.next(input[i++]);
1706
+ if (!subscriber.closed) {
1707
+ this.schedule();
1708
+ }
1709
+ }
1710
+ });
1711
+ });
1712
+ }
1713
+
1714
+ function scheduleIterable(input, scheduler) {
1715
+ return new Observable(function (subscriber) {
1716
+ var iterator$1;
1717
+ executeSchedule(subscriber, scheduler, function () {
1718
+ iterator$1 = input[iterator]();
1719
+ executeSchedule(subscriber, scheduler, function () {
1720
+ var _a;
1721
+ var value;
1722
+ var done;
1723
+ try {
1724
+ (_a = iterator$1.next(), value = _a.value, done = _a.done);
1725
+ }
1726
+ catch (err) {
1727
+ subscriber.error(err);
1728
+ return;
1729
+ }
1730
+ if (done) {
1731
+ subscriber.complete();
1732
+ }
1733
+ else {
1734
+ subscriber.next(value);
1735
+ }
1736
+ }, 0, true);
1737
+ });
1738
+ return function () { return isFunction(iterator$1 === null || iterator$1 === void 0 ? void 0 : iterator$1.return) && iterator$1.return(); };
1739
+ });
1740
+ }
1741
+
1742
+ function scheduleAsyncIterable(input, scheduler) {
1743
+ if (!input) {
1744
+ throw new Error('Iterable cannot be null');
1745
+ }
1746
+ return new Observable(function (subscriber) {
1747
+ executeSchedule(subscriber, scheduler, function () {
1748
+ var iterator = input[Symbol.asyncIterator]();
1749
+ executeSchedule(subscriber, scheduler, function () {
1750
+ iterator.next().then(function (result) {
1751
+ if (result.done) {
1752
+ subscriber.complete();
1753
+ }
1754
+ else {
1755
+ subscriber.next(result.value);
1756
+ }
1757
+ });
1758
+ }, 0, true);
1759
+ });
1760
+ });
1761
+ }
1762
+
1763
+ function scheduleReadableStreamLike(input, scheduler) {
1764
+ return scheduleAsyncIterable(readableStreamLikeToAsyncGenerator(input), scheduler);
1765
+ }
1766
+
1767
+ function scheduled(input, scheduler) {
1768
+ if (input != null) {
1769
+ if (isInteropObservable(input)) {
1770
+ return scheduleObservable(input, scheduler);
1771
+ }
1772
+ if (isArrayLike(input)) {
1773
+ return scheduleArray(input, scheduler);
1774
+ }
1775
+ if (isPromise(input)) {
1776
+ return schedulePromise(input, scheduler);
1777
+ }
1778
+ if (isAsyncIterable(input)) {
1779
+ return scheduleAsyncIterable(input, scheduler);
1780
+ }
1781
+ if (isIterable(input)) {
1782
+ return scheduleIterable(input, scheduler);
1783
+ }
1784
+ if (isReadableStreamLike(input)) {
1785
+ return scheduleReadableStreamLike(input, scheduler);
1786
+ }
1787
+ }
1788
+ throw createInvalidObservableTypeError(input);
1789
+ }
1790
+
1791
+ function from(input, scheduler) {
1792
+ return scheduler ? scheduled(input, scheduler) : innerFrom(input);
1793
+ }
1794
+
1667
1795
  var EmptyError = createErrorClass(function (_super) { return function EmptyErrorImpl() {
1668
1796
  _super(this);
1669
1797
  this.name = 'EmptyError';
@@ -1774,6 +1902,11 @@ function mergeMap(project, resultSelector, concurrent) {
1774
1902
  return operate(function (source, subscriber) { return mergeInternals(source, subscriber, project, concurrent); });
1775
1903
  }
1776
1904
 
1905
+ function mergeAll(concurrent) {
1906
+ if (concurrent === void 0) { concurrent = Infinity; }
1907
+ return mergeMap(identity, concurrent);
1908
+ }
1909
+
1777
1910
  function timer(dueTime, intervalOrScheduler, scheduler) {
1778
1911
  if (dueTime === void 0) { dueTime = 0; }
1779
1912
  if (scheduler === void 0) { scheduler = async; }
@@ -1806,13 +1939,22 @@ function timer(dueTime, intervalOrScheduler, scheduler) {
1806
1939
  });
1807
1940
  }
1808
1941
 
1809
- function interval(period, scheduler) {
1810
- if (period === void 0) { period = 0; }
1811
- if (scheduler === void 0) { scheduler = asyncScheduler; }
1812
- if (period < 0) {
1813
- period = 0;
1942
+ function merge() {
1943
+ var args = [];
1944
+ for (var _i = 0; _i < arguments.length; _i++) {
1945
+ args[_i] = arguments[_i];
1814
1946
  }
1815
- return timer(period, period, scheduler);
1947
+ var scheduler = popScheduler(args);
1948
+ var concurrent = popNumber(args, Infinity);
1949
+ var sources = args;
1950
+ return !sources.length
1951
+ ?
1952
+ EMPTY
1953
+ : sources.length === 1
1954
+ ?
1955
+ innerFrom(sources[0])
1956
+ :
1957
+ mergeAll(concurrent)(from(sources, scheduler));
1816
1958
  }
1817
1959
 
1818
1960
  function filter(predicate, thisArg) {
@@ -1840,6 +1982,36 @@ function buffer(closingNotifier) {
1840
1982
  });
1841
1983
  }
1842
1984
 
1985
+ function debounce(durationSelector) {
1986
+ return operate(function (source, subscriber) {
1987
+ var hasValue = false;
1988
+ var lastValue = null;
1989
+ var durationSubscriber = null;
1990
+ var emit = function () {
1991
+ durationSubscriber === null || durationSubscriber === void 0 ? void 0 : durationSubscriber.unsubscribe();
1992
+ durationSubscriber = null;
1993
+ if (hasValue) {
1994
+ hasValue = false;
1995
+ var value = lastValue;
1996
+ lastValue = null;
1997
+ subscriber.next(value);
1998
+ }
1999
+ };
2000
+ source.subscribe(createOperatorSubscriber(subscriber, function (value) {
2001
+ durationSubscriber === null || durationSubscriber === void 0 ? void 0 : durationSubscriber.unsubscribe();
2002
+ hasValue = true;
2003
+ lastValue = value;
2004
+ durationSubscriber = createOperatorSubscriber(subscriber, emit, noop);
2005
+ innerFrom(durationSelector(value)).subscribe(durationSubscriber);
2006
+ }, function () {
2007
+ emit();
2008
+ subscriber.complete();
2009
+ }, undefined, function () {
2010
+ lastValue = durationSubscriber = null;
2011
+ }));
2012
+ });
2013
+ }
2014
+
1843
2015
  function defaultIfEmpty(defaultValue) {
1844
2016
  return operate(function (source, subscriber) {
1845
2017
  var hasValue = false;
@@ -1872,10 +2044,6 @@ function take(count) {
1872
2044
  });
1873
2045
  }
1874
2046
 
1875
- function mapTo(value) {
1876
- return map(function () { return value; });
1877
- }
1878
-
1879
2047
  function throwIfEmpty(errorFactory) {
1880
2048
  if (errorFactory === void 0) { errorFactory = defaultErrorFactory; }
1881
2049
  return operate(function (source, subscriber) {
@@ -1970,7 +2138,7 @@ const filesStart$ = writerEvent$.pipe(filter((x) => {
1970
2138
  const filesStart = () => firstValueFrom(filesStart$);
1971
2139
  const filesReady$ = writerEvent$.pipe(filter((x) => {
1972
2140
  return x.type === "writeBundle";
1973
- }), switchMap((event) => interval(100).pipe(mapTo(event), first(({ bundle, options, timestamp }) => {
2141
+ }), switchMap((event) => timer(0, 100).pipe(map(() => event), first(({ bundle, options, timestamp }) => {
1974
2142
  const result = Object.keys(bundle).every((p) => {
1975
2143
  const stats = statSync(join(options.dir, p));
1976
2144
  return stats.mtimeMs > timestamp;
@@ -2061,27 +2229,40 @@ const pluginFileWriterEvents = () => {
2061
2229
  };
2062
2230
  };
2063
2231
 
2064
- var preControllerScript = "const id = setInterval(() => location.reload(), 100);\nsetTimeout(() => clearInterval(id), 5e3);\n";
2232
+ var precontrollerScript = "const id = setInterval(() => location.reload(), 100);\nsetTimeout(() => clearInterval(id), 5e3);\n";
2065
2233
 
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";
2234
+ 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>This page is never added in production.</p>\n </body>\n</html>\n";
2067
2235
 
2068
2236
  const pluginFileWriterHtml = () => {
2237
+ let precontrollerName;
2069
2238
  return {
2070
2239
  name: "crx:file-writer-html",
2071
2240
  apply: "build",
2241
+ fileWriterStart(server) {
2242
+ const plugins = server.config.plugins;
2243
+ const i = plugins.findIndex(({ name }) => name === "alias");
2244
+ plugins.splice(i, 0, {
2245
+ name: "crx:load-precontroller",
2246
+ apply: "serve",
2247
+ load(id) {
2248
+ if (id === `/${precontrollerName}`)
2249
+ return "location.reload();";
2250
+ }
2251
+ });
2252
+ },
2072
2253
  renderCrxManifest(manifest) {
2073
2254
  if (this.meta.watchMode) {
2074
2255
  const refId = this.emitFile({
2075
2256
  type: "asset",
2076
2257
  name: "precontroller.js",
2077
- source: preControllerScript
2258
+ source: precontrollerScript
2078
2259
  });
2079
- const name = this.getFileName(refId);
2260
+ precontrollerName = this.getFileName(refId);
2080
2261
  for (const fileName of htmlFiles(manifest)) {
2081
2262
  this.emitFile({
2082
2263
  type: "asset",
2083
2264
  fileName,
2084
- source: preControllerHtml.replace("%PATH%", `/${name}`)
2265
+ source: precontrollerHtml.replace("%PATH%", `/${precontrollerName}`)
2085
2266
  });
2086
2267
  }
2087
2268
  }
@@ -2106,7 +2287,6 @@ const pluginFileWriterPublic = () => {
2106
2287
  const source = await readFile(file);
2107
2288
  const fileName = relative(config.publicDir, file);
2108
2289
  this.emitFile({ type: "asset", fileName, source });
2109
- this.addWatchFile(file);
2110
2290
  }
2111
2291
  }
2112
2292
  }
@@ -2141,6 +2321,94 @@ const pluginFileWriterPolyfill = () => {
2141
2321
  };
2142
2322
  };
2143
2323
 
2324
+ function isUpdatePayload(p) {
2325
+ return p.type === "update";
2326
+ }
2327
+ function isFullReloadPayload(p) {
2328
+ return p.type === "full-reload";
2329
+ }
2330
+ function isPrunePayload(p) {
2331
+ return p.type === "prune";
2332
+ }
2333
+ function isCrxHMRPayload(x) {
2334
+ return x.type === "custom" && x.event.startsWith("crx:");
2335
+ }
2336
+ const hmrPayload$ = new Subject();
2337
+ const hmrPrune$ = hmrPayload$.pipe(filter(isPrunePayload));
2338
+ const hmrFullReload$ = hmrPayload$.pipe(filter(isFullReloadPayload));
2339
+ const hmrUpdate$ = hmrPayload$.pipe(filter(isUpdatePayload));
2340
+ const payload$ = merge(hmrFullReload$, hmrPrune$, hmrUpdate$);
2341
+ const rebuildSignal$ = payload$.pipe(buffer(payload$.pipe(debounce(() => filesReady$))), map((payloads) => {
2342
+ if (payloads.every(isUpdatePayload)) {
2343
+ const owners = /* @__PURE__ */ new Set();
2344
+ for (const { updates } of payloads)
2345
+ for (const { path } of updates)
2346
+ if (transformResultByOwner.has(path))
2347
+ owners.add(path);
2348
+ return { type: "partial", owners };
2349
+ }
2350
+ return { type: "full" };
2351
+ }), filter((rebuild) => rebuild.type === "partial" ? rebuild.owners.size > 0 : true));
2352
+ const crxHmrPayload$ = hmrPayload$.pipe(filter((p) => !isCrxHMRPayload(p)), buffer(filesReady$), mergeMap((pps) => {
2353
+ let fullReload;
2354
+ const payloads = [];
2355
+ for (const p of pps.slice(-50))
2356
+ if (p.type === "full-reload") {
2357
+ fullReload = p;
2358
+ } else {
2359
+ payloads.push(p);
2360
+ }
2361
+ if (fullReload)
2362
+ payloads.push(fullReload);
2363
+ return payloads;
2364
+ }), map((p) => {
2365
+ switch (p.type) {
2366
+ case "full-reload": {
2367
+ const path = p.path && outputByOwner.get(p.path);
2368
+ const fullReload = {
2369
+ type: "full-reload",
2370
+ path
2371
+ };
2372
+ return fullReload;
2373
+ }
2374
+ case "prune": {
2375
+ const paths = [];
2376
+ for (const owner of p.paths)
2377
+ if (outputByOwner.has(owner))
2378
+ paths.push(outputByOwner.get(owner));
2379
+ return { type: "prune", paths };
2380
+ }
2381
+ case "update": {
2382
+ const updates = [];
2383
+ for (const { acceptedPath, path, ...rest } of p.updates)
2384
+ if (outputByOwner.has(acceptedPath) && outputByOwner.has(path))
2385
+ updates.push({
2386
+ ...rest,
2387
+ acceptedPath: outputByOwner.get(acceptedPath),
2388
+ path: outputByOwner.get(path)
2389
+ });
2390
+ return { type: "update", updates };
2391
+ }
2392
+ default:
2393
+ return p;
2394
+ }
2395
+ }), withLatestFrom(filesReady$), filter(([p, { bundle }]) => {
2396
+ switch (p.type) {
2397
+ case "full-reload":
2398
+ return typeof p.path === "undefined" || p.path in bundle;
2399
+ case "prune":
2400
+ return p.paths.length > 0;
2401
+ case "update":
2402
+ return p.updates.length > 0;
2403
+ default:
2404
+ return true;
2405
+ }
2406
+ }), map(([p]) => ({
2407
+ type: "custom",
2408
+ event: "crx:content-script-payload",
2409
+ data: p
2410
+ })));
2411
+
2144
2412
  function sortPlugins(plugins, command) {
2145
2413
  const pre = [];
2146
2414
  const mid = [];
@@ -2237,6 +2505,18 @@ const pluginFileWriter = (crxPlugins) => (options) => {
2237
2505
  writerEvent$.next({ type: "error", error, code, frame });
2238
2506
  }
2239
2507
  });
2508
+ const rebuildSub = rebuildSignal$.subscribe((rebuild) => {
2509
+ if (rebuild.type === "partial") {
2510
+ for (const owner of rebuild.owners)
2511
+ transformResultByOwner.delete(owner);
2512
+ } else {
2513
+ transformResultByOwner.clear();
2514
+ }
2515
+ rebuildFiles();
2516
+ });
2517
+ watcher.on("close", () => {
2518
+ rebuildSub.unsubscribe();
2519
+ });
2240
2520
  });
2241
2521
  },
2242
2522
  closeBundle() {
@@ -2245,87 +2525,30 @@ const pluginFileWriter = (crxPlugins) => (options) => {
2245
2525
  };
2246
2526
  };
2247
2527
 
2248
- const debug$2 = _debug("hmr");
2249
2528
  function isImporter(file) {
2250
- const pred = (node) => {
2251
- if (node.file === file)
2529
+ const seen = /* @__PURE__ */ new Set();
2530
+ const pred = (changedNode) => {
2531
+ seen.add(changedNode);
2532
+ if (changedNode.file === file)
2252
2533
  return true;
2253
- for (const node2 of node.importers)
2254
- if (pred(node2))
2534
+ for (const parentNode of changedNode.importers) {
2535
+ const unseen = !seen.has(parentNode);
2536
+ if (unseen && pred(parentNode))
2255
2537
  return true;
2538
+ }
2539
+ return false;
2256
2540
  };
2257
2541
  return pred;
2258
2542
  }
2259
- function isCrxHMRPayload(x) {
2260
- return x.type === "custom" && x.event.startsWith("crx:");
2261
- }
2262
- const hmrPayload$ = new Subject();
2263
- const crxHmrPayload$ = hmrPayload$.pipe(filter((p) => !isCrxHMRPayload(p)), buffer(filesReady$), mergeMap((pps) => {
2264
- let fullReload;
2265
- const payloads = [];
2266
- for (const p of pps.slice(-50))
2267
- if (p.type === "full-reload") {
2268
- fullReload = p;
2269
- } else {
2270
- payloads.push(p);
2271
- }
2272
- if (fullReload)
2273
- payloads.push(fullReload);
2274
- return payloads;
2275
- }), map((p) => {
2276
- switch (p.type) {
2277
- case "full-reload": {
2278
- const path = p.path && outputByOwner.get(p.path);
2279
- const fullReload = {
2280
- type: "full-reload",
2281
- path
2282
- };
2283
- return fullReload;
2284
- }
2285
- case "prune": {
2286
- const paths = [];
2287
- for (const owner of p.paths)
2288
- if (outputByOwner.has(owner))
2289
- paths.push(outputByOwner.get(owner));
2290
- return { type: "prune", paths };
2291
- }
2292
- case "update": {
2293
- const updates = [];
2294
- for (const { acceptedPath, path, ...rest } of p.updates)
2295
- if (outputByOwner.has(acceptedPath) && outputByOwner.has(path))
2296
- updates.push({
2297
- ...rest,
2298
- acceptedPath: outputByOwner.get(acceptedPath),
2299
- path: outputByOwner.get(path)
2300
- });
2301
- return { type: "update", updates };
2302
- }
2303
- default:
2304
- return p;
2305
- }
2306
- }), withLatestFrom(filesReady$), filter(([p, { bundle }]) => {
2307
- switch (p.type) {
2308
- case "full-reload":
2309
- return typeof p.path === "undefined" || p.path in bundle;
2310
- case "prune":
2311
- return p.paths.length > 0;
2312
- case "update":
2313
- return p.updates.length > 0;
2314
- default:
2315
- return true;
2316
- }
2317
- }), map(([p]) => ({
2318
- type: "custom",
2319
- event: "crx:content-script-payload",
2320
- data: p
2321
- })));
2543
+
2544
+ const debug$2 = _debug("hmr");
2322
2545
  const crxRuntimeReload = {
2323
2546
  type: "custom",
2324
2547
  event: "crx:runtime-reload"
2325
2548
  };
2326
2549
  const pluginHMR = () => {
2327
2550
  let files;
2328
- let server;
2551
+ let decoratedSend;
2329
2552
  return [
2330
2553
  {
2331
2554
  name: "crx:hmr",
@@ -2342,38 +2565,41 @@ const pluginHMR = () => {
2342
2565
  name: "crx:hmr",
2343
2566
  apply: "serve",
2344
2567
  enforce: "pre",
2345
- config({ server: server2 = {}, ...config }) {
2346
- if (server2.hmr === false)
2568
+ config({ server = {}, ...config }) {
2569
+ if (server.hmr === false)
2347
2570
  return;
2348
- if (server2.hmr === true)
2349
- server2.hmr = {};
2350
- server2.hmr = server2.hmr ?? {};
2351
- server2.hmr.host = "localhost";
2352
- return { server: server2, ...config };
2571
+ if (server.hmr === true)
2572
+ server.hmr = {};
2573
+ server.hmr = server.hmr ?? {};
2574
+ server.hmr.host = "localhost";
2575
+ return { server, ...config };
2353
2576
  },
2354
2577
  configResolved(config) {
2355
2578
  const { watch = {} } = config.server;
2356
2579
  config.server.watch = watch;
2357
2580
  watch.ignored = watch.ignored ? [...new Set([watch.ignored].flat())] : [];
2358
- watch.ignored.push(config.build.outDir);
2581
+ const outDir = isAbsolute(config.build.outDir) ? config.build.outDir : join(config.root, config.build.outDir, "**/*");
2582
+ watch.ignored.push(outDir);
2359
2583
  },
2360
- configureServer(_server) {
2361
- server = _server;
2362
- const { send } = server.ws;
2363
- server.ws.send = (payload) => {
2364
- hmrPayload$.next(payload);
2365
- send(payload);
2366
- };
2367
- crxHmrPayload$.subscribe((payload) => {
2368
- send(payload);
2369
- });
2584
+ configureServer(server) {
2585
+ if (server.ws.send !== decoratedSend) {
2586
+ const { send } = server.ws;
2587
+ decoratedSend = (payload) => {
2588
+ hmrPayload$.next(payload);
2589
+ send(payload);
2590
+ };
2591
+ server.ws.send = decoratedSend;
2592
+ crxHmrPayload$.subscribe((payload) => {
2593
+ send(payload);
2594
+ });
2595
+ }
2370
2596
  },
2371
- handleHotUpdate({ file, modules, server: server2 }) {
2372
- const background = files.background[0] && join(server2.config.root, files.background[0]);
2597
+ handleHotUpdate({ file, modules, server }) {
2598
+ const background = files.background[0] && join(server.config.root, files.background[0]);
2373
2599
  if (background) {
2374
2600
  if (file === background || modules.some(isImporter(background))) {
2375
2601
  debug$2("sending runtime reload");
2376
- server2.ws.send(crxRuntimeReload);
2602
+ server.ws.send(crxRuntimeReload);
2377
2603
  return [];
2378
2604
  }
2379
2605
  }
@@ -3079,6 +3305,7 @@ const pluginResources = ({ contentScripts = {} }) => {
3079
3305
  resource.resources.push(...css);
3080
3306
  }
3081
3307
  if (resource.resources.length) {
3308
+ resource.matches = resource.matches.map(stubMatchPattern);
3082
3309
  manifest.web_accessible_resources.push(resource);
3083
3310
  }
3084
3311
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crxjs/vite-plugin",
3
- "version": "1.0.4",
3
+ "version": "1.0.7",
4
4
  "description": "Build Chrome Extensions with this Vite plugin.",
5
5
  "keywords": [
6
6
  "rollup-plugin",
@@ -50,12 +50,13 @@
50
50
  "lint": "run-s lint:eslint lint:types",
51
51
  "lint:eslint": "eslint \"{src,test}/**/*.ts\"",
52
52
  "lint:types": "tsc --noEmit",
53
- "test": "run-s test:update:*",
53
+ "test": "run-s test:units test:update:*",
54
54
  "test:ci:e2e:build": "node --expose-gc ./node_modules/jest/bin/jest.js --logHeapUsage --runInBand --forceExit --testPathIgnorePatterns=\\(/node_modules/ /templates/ /mv3/\\) -t build",
55
55
  "test:ci:e2e:serve": "npm run test:ci:e2e:serve:cmd || npm run test:ci:e2e:serve:cmd",
56
56
  "test:ci:e2e:serve:cmd": "node --expose-gc ./node_modules/jest/bin/jest.js --logHeapUsage --runInBand --forceExit --testPathIgnorePatterns=\\(/node_modules/ /templates/ /mv3/\\) -t serve",
57
57
  "test:ci:mv3:build": "node --expose-gc ./node_modules/jest/bin/jest.js --logHeapUsage --runInBand --forceExit --testPathIgnorePatterns=\\(/node_modules/ /templates/ /e2e/\\) --updateSnapshot build",
58
58
  "test:ci:mv3:serve": "node --expose-gc ./node_modules/jest/bin/jest.js --logHeapUsage --runInBand --forceExit --testPathIgnorePatterns=\\(/node_modules/ /templates/ /e2e/\\) --updateSnapshot serve",
59
+ "test:units": "jest src",
59
60
  "test:update:build": "jest build -u",
60
61
  "test:update:serve": "jest serve -u"
61
62
  },
@@ -82,22 +83,22 @@
82
83
  "@rollup/plugin-commonjs": "21.1.0",
83
84
  "@rollup/plugin-node-resolve": "13.2.0",
84
85
  "@types/acorn": "4.0.6",
85
- "@types/chrome": "0.0.183",
86
+ "@types/chrome": "0.0.188",
86
87
  "@types/debug": "4.1.7",
87
88
  "@types/fs-extra": "9.0.13",
88
- "@types/jest": "27.4.1",
89
+ "@types/jest": "27.5.1",
89
90
  "@types/jest-image-snapshot": "4.3.1",
90
91
  "@types/jsesc": "3.0.1",
91
92
  "@types/node": "17.0.18",
92
93
  "@types/react": "17.0.44",
93
- "@types/react-dom": "17.0.16",
94
- "@typescript-eslint/eslint-plugin": "5.21.0",
95
- "@typescript-eslint/parser": "5.21.0",
96
- "@vitejs/plugin-react": "1.3.1",
97
- "@vitejs/plugin-vue": "2.3.1",
94
+ "@types/react-dom": "17.0.17",
95
+ "@typescript-eslint/eslint-plugin": "5.23.0",
96
+ "@typescript-eslint/parser": "5.23.0",
97
+ "@vitejs/plugin-react": "1.3.2",
98
+ "@vitejs/plugin-vue": "2.3.2",
98
99
  "esbuild": "0.14.38",
99
100
  "esbuild-runner": "2.2.1",
100
- "eslint": "^8.14.0",
101
+ "eslint": "8.15.0",
101
102
  "jest": "27.5.1",
102
103
  "jest-image-snapshot": "4.5.1",
103
104
  "playwright-chromium": "1.21.1",
@@ -107,9 +108,10 @@
107
108
  "rollup-plugin-dts": "^4.2.0",
108
109
  "rollup-plugin-esbuild": "4.9.1",
109
110
  "rxjs": "7.5.5",
111
+ "typescript": "^4.6.4",
110
112
  "vite": "^2.9.5",
111
113
  "vite-plugin-inspect": "0.5.0",
112
- "vue": "3.2.33"
114
+ "vue": "3.2.36"
113
115
  },
114
116
  "peerDependencies": {
115
117
  "vite": "^2.9.0"