extension-develop 4.0.26 → 4.0.27

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.
@@ -12,7 +12,7 @@ import { filterKeysForThisBrowser as external_browser_extension_manifest_fields_
12
12
  import content_security_policy_parser from "content-security-policy-parser";
13
13
  import node_fs from "node:fs";
14
14
  import node_path from "node:path";
15
- import { debugExtensionsToLoad, package_namespaceObject, getDirs, asAbsolute, browserRunnerDisabled, treeWithDistFilesBrowser, packagingDistributionFiles, toPosixPath, resolveCompanionExtensionDirs, debugContextPath, PlaywrightPlugin, isGeckoBasedBrowser, spacerLine, messages_ready, CHROMIUM_FAMILY_ALIASES, treeWithSourceAndDistFiles, computeExtensionsToLoad, noCompanionExtensionsResolved, treeWithSourceFiles, bundlerFatalError, bundlerRecompiling, noEntrypointsDetected, GECKO_FAMILY_ALIASES, packagingSourceFiles, debugBrowser, debugOutputPath, manifestInvalidJson, isChromiumBasedBrowser, getSpecialFoldersDataForCompiler } from "./101.mjs";
15
+ import { debugExtensionsToLoad, package_namespaceObject, getDirs, asAbsolute, browserRunnerDisabled, treeWithDistFilesBrowser, packagingDistributionFiles, toPosixPath, resolveCompanionExtensionDirs, debugContextPath, PlaywrightPlugin, isWebkitBasedBrowser, spacerLine, isGeckoBasedBrowser, messages_ready, CHROMIUM_FAMILY_ALIASES, treeWithSourceAndDistFiles, computeExtensionsToLoad, noCompanionExtensionsResolved, treeWithSourceFiles, bundlerFatalError, bundlerRecompiling, noEntrypointsDetected, GECKO_FAMILY_ALIASES, packagingSourceFiles, debugBrowser, debugOutputPath, manifestInvalidJson, isChromiumBasedBrowser, getSpecialFoldersDataForCompiler } from "./101.mjs";
16
16
  import { stripBom, parseJsonSafe } from "./23.mjs";
17
17
  import { isResourceUnderDirs, canonicalizeDir, toResourceKey } from "./93.mjs";
18
18
  import { prefix as messaging_prefix, isDebug } from "./349.mjs";
@@ -377,6 +377,7 @@ class BoringPlugin {
377
377
  browser;
378
378
  sawUserInvalidation = false;
379
379
  printedStartupSuccess = false;
380
+ printedStartupWarning = false;
380
381
  lastKnownManifestName;
381
382
  constructor(options){
382
383
  this.manifestPath = options.manifestPath;
@@ -386,6 +387,7 @@ class BoringPlugin {
386
387
  compiler.hooks.watchClose.tap('develop:brand:watch-close', ()=>{
387
388
  this.sawUserInvalidation = false;
388
389
  this.printedStartupSuccess = false;
390
+ this.printedStartupWarning = false;
389
391
  });
390
392
  compiler.hooks.done.tap('develop:brand', (stats)=>{
391
393
  const hasErrors = Boolean(stats?.hasErrors?.());
@@ -396,8 +398,10 @@ class BoringPlugin {
396
398
  let manifestName;
397
399
  try {
398
400
  const parsedName = parseJsonSafe(__rspack_external_node_fs_5ea92f0c.readFileSync(this.manifestPath, 'utf-8')).name;
399
- if ('string' == typeof parsedName && parsedName) this.lastKnownManifestName = parsedName;
400
- manifestName = parsedName;
401
+ if ('string' == typeof parsedName && parsedName) {
402
+ this.lastKnownManifestName = parsedName;
403
+ manifestName = parsedName;
404
+ } else manifestName = this.lastKnownManifestName;
401
405
  } catch {
402
406
  manifestName = this.lastKnownManifestName;
403
407
  }
@@ -413,16 +417,22 @@ class BoringPlugin {
413
417
  ].map((file)=>String(file).replace(/\\/g, '/'));
414
418
  if (!this.sawUserInvalidation && modifiedFiles.length > 0) {
415
419
  const context = String(compiler?.options?.context || '').replace(/\\/g, '/');
420
+ const outputPath = String(compiler?.options?.output?.path || '').replace(/\\/g, '/');
421
+ const distRoot = context ? `${context}/dist` : '';
422
+ const isUnderRoot = (file, root)=>'' !== root && (file === root || file.startsWith(`${root}/`));
416
423
  const hasUserFileChange = modifiedFiles.some((file)=>{
417
424
  const inProject = !context || file.startsWith(`${context}/`);
418
- const isGenerated = file.includes('/dist/') || file.includes('/extension-js/profiles/');
425
+ const isGenerated = isUnderRoot(file, outputPath) || isUnderRoot(file, distRoot) || file.includes('/extension-js/profiles/');
419
426
  return inProject && !isGenerated;
420
427
  });
421
428
  if (hasUserFileChange) this.sawUserInvalidation = true;
422
429
  }
423
- if (browserLaunchEnabled && !hasErrors && !hasWarnings) {
424
- if (!this.sawUserInvalidation) if (this.printedStartupSuccess) return;
425
- else this.printedStartupSuccess = true;
430
+ if (browserLaunchEnabled && !hasErrors && !this.sawUserInvalidation) if (hasWarnings) {
431
+ if (this.printedStartupWarning) return;
432
+ this.printedStartupWarning = true;
433
+ } else {
434
+ if (this.printedStartupSuccess) return;
435
+ this.printedStartupSuccess = true;
426
436
  }
427
437
  humanLine(line);
428
438
  } catch {}
@@ -436,7 +446,8 @@ class CleanDistFolderPlugin {
436
446
  }
437
447
  apply(compiler) {
438
448
  const logger = compiler.getInfrastructureLogger('plugin-compilation:clean');
439
- const distPath = __rspack_external_node_path_c5b9b54f.join(compiler.options.context, 'dist', this.options.browser);
449
+ const configuredOutputPath = compiler.options.output?.path;
450
+ const distPath = 'string' == typeof configuredOutputPath && configuredOutputPath ? configuredOutputPath : __rspack_external_node_path_c5b9b54f.join(compiler.options.context, 'dist', this.options.browser);
440
451
  if (__rspack_external_node_fs_5ea92f0c.existsSync(distPath)) {
441
452
  const removedCount = countFilesRecursively(distPath);
442
453
  if (isDebug()) console.log(cleanDistStarting(distPath));
@@ -1305,6 +1316,23 @@ function isCompanionExtension(file) {
1305
1316
  const [first] = toPosix(file).split('/');
1306
1317
  return first === COMPANION_DIR;
1307
1318
  }
1319
+ const DENIED_SEGMENTS = new Set([
1320
+ '.git',
1321
+ '.extension-js',
1322
+ 'node_modules'
1323
+ ]);
1324
+ const SESSION_ARTIFACTS_PREFIX = 'dist/extension-js';
1325
+ function isDeniedEnvFile(basename) {
1326
+ if (!basename.startsWith('.env')) return false;
1327
+ return !basename.endsWith('.example');
1328
+ }
1329
+ function isDeniedFromSourceZip(file) {
1330
+ const posix = toPosix(file);
1331
+ const segments = posix.split('/');
1332
+ if (segments.some((segment)=>DENIED_SEGMENTS.has(segment))) return true;
1333
+ if (isDeniedEnvFile(segments[segments.length - 1])) return true;
1334
+ return posix === SESSION_ARTIFACTS_PREFIX || posix.startsWith(`${SESSION_ARTIFACTS_PREFIX}/`);
1335
+ }
1308
1336
  async function getFilesToZip(projectDir) {
1309
1337
  const gitignorePath = __rspack_external_node_path_c5b9b54f.join(projectDir, '.gitignore');
1310
1338
  const ig = ignore();
@@ -1314,9 +1342,11 @@ async function getFilesToZip(projectDir) {
1314
1342
  } catch {}
1315
1343
  const files = await tiny_glob('**/*', {
1316
1344
  cwd: projectDir,
1317
- dot: true
1345
+ dot: true,
1346
+ filesOnly: true,
1347
+ flush: true
1318
1348
  });
1319
- return files.filter((file)=>!ig.ignores(file) && !isCompanionExtension(file));
1349
+ return files.filter((file)=>!isDeniedFromSourceZip(file) && !ig.ignores(file) && !isCompanionExtension(file));
1320
1350
  }
1321
1351
  class ZipPlugin {
1322
1352
  options;
@@ -2928,7 +2958,7 @@ const cloneParsedHtmlAsset = (assets)=>({
2928
2958
  ]
2929
2959
  });
2930
2960
  const assetsFromHtmlCache = new Map();
2931
- function getAssetsFromHtml(htmlFilePath, htmlContent, publicPath = 'public') {
2961
+ function getAssetsFromHtml(htmlFilePath, htmlContent) {
2932
2962
  const assets = {
2933
2963
  css: [],
2934
2964
  js: [],
@@ -2939,7 +2969,7 @@ function getAssetsFromHtml(htmlFilePath, htmlContent, publicPath = 'public') {
2939
2969
  let cacheKey;
2940
2970
  if (void 0 === htmlContent) try {
2941
2971
  const stat = __rspack_external_node_fs_5ea92f0c.statSync(htmlFilePath);
2942
- cacheKey = `${stat.mtimeMs}:${stat.size}:${publicPath}`;
2972
+ cacheKey = `${stat.mtimeMs}:${stat.size}`;
2943
2973
  const cached = assetsFromHtmlCache.get(htmlFilePath);
2944
2974
  if (cached && cached.key === cacheKey) return cloneParsedHtmlAsset(cached.assets);
2945
2975
  } catch {
@@ -2983,6 +3013,11 @@ function getAssetsFromHtml(htmlFilePath, htmlContent, publicPath = 'public') {
2983
3013
  }
2984
3014
  });
2985
3015
  } catch (error) {
3016
+ const code = error?.code;
3017
+ if (void 0 === htmlContent && 'ENOENT' === code) {
3018
+ assetsFromHtmlCache.delete(htmlFilePath);
3019
+ throw error;
3020
+ }
2986
3021
  return assets;
2987
3022
  }
2988
3023
  if (cacheKey) assetsFromHtmlCache.set(htmlFilePath, {
@@ -2995,6 +3030,7 @@ function getHtmlPageDeclaredAssetPath(filepathList, filePath, extension) {
2995
3030
  const entryname = Object.keys(filepathList).find((key)=>{
2996
3031
  const includePath = filepathList[key];
2997
3032
  if (includePath === filePath) return true;
3033
+ if (!includePath || !__rspack_external_node_fs_5ea92f0c.existsSync(includePath)) return false;
2998
3034
  const assets = getAssetsFromHtml(includePath);
2999
3035
  return Boolean(assets?.js?.includes(filePath) || assets?.css?.includes(filePath));
3000
3036
  }) || '';
@@ -3872,6 +3908,7 @@ class PerfBudgetsPlugin {
3872
3908
  });
3873
3909
  }
3874
3910
  }
3911
+ const CONTROL_PORT_ASSET_NAME = 'extension-js-control.json';
3875
3912
  const BRIDGE_PRODUCER_SOURCE = `;(function () {
3876
3913
  try {
3877
3914
  var g = (typeof globalThis === "object" && globalThis) ? globalThis : this;
@@ -3913,12 +3950,24 @@ const BRIDGE_PRODUCER_SOURCE = `;(function () {
3913
3950
  } catch (e) { /* non-fatal: trigger falls back to its no-listener reply */ }
3914
3951
  }
3915
3952
 
3916
- // Use g.chrome (not the hoisted chrome var below). This runs first.
3953
+ // Use the raw globals (not the hoisted chrome var below). This runs first.
3954
+ // On Gecko browser.* is a DISTINCT wrapper object: hook BOTH namespaces.
3917
3955
  var actionClickedListeners = [];
3918
3956
  var commandListeners = [];
3919
- if (g.chrome) {
3920
- captureEvent(g.chrome.action && g.chrome.action.onClicked, actionClickedListeners);
3921
- captureEvent(g.chrome.commands && g.chrome.commands.onCommand, commandListeners);
3957
+ var capturedEvents = [];
3958
+ function captureEventOnce(event, sink) {
3959
+ // Polyfilled extensions expose ONE event object on both namespaces:
3960
+ // wrap it once, and the sink dedupes callbacks shared across wrappers.
3961
+ if (!event || capturedEvents.indexOf(event) !== -1) return;
3962
+ capturedEvents.push(event);
3963
+ captureEvent(event, sink);
3964
+ }
3965
+ var captureNamespaces = [g.chrome, g.browser];
3966
+ for (var cn = 0; cn < captureNamespaces.length; cn++) {
3967
+ var capNS = captureNamespaces[cn];
3968
+ if (!capNS) continue;
3969
+ captureEventOnce(capNS.action && capNS.action.onClicked, actionClickedListeners);
3970
+ captureEventOnce(capNS.commands && capNS.commands.onCommand, commandListeners);
3922
3971
  }
3923
3972
 
3924
3973
  var LEVELS = ["log", "info", "warn", "error", "debug", "trace"];
@@ -3927,6 +3976,7 @@ const BRIDGE_PRODUCER_SOURCE = `;(function () {
3927
3976
  var queue = [];
3928
3977
  var backoff = 250;
3929
3978
  var MAX_BACKOFF = 5000;
3979
+ var connectFailures = 0;
3930
3980
  var MAX_QUEUE = 1000;
3931
3981
  var MAX_RESULT_BYTES = 256 * 1024;
3932
3982
 
@@ -4695,10 +4745,36 @@ const BRIDGE_PRODUCER_SOURCE = `;(function () {
4695
4745
  if (queue.length < MAX_QUEUE) queue.push(frame);
4696
4746
  }
4697
4747
 
4748
+ // The baked PORT is a snapshot of an ephemeral port. After a dev-server
4749
+ // restart, the port file shipped in the unpacked extension names the
4750
+ // live one, and unpacked resources are read from disk on every fetch.
4751
+ function refreshControlPort(done) {
4752
+ try {
4753
+ var runtime = (g.chrome && g.chrome.runtime) || (g.browser && g.browser.runtime);
4754
+ if (!runtime || typeof runtime.getURL !== "function" || typeof g.fetch !== "function") { done(); return; }
4755
+ g.fetch(runtime.getURL("extension-js-control.json"), {cache: "no-store"})
4756
+ .then(function (res) { return res.json(); })
4757
+ .then(function (data) {
4758
+ var next = data && parseInt(data.port, 10);
4759
+ if (next && next > 0 && next < 65536 && next !== PORT) PORT = next;
4760
+ done();
4761
+ })
4762
+ .catch(function () { done(); });
4763
+ } catch (e) { done(); }
4764
+ }
4765
+
4698
4766
  function schedule() {
4699
4767
  var delay = backoff;
4700
4768
  backoff = Math.min(backoff * 2, MAX_BACKOFF);
4701
- try { setTimeout(connect, delay); } catch (e) {
4769
+ connectFailures++;
4770
+ try {
4771
+ setTimeout(function () {
4772
+ // Two straight failures make the baked port suspect (a restarted
4773
+ // server dials out on a new port): re-resolve before re-dialing.
4774
+ if (connectFailures >= 2) refreshControlPort(connect);
4775
+ else connect();
4776
+ }, delay);
4777
+ } catch (e) {
4702
4778
  // Ignore
4703
4779
  }
4704
4780
  }
@@ -4713,6 +4789,7 @@ const BRIDGE_PRODUCER_SOURCE = `;(function () {
4713
4789
  socket.onopen = function () {
4714
4790
  open = true;
4715
4791
  backoff = 250;
4792
+ connectFailures = 0;
4716
4793
  try {
4717
4794
  socket.send(JSON.stringify({type: "hello", v: 1, role: "producer", instanceId: INSTANCE_ID}));
4718
4795
  } catch (e) {
@@ -5171,6 +5248,10 @@ class InjectBridgeProducer {
5171
5248
  name: InjectBridgeProducer.name,
5172
5249
  stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT + 101
5173
5250
  }, ()=>{
5251
+ if (!compilation.getAsset(CONTROL_PORT_ASSET_NAME)) compilation.emitAsset(CONTROL_PORT_ASSET_NAME, new core_sources.RawSource(JSON.stringify({
5252
+ port: controlPort,
5253
+ instanceId
5254
+ })));
5174
5255
  for (const asset of compilation.getAssets()){
5175
5256
  if (!BACKGROUND_ASSET.test(asset.name)) continue;
5176
5257
  const original = asset.source.source().toString();
@@ -7485,31 +7566,29 @@ class StaticAssetsPlugin {
7485
7566
  }
7486
7567
  }
7487
7568
  };
7488
- const hasCustomSvgRule = compiler.options.module.rules.some((thisRule)=>{
7569
+ const isFullCustomRuleFor = (thisRule, sample)=>{
7489
7570
  const rule = thisRule;
7490
- return Boolean(rule && rule.test instanceof RegExp && rule.test.test('.svg') && void 0 !== rule.use);
7491
- });
7571
+ return Boolean(rule && rule.test instanceof RegExp && rule.test.test(sample) && (void 0 !== rule.type || void 0 !== rule.use) && void 0 === rule.resourceQuery);
7572
+ };
7573
+ const scopedQueriesFor = (sample)=>compiler.options.module.rules.map((thisRule)=>thisRule).filter((rule)=>Boolean(rule && rule.test instanceof RegExp && rule.test.test(sample) && (void 0 !== rule.type || void 0 !== rule.use) && rule.resourceQuery instanceof RegExp)).map((rule)=>rule?.resourceQuery);
7574
+ const hasCustomSvgRule = compiler.options.module.rules.some((thisRule)=>isFullCustomRuleFor(thisRule, '.svg'));
7492
7575
  const hasUrlResourceQueryRule = compiler.options.module.rules.some((thisRule)=>{
7493
7576
  const rule = thisRule;
7494
7577
  const resourceQuery = rule?.resourceQuery;
7495
7578
  if (!(resourceQuery instanceof RegExp)) return false;
7496
7579
  return resourceQuery.test('?url');
7497
7580
  });
7498
- const hasCustomFontsRule = compiler.options.module.rules.some((thisRule)=>{
7499
- const rule = thisRule;
7500
- if (!rule || !(rule.test instanceof RegExp)) return false;
7501
- if (!rule.test.test('.woff')) return false;
7502
- return void 0 !== rule.type || void 0 !== rule.use;
7503
- });
7581
+ const hasCustomFontsRule = compiler.options.module.rules.some((thisRule)=>isFullCustomRuleFor(thisRule, '.woff'));
7582
+ const svgScopedQueries = scopedQueriesFor('.svg');
7583
+ const fontsScopedQueries = scopedQueriesFor('.woff');
7504
7584
  const loaders = [
7505
- ...hasUrlResourceQueryRule ? [] : [
7506
- {
7507
- resourceQuery: /(?:^\?|&)url(?:&|=|$)/,
7508
- type: 'asset/resource'
7509
- }
7510
- ],
7511
7585
  ...hasCustomSvgRule ? [] : [
7512
- defaultSvgRule
7586
+ svgScopedQueries.length ? {
7587
+ ...defaultSvgRule,
7588
+ resourceQuery: {
7589
+ not: svgScopedQueries
7590
+ }
7591
+ } : defaultSvgRule
7513
7592
  ],
7514
7593
  {
7515
7594
  test: /\.(png|jpg|jpeg|gif|webp|avif|ico|bmp)$/i,
@@ -7529,7 +7608,12 @@ class StaticAssetsPlugin {
7529
7608
  type: 'asset',
7530
7609
  generator: {
7531
7610
  filename: filenamePattern
7532
- }
7611
+ },
7612
+ ...fontsScopedQueries.length ? {
7613
+ resourceQuery: {
7614
+ not: fontsScopedQueries
7615
+ }
7616
+ } : {}
7533
7617
  }
7534
7618
  ],
7535
7619
  {
@@ -7543,7 +7627,16 @@ class StaticAssetsPlugin {
7543
7627
  maxSize: 2048
7544
7628
  }
7545
7629
  }
7546
- }
7630
+ },
7631
+ ...hasUrlResourceQueryRule ? [] : [
7632
+ {
7633
+ resourceQuery: /(?:^\?|&)url(?:&|=|$)/,
7634
+ type: 'asset/resource',
7635
+ generator: {
7636
+ filename: filenamePattern
7637
+ }
7638
+ }
7639
+ ]
7547
7640
  ];
7548
7641
  compiler.options.module.rules = [
7549
7642
  ...compiler.options.module.rules,
@@ -8347,9 +8440,9 @@ class AddToFileDependencies {
8347
8440
  for (const field of Object.entries(allEntries)){
8348
8441
  const [, resource] = field;
8349
8442
  if (resource) {
8350
- const resourceData = getAssetsFromHtml(resource);
8351
8443
  const fileDependencies = new Set(compilation.fileDependencies);
8352
8444
  if (__rspack_external_node_fs_5ea92f0c.existsSync(resource)) {
8445
+ const resourceData = getAssetsFromHtml(resource);
8353
8446
  const fileResources = [
8354
8447
  resource,
8355
8448
  ...resourceData?.static || []
@@ -8576,8 +8669,9 @@ class ThrowIfRecompileIsNeeded {
8576
8669
  if (changedFile && this.initialHtmlAssets[changedFile]) {
8577
8670
  const isRemoteUrl = (p)=>/^(https?:)?\/\//i.test(p);
8578
8671
  const looksLikePublicRootUrl = (p)=>p.startsWith('/') && !__rspack_external_node_fs_5ea92f0c.existsSync(p);
8579
- const updatedJsEntries = (getAssetsFromHtml(changedFile)?.js || []).filter((p)=>!looksLikePublicRootUrl(p) && !isRemoteUrl(p));
8580
- const updatedCssEntries = (getAssetsFromHtml(changedFile)?.css || []).filter((p)=>!looksLikePublicRootUrl(p) && !isRemoteUrl(p));
8672
+ const updatedAssets = __rspack_external_node_fs_5ea92f0c.existsSync(changedFile) ? getAssetsFromHtml(changedFile) : void 0;
8673
+ const updatedJsEntries = (updatedAssets?.js || []).filter((p)=>!looksLikePublicRootUrl(p) && !isRemoteUrl(p));
8674
+ const updatedCssEntries = (updatedAssets?.css || []).filter((p)=>!looksLikePublicRootUrl(p) && !isRemoteUrl(p));
8581
8675
  const { js, css } = this.initialHtmlAssets[changedFile];
8582
8676
  if (this.hasEntriesChanged(updatedCssEntries, css) || this.hasEntriesChanged(updatedJsEntries, js)) {
8583
8677
  const projectRoot = __rspack_external_node_path_c5b9b54f.dirname(this.manifestPath);
@@ -8985,6 +9079,24 @@ function invalidRulesetStructure(manifestField, file) {
8985
9079
  "Update the file to contain an array of rules."
8986
9080
  ].join('\n');
8987
9081
  }
9082
+ function invalidRulesetRule(manifestField, file, ruleIndex, reason) {
9083
+ return [
9084
+ `The Declarative Net Request ruleset listed in ${pintor.blue(manifestField)} has a rule Chrome rejects at load.`,
9085
+ `${pintor.gray('PATH')} ${pintor.underline(file)}`,
9086
+ `${pintor.gray('RULE')} ${pintor.underline(`index ${ruleIndex}`)}`,
9087
+ `${pintor.gray('REASON')} ${pintor.underline(reason)}`,
9088
+ "Give every rule an integer id of 1 or more, an action with a type, and a condition object."
9089
+ ].join('\n');
9090
+ }
9091
+ function rulesetRuleShapeIssue(manifestField, file, ruleIndex, reason) {
9092
+ return [
9093
+ `A rule in the Declarative Net Request ruleset listed in ${pintor.blue(manifestField)} may not behave as intended.`,
9094
+ `${pintor.gray('PATH')} ${pintor.underline(file)}`,
9095
+ `${pintor.gray('RULE')} ${pintor.underline(`index ${ruleIndex}`)}`,
9096
+ `${pintor.gray('REASON')} ${pintor.underline(reason)}`,
9097
+ `Check the rule against the Declarative Net Request rule schema.\nThe build continues.`
9098
+ ].join('\n');
9099
+ }
8988
9100
  function invalidManagedSchemaStructure(manifestField, file) {
8989
9101
  return [
8990
9102
  `The managed storage schema listed in ${pintor.blue(manifestField)} isn't a schema object.`,
@@ -9005,6 +9117,22 @@ function jsonIncludeSummary(totalFeatures, criticalCount) {
9005
9117
  function isCriticalJsonFeature(feature) {
9006
9118
  return feature.startsWith('declarative_net_request') || 'storage.managed_schema' === feature;
9007
9119
  }
9120
+ function isPlainObject(value) {
9121
+ return 'object' == typeof value && null !== value && !Array.isArray(value);
9122
+ }
9123
+ function getDnrRuleRejection(rule) {
9124
+ if (!isPlainObject(rule)) return 'the rule is not an object';
9125
+ const id = rule.id;
9126
+ if ('number' != typeof id || !Number.isInteger(id) || id < 1) return 'the rule id is not a positive integer';
9127
+ const action = rule.action;
9128
+ if (!isPlainObject(action)) return 'the rule has no action object';
9129
+ if ('string' != typeof action.type || 0 === action.type.length) return 'the rule action has no type';
9130
+ if (!isPlainObject(rule.condition)) return 'the rule has no condition object';
9131
+ }
9132
+ function getDnrRuleSoftIssue(rule) {
9133
+ const priority = rule.priority;
9134
+ if (void 0 !== priority && ('number' != typeof priority || !Number.isInteger(priority) || priority < 1)) return 'the rule priority is not a positive integer';
9135
+ }
9008
9136
  function validateJsonAsset(compilation, feature, filePath, buf) {
9009
9137
  let parsed;
9010
9138
  try {
@@ -9024,6 +9152,23 @@ function validateJsonAsset(compilation, feature, filePath, buf) {
9024
9152
  compilation.errors.push(err);
9025
9153
  return false;
9026
9154
  }
9155
+ for(let index = 0; index < parsed.length; index++){
9156
+ const rejection = getDnrRuleRejection(parsed[index]);
9157
+ if (rejection) {
9158
+ const err = new core_WebpackError(invalidRulesetRule(feature, filePath, index, rejection));
9159
+ err.file = filePath;
9160
+ err.name = 'DNRInvalidRule';
9161
+ compilation.errors.push(err);
9162
+ return false;
9163
+ }
9164
+ const softIssue = getDnrRuleSoftIssue(parsed[index]);
9165
+ if (softIssue) {
9166
+ const warn = new core_WebpackError(rulesetRuleShapeIssue(feature, filePath, index, softIssue));
9167
+ warn.file = filePath;
9168
+ warn.name = 'DNRRuleShapeIssue';
9169
+ compilation.warnings.push(warn);
9170
+ }
9171
+ }
9027
9172
  } else if ('storage.managed_schema' === feature) {
9028
9173
  if (null === parsed || Array.isArray(parsed) || 'object' != typeof parsed) {
9029
9174
  const err = new core_WebpackError(invalidManagedSchemaStructure(feature, filePath));
@@ -9462,6 +9607,12 @@ function invalidThemeValue(field, detail, value) {
9462
9607
  lines.push(`${pintor.red('INVALID VALUE')} ${value}`);
9463
9608
  return lines.join('\n');
9464
9609
  }
9610
+ function themeNotSupportedByBrowser(browser) {
9611
+ const lines = [];
9612
+ lines.push(`${messaging_prefix('warn')} ${pintor.blue(browser)} does not support the ${pintor.yellow('theme')} field.`);
9613
+ lines.push("The field ships unchanged in the manifest and Safari ignores it.");
9614
+ return lines.join('\n');
9615
+ }
9465
9616
  function missingGeckoDataCollectionPermissions() {
9466
9617
  const lines = [];
9467
9618
  lines.push(`${messaging_prefix('warn')} addons.mozilla.org requires ${pintor.blue('browser_specific_settings.gecko.data_collection_permissions')} for new add-ons.`);
@@ -9933,6 +10084,99 @@ class ManifestLegacyWarnings {
9933
10084
  });
9934
10085
  }
9935
10086
  }
10087
+ const EMITTED_ASSET_REF_PATTERN = /assets\/[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*/g;
10088
+ function forEachStringKey(objectOrMap, callback) {
10089
+ if (!objectOrMap) return;
10090
+ if (objectOrMap instanceof Map) {
10091
+ const keys = objectOrMap.keys();
10092
+ for (const key of keys)callback(String(key));
10093
+ } else if ('object' == typeof objectOrMap) {
10094
+ const objectKeys = Object.keys(objectOrMap);
10095
+ for (const key of objectKeys)callback(key);
10096
+ }
10097
+ }
10098
+ function getAssetSource(compilation, filename) {
10099
+ let assetGetFunction;
10100
+ if ('function' == typeof compilation.getAsset) assetGetFunction = compilation.getAsset(filename);
10101
+ let assetViaAssets;
10102
+ if (!assetGetFunction && compilation.assets) assetViaAssets = compilation.assets[filename];
10103
+ const asset = assetGetFunction || assetViaAssets;
10104
+ if (!asset) return '';
10105
+ let src;
10106
+ src = 'function' == typeof asset.source ? asset.source() : asset.source?.source ? asset.source.source() : asset.source;
10107
+ if ('string' == typeof src) return src;
10108
+ return '';
10109
+ }
10110
+ function collectContentScriptEntryImports(compilation, includeList) {
10111
+ const entryImports = {};
10112
+ const contentEntryNames = new Set(Object.keys(includeList || {}).filter((k)=>k.startsWith("content_scripts")));
10113
+ const chunkGraph = compilation.chunkGraph;
10114
+ compilation.entrypoints.forEach((_entry, entryName)=>{
10115
+ if (String(entryName).startsWith("content_scripts/")) contentEntryNames.add(entryName);
10116
+ });
10117
+ compilation.entrypoints.forEach((entry, entryName)=>{
10118
+ if (!contentEntryNames.has(entryName)) return;
10119
+ const collectedFilesSet = new Set();
10120
+ function addFileIfRelevant(file) {
10121
+ if (null == file) return;
10122
+ const fileNameStr = String(file);
10123
+ const isJavaScript = fileNameStr.endsWith('.js');
10124
+ const isSourceMap = fileNameStr.endsWith('.map');
10125
+ if (isJavaScript || isSourceMap) return;
10126
+ collectedFilesSet.add(fileNameStr);
10127
+ }
10128
+ entry.chunks.forEach((chunk)=>{
10129
+ const currentChunk = chunk;
10130
+ const chunkFilesArray = Array.isArray(currentChunk.files) ? currentChunk.files : [];
10131
+ for(let i = 0; i < chunkFilesArray.length; i++)addFileIfRelevant(chunkFilesArray[i]);
10132
+ let chunkAuxFilesArray = [];
10133
+ if (Array.isArray(currentChunk.auxiliaryFiles)) chunkAuxFilesArray = currentChunk.auxiliaryFiles;
10134
+ for(let i = 0; i < chunkAuxFilesArray.length; i++)addFileIfRelevant(chunkAuxFilesArray[i]);
10135
+ const modulesArray = Array.from(chunkGraph.getChunkModulesIterable(chunk));
10136
+ for(let j = 0; j < modulesArray.length; j++){
10137
+ const moduleObj = modulesArray[j];
10138
+ const moduleChunksArray = Array.from(chunkGraph.getModuleChunks(moduleObj));
10139
+ for(let k = 0; k < moduleChunksArray.length; k++){
10140
+ const mk = moduleChunksArray[k];
10141
+ const mkAuxFilesArr = Array.isArray(mk.auxiliaryFiles) ? mk.auxiliaryFiles : [];
10142
+ for(let l = 0; l < mkAuxFilesArr.length; l++)addFileIfRelevant(mkAuxFilesArr[l]);
10143
+ }
10144
+ const moduleWithBuildInfo = moduleObj;
10145
+ const buildInfo = moduleWithBuildInfo.buildInfo;
10146
+ forEachStringKey(buildInfo?.assets, (key)=>{
10147
+ addFileIfRelevant(key);
10148
+ });
10149
+ forEachStringKey(buildInfo?.assetsInfo, (key)=>{
10150
+ addFileIfRelevant(key);
10151
+ });
10152
+ }
10153
+ for(let i = 0; i < chunkFilesArray.length; i++){
10154
+ const chunkFileName = chunkFilesArray[i];
10155
+ if (!String(chunkFileName).endsWith('.js')) continue;
10156
+ const jsSource = getAssetSource(compilation, chunkFileName);
10157
+ if (!jsSource) continue;
10158
+ const matchedStrings = jsSource.match(EMITTED_ASSET_REF_PATTERN) || [];
10159
+ for(let m = 0; m < matchedStrings.length; m++)addFileIfRelevant(matchedStrings[m]);
10160
+ }
10161
+ });
10162
+ const logicalJsAssetName = `${entryName}.js`;
10163
+ const logicalJsAssetSource = getAssetSource(compilation, logicalJsAssetName);
10164
+ if (logicalJsAssetSource) {
10165
+ const matchedStrings = logicalJsAssetSource.match(EMITTED_ASSET_REF_PATTERN) || [];
10166
+ for(let n = 0; n < matchedStrings.length; n++)addFileIfRelevant(matchedStrings[n]);
10167
+ }
10168
+ entryImports[entryName] = Array.from(collectedFilesSet);
10169
+ });
10170
+ const entryImportsEntries = Object.entries(entryImports);
10171
+ for(let i = 0; i < entryImportsEntries.length; i++){
10172
+ const name = entryImportsEntries[i][0];
10173
+ const files = entryImportsEntries[i][1];
10174
+ const normalizedFiles = [];
10175
+ for(let j = 0; j < files.length; j++)normalizedFiles.push(unixify(files[j]));
10176
+ entryImports[name] = normalizedFiles;
10177
+ }
10178
+ return entryImports;
10179
+ }
9936
10180
  function cleanMatches(matches) {
9937
10181
  return matches.map((match)=>{
9938
10182
  try {
@@ -10072,6 +10316,9 @@ function resolve_war_findSourceSibling(absOutputPath) {
10072
10316
  function isFirefox(browser) {
10073
10317
  return !!browser && isGeckoBasedBrowser(browser.toLowerCase());
10074
10318
  }
10319
+ function isWebkit(browser) {
10320
+ return !!browser && isWebkitBasedBrowser(browser.toLowerCase());
10321
+ }
10075
10322
  function isValidChromeMatchPattern(pattern) {
10076
10323
  if ('<all_urls>' === pattern) return true;
10077
10324
  if (/[?#]/.test(pattern)) return false;
@@ -10085,7 +10332,7 @@ function isValidChromeMatchPattern(pattern) {
10085
10332
  }
10086
10333
  }
10087
10334
  function validateMatchesOrReport(compilation, matches, browser) {
10088
- if (!matches || isFirefox(browser)) return;
10335
+ if (!matches || isFirefox(browser) || isWebkit(browser)) return;
10089
10336
  compilation.errors ||= [];
10090
10337
  for (const m of matches)if (!isValidChromeMatchPattern(m)) {
10091
10338
  const msg = warInvalidMatchPattern(m);
@@ -10227,7 +10474,7 @@ function resolveUserDeclaredWAR(compilation, manifestPath, manifest, browser) {
10227
10474
  v3
10228
10475
  };
10229
10476
  }
10230
- function getAssetSource(compilation, filename) {
10477
+ function generate_manifest_getAssetSource(compilation, filename) {
10231
10478
  const byGet = 'function' == typeof compilation.getAsset ? compilation.getAsset(filename) : void 0;
10232
10479
  const byAssets = !byGet && compilation.assets ? compilation.assets[filename] : void 0;
10233
10480
  const asset = byGet || byAssets;
@@ -10318,10 +10565,9 @@ function generateManifestPatches(compilation, manifestPath, entryImports, browse
10318
10565
  const cacheKey = assetForCache && 'object' == typeof assetForCache.source ? assetForCache.source : void 0;
10319
10566
  let filtered = cacheKey ? assetScanCache.get(cacheKey) : void 0;
10320
10567
  if (!filtered) {
10321
- const source = getAssetSource(compilation, jsFile);
10568
+ const source = generate_manifest_getAssetSource(compilation, jsFile);
10322
10569
  if (!source) continue;
10323
- const re = /assets\/[A-Za-z0-9._-]+/g;
10324
- const found = source.match(re) || [];
10570
+ const found = source.match(EMITTED_ASSET_REF_PATTERN) || [];
10325
10571
  filtered = Array.from(new Set(found.filter((r)=>!r.endsWith('.js') && !r.endsWith('.map')))).sort();
10326
10572
  if (cacheKey) assetScanCache.set(cacheKey, filtered);
10327
10573
  }
@@ -10967,7 +11213,9 @@ class ValidateThemeValues {
10967
11213
  this.browser = options.browser || 'chrome';
10968
11214
  }
10969
11215
  apply(compiler) {
10970
- if (!isChromiumBasedBrowser(String(this.browser))) return;
11216
+ const browserName = String(this.browser);
11217
+ const webkitTarget = isWebkitBasedBrowser(browserName);
11218
+ if (!isChromiumBasedBrowser(browserName) && !webkitTarget) return;
10971
11219
  compiler.hooks.thisCompilation.tap(ValidateThemeValues.name, (compilation)=>{
10972
11220
  compilation.hooks.processAssets.tap({
10973
11221
  name: ValidateThemeValues.name,
@@ -10979,6 +11227,10 @@ class ValidateThemeValues {
10979
11227
  } catch {
10980
11228
  return;
10981
11229
  }
11230
+ if (webkitTarget) {
11231
+ if (manifest.theme) reportToCompilation(compilation, compiler, themeNotSupportedByBrowser(browserName), 'warning', 'manifest.json');
11232
+ return;
11233
+ }
10982
11234
  for (const issue of collectThemeValueIssues(manifest))reportToCompilation(compilation, compiler, invalidThemeValue(issue.field, issue.detail, issue.value), 'error', 'manifest.json');
10983
11235
  });
10984
11236
  });
@@ -11436,100 +11688,6 @@ class ScriptsPlugin {
11436
11688
  new ValidateContentScriptSyntax().apply(compiler);
11437
11689
  }
11438
11690
  }
11439
- function forEachStringKey(objectOrMap, callback) {
11440
- if (!objectOrMap) return;
11441
- if (objectOrMap instanceof Map) {
11442
- const keys = objectOrMap.keys();
11443
- for (const key of keys)callback(String(key));
11444
- } else if ('object' == typeof objectOrMap) {
11445
- const objectKeys = Object.keys(objectOrMap);
11446
- for (const key of objectKeys)callback(key);
11447
- }
11448
- }
11449
- function collect_entry_imports_getAssetSource(compilation, filename) {
11450
- let assetGetFunction;
11451
- if ('function' == typeof compilation.getAsset) assetGetFunction = compilation.getAsset(filename);
11452
- let assetViaAssets;
11453
- if (!assetGetFunction && compilation.assets) assetViaAssets = compilation.assets[filename];
11454
- const asset = assetGetFunction || assetViaAssets;
11455
- if (!asset) return '';
11456
- let src;
11457
- src = 'function' == typeof asset.source ? asset.source() : asset.source?.source ? asset.source.source() : asset.source;
11458
- if ('string' == typeof src) return src;
11459
- return '';
11460
- }
11461
- function collectContentScriptEntryImports(compilation, includeList) {
11462
- const entryImports = {};
11463
- const contentEntryNames = new Set(Object.keys(includeList || {}).filter((k)=>k.startsWith("content_scripts")));
11464
- const chunkGraph = compilation.chunkGraph;
11465
- compilation.entrypoints.forEach((_entry, entryName)=>{
11466
- if (String(entryName).startsWith("content_scripts/")) contentEntryNames.add(entryName);
11467
- });
11468
- compilation.entrypoints.forEach((entry, entryName)=>{
11469
- if (!contentEntryNames.has(entryName)) return;
11470
- const collectedFilesSet = new Set();
11471
- function addFileIfRelevant(file) {
11472
- if (null == file) return;
11473
- const fileNameStr = String(file);
11474
- const isJavaScript = fileNameStr.endsWith('.js');
11475
- const isSourceMap = fileNameStr.endsWith('.map');
11476
- if (isJavaScript || isSourceMap) return;
11477
- collectedFilesSet.add(fileNameStr);
11478
- }
11479
- entry.chunks.forEach((chunk)=>{
11480
- const currentChunk = chunk;
11481
- const chunkFilesArray = Array.isArray(currentChunk.files) ? currentChunk.files : [];
11482
- for(let i = 0; i < chunkFilesArray.length; i++)addFileIfRelevant(chunkFilesArray[i]);
11483
- let chunkAuxFilesArray = [];
11484
- if (Array.isArray(currentChunk.auxiliaryFiles)) chunkAuxFilesArray = currentChunk.auxiliaryFiles;
11485
- for(let i = 0; i < chunkAuxFilesArray.length; i++)addFileIfRelevant(chunkAuxFilesArray[i]);
11486
- const modulesArray = Array.from(chunkGraph.getChunkModulesIterable(chunk));
11487
- for(let j = 0; j < modulesArray.length; j++){
11488
- const moduleObj = modulesArray[j];
11489
- const moduleChunksArray = Array.from(chunkGraph.getModuleChunks(moduleObj));
11490
- for(let k = 0; k < moduleChunksArray.length; k++){
11491
- const mk = moduleChunksArray[k];
11492
- const mkAuxFilesArr = Array.isArray(mk.auxiliaryFiles) ? mk.auxiliaryFiles : [];
11493
- for(let l = 0; l < mkAuxFilesArr.length; l++)addFileIfRelevant(mkAuxFilesArr[l]);
11494
- }
11495
- const moduleWithBuildInfo = moduleObj;
11496
- const buildInfo = moduleWithBuildInfo.buildInfo;
11497
- forEachStringKey(buildInfo?.assets, (key)=>{
11498
- addFileIfRelevant(key);
11499
- });
11500
- forEachStringKey(buildInfo?.assetsInfo, (key)=>{
11501
- addFileIfRelevant(key);
11502
- });
11503
- }
11504
- for(let i = 0; i < chunkFilesArray.length; i++){
11505
- const chunkFileName = chunkFilesArray[i];
11506
- if (!String(chunkFileName).endsWith('.js')) continue;
11507
- const jsSource = collect_entry_imports_getAssetSource(compilation, chunkFileName);
11508
- if (!jsSource) continue;
11509
- const assetPattern = /assets\/[A-Za-z0-9._-]+/g;
11510
- const matchedStrings = jsSource.match(assetPattern) || [];
11511
- for(let m = 0; m < matchedStrings.length; m++)addFileIfRelevant(matchedStrings[m]);
11512
- }
11513
- });
11514
- const logicalJsAssetName = `${entryName}.js`;
11515
- const logicalJsAssetSource = collect_entry_imports_getAssetSource(compilation, logicalJsAssetName);
11516
- if (logicalJsAssetSource) {
11517
- const assetPattern = /assets\/[A-Za-z0-9._-]+/g;
11518
- const matchedStrings = logicalJsAssetSource.match(assetPattern) || [];
11519
- for(let n = 0; n < matchedStrings.length; n++)addFileIfRelevant(matchedStrings[n]);
11520
- }
11521
- entryImports[entryName] = Array.from(collectedFilesSet);
11522
- });
11523
- const entryImportsEntries = Object.entries(entryImports);
11524
- for(let i = 0; i < entryImportsEntries.length; i++){
11525
- const name = entryImportsEntries[i][0];
11526
- const files = entryImportsEntries[i][1];
11527
- const normalizedFiles = [];
11528
- for(let j = 0; j < files.length; j++)normalizedFiles.push(unixify(files[j]));
11529
- entryImports[name] = normalizedFiles;
11530
- }
11531
- return entryImports;
11532
- }
11533
11691
  class CollectContentEntryImports {
11534
11692
  includeList;
11535
11693
  constructor(options){
@@ -11800,6 +11958,7 @@ function webpackConfig(projectStructure, devOptions) {
11800
11958
  }
11801
11959
  const manifest = filterKeysForThisBrowser(rawManifest, devOptions.browser);
11802
11960
  const primaryExtensionOutputDir = asAbsolute(__rspack_external_node_path_c5b9b54f.isAbsolute(devOptions.output.path) ? devOptions.output.path : __rspack_external_node_path_c5b9b54f.resolve(packageJsonDir, devOptions.output.path));
11961
+ const publishedExtensionOutputDir = devOptions.output.finalPath ? asAbsolute(__rspack_external_node_path_c5b9b54f.isAbsolute(devOptions.output.finalPath) ? devOptions.output.finalPath : __rspack_external_node_path_c5b9b54f.resolve(packageJsonDir, devOptions.output.finalPath)) : primaryExtensionOutputDir;
11803
11962
  const companionUnpackedExtensionDirs = resolveCompanionExtensionDirs({
11804
11963
  projectRoot: packageJsonDir,
11805
11964
  config: devOptions.extensions
@@ -11926,7 +12085,7 @@ function webpackConfig(projectStructure, devOptions) {
11926
12085
  browser: devOptions.browser,
11927
12086
  mode: devOptions.mode,
11928
12087
  command: devOptions.metadataCommand,
11929
- outputPath: primaryExtensionOutputDir,
12088
+ outputPath: publishedExtensionOutputDir,
11930
12089
  manifestPath,
11931
12090
  port: devOptions.port,
11932
12091
  host: process.env.EXTENSION_DEV_SERVER_CONNECTABLE_HOST || devOptions.host,