extension-develop 4.0.19 → 4.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/0~dev-server.mjs +8 -3
  2. package/dist/0~rspack-config.mjs +3759 -205
  3. package/dist/101.mjs +65 -2
  4. package/dist/349.mjs +2 -0
  5. package/dist/494.mjs +7 -1
  6. package/dist/839.mjs +164 -3619
  7. package/dist/887.mjs +5 -1
  8. package/dist/bridge-entry.d.ts +5 -4
  9. package/dist/bridge.mjs +25 -5
  10. package/dist/contract/codes.json +19 -2
  11. package/dist/contract/envelope.schema.json +10 -1
  12. package/dist/contract/golden.eval.target-not-found.json +14 -0
  13. package/dist/contract/golden.open.headed-window-required.json +14 -0
  14. package/dist/contracts-entry.d.ts +2 -0
  15. package/dist/contracts.mjs +12 -0
  16. package/dist/dev-server/control-bridge/consumer-client.d.ts +15 -1
  17. package/dist/dev-server/control-bridge/contracts.d.ts +6 -0
  18. package/dist/dev-server/control-bridge/producer-runtime.d.ts +1 -1
  19. package/dist/ensure-hmr-for-scripts.js +1 -1
  20. package/dist/ensure-hmr-for-scripts.mjs +1 -1
  21. package/dist/feature-scripts-content-script-wrapper.js +2 -2
  22. package/dist/feature-scripts-content-script-wrapper.mjs +2 -2
  23. package/dist/lib/extension-id.d.ts +9 -0
  24. package/dist/lib/messaging.d.ts +9 -0
  25. package/dist/lib/session-paths.d.ts +2 -0
  26. package/dist/plugin-playwright/index.d.ts +7 -0
  27. package/dist/plugin-web-extension/feature-icons/messages.d.ts +3 -0
  28. package/dist/plugin-web-extension/feature-manifest/manifest-lib/theme-values.d.ts +7 -0
  29. package/dist/plugin-web-extension/feature-manifest/messages.d.ts +1 -0
  30. package/dist/plugin-web-extension/feature-manifest/steps/validate-theme-values.d.ts +9 -0
  31. package/package.json +10 -1
@@ -2,25 +2,25 @@ import { createRequire as __extjsCreateRequire } from "node:module"; const requi
2
2
  import { __webpack_require__ } from "./0~rslib-runtime.mjs";
3
3
  import { createRequire } from "node:module";
4
4
  import pintor from "pintor";
5
- import core, { Compilation, DefinePlugin, ProvidePlugin, WebpackError, rspack, sources as core_sources } from "@rspack/core";
5
+ import core, { Compilation as core_Compilation, DefinePlugin, ProvidePlugin, WebpackError as core_WebpackError, rspack, sources as core_sources } from "@rspack/core";
6
6
  import case_sensitive_paths_webpack_plugin from "case-sensitive-paths-webpack-plugin";
7
7
  import adm_zip from "adm-zip";
8
8
  import ignore from "ignore";
9
9
  import tiny_glob from "tiny-glob";
10
10
  import { pathToFileURL } from "node:url";
11
- import { filterKeysForThisBrowser, getManifestFieldsData } from "browser-extension-manifest-fields";
11
+ import { filterKeysForThisBrowser as external_browser_extension_manifest_fields_filterKeysForThisBrowser, getManifestFieldsData } from "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";
16
+ import { stripBom, parseJsonSafe } from "./23.mjs";
15
17
  import { isResourceUnderDirs, canonicalizeDir, toResourceKey } from "./93.mjs";
16
18
  import { prefix as messaging_prefix, isDebug } from "./349.mjs";
17
- import { debugExtensionsToLoad, package_namespaceObject, getDirs, asAbsolute, browserRunnerDisabled, treeWithDistFilesBrowser, packagingDistributionFiles, toPosixPath, PlaywrightPlugin, debugContextPath, resolveCompanionExtensionDirs, 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";
18
- import { unixify as paths_unixify, getManifestOverrides, isUsingTypeScript, resolvePackageManager, jsFrameworksIntegrationsEnabled, resolveRootAbsoluteRef, isStaticThemeSource, buildCanonicalManifest, getCurrentManifestContent, ensureTypeScriptConfig, resolveDevelopInstallRoot, filterKeysForThisBrowser as manifest_utils_filterKeysForThisBrowser, isUsingCustomLoader, getUserTypeScriptConfigFile, isUsingIntegration as messages_isUsingIntegration, reportToCompilation, normalizeManifestOutputPath, jsFrameworksHmrSummary, ReloadPlugin, humanLine, jsFrameworksConfigsDetected, collectRootAbsoluteRefs, getManifestContent, resolveDevelopDistFile, hasDependency, setOriginalManifestContent, isFromFilepathList, setCurrentManifestContent } from "./839.mjs";
19
+ import { jsFrameworksHmrSummary, isUsingTypeScript, resolvePackageManager, humanLine, jsFrameworksIntegrationsEnabled, jsFrameworksConfigsDetected, ensureTypeScriptConfig, hasDependency, resolveDevelopInstallRoot, resolveDevelopDistFile, isUsingCustomLoader, getUserTypeScriptConfigFile, isUsingIntegration as messages_isUsingIntegration } from "./839.mjs";
19
20
  import { scrubBrand, makeSanitizedConsole } from "./0~branding.mjs";
20
- import { stripBom, parseJsonSafe } from "./23.mjs";
21
+ import { getCanonicalContentScriptEntryName, parseCanonicalContentScriptAsset, EXTENSIONJS_CONTENT_SCRIPT_LAYER, isCanonicalContentScriptAsset, getCanonicalContentScriptJsAssetName, getCanonicalContentScriptCssAssetName } from "./291.mjs";
21
22
  import { hasProjectDependency, findNearestProjectManifestSync, PROJECT_MANIFEST_FILENAMES, readProjectDependencies } from "./80.mjs";
22
23
  import { cssIntegrationsEnabled, cssConfigsDetected, deadCssUrlRef, missingSassDependency, isUsingIntegration, postCssPluginNotResolved } from "./845.mjs";
23
- import { getCanonicalContentScriptEntryName, parseCanonicalContentScriptAsset, getCanonicalContentScriptJsAssetName, isCanonicalContentScriptAsset, EXTENSIONJS_CONTENT_SCRIPT_LAYER } from "./291.mjs";
24
24
  import { injectedFileDependencyMissing, importScriptsDependencyMissing, getURLDependencyMissing, staticImportDependencyMissing, fetchedFileDependencyMissing, runtimeSetSurfaceDependencyMissing } from "./266.mjs";
25
25
  import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
26
26
  import { dirname as __rspack_dirname } from "node:path";
@@ -37,6 +37,85 @@ __webpack_require__.r(rspack_config_namespaceObject);
37
37
  __webpack_require__.d(rspack_config_namespaceObject, {
38
38
  default: ()=>webpackConfig
39
39
  });
40
+ function filterKeysForThisBrowser(manifest, browser) {
41
+ const isSafariTarget = 'safari' === browser || 'webkit-based' === browser || String(browser).includes('webkit');
42
+ const isChromiumTarget = isChromiumBasedBrowser(String(browser)) || isSafariTarget;
43
+ const isGeckoTarget = isGeckoBasedBrowser(String(browser));
44
+ const chromiumPrefixes = new Set([
45
+ 'chromium',
46
+ 'chrome',
47
+ 'edge'
48
+ ]);
49
+ const geckoPrefixes = new Set([
50
+ 'gecko',
51
+ 'firefox'
52
+ ]);
53
+ const webkitPrefixes = new Set([
54
+ 'safari',
55
+ 'webkit'
56
+ ]);
57
+ const isFamilyPrefix = (prefix)=>isChromiumTarget && chromiumPrefixes.has(prefix) || isGeckoTarget && geckoPrefixes.has(prefix);
58
+ const isSpecificPrefix = (prefix)=>prefix === browser || isSafariTarget && webkitPrefixes.has(prefix);
59
+ const resolve = (node)=>{
60
+ if (Array.isArray(node)) return node.map((item)=>resolve(item));
61
+ if (node && 'object' == typeof node) {
62
+ const result = {};
63
+ const familyMatches = {};
64
+ const specificMatches = {};
65
+ for (const [key, value] of Object.entries(node)){
66
+ const indexOfColon = key.indexOf(':');
67
+ if (-1 === indexOfColon) {
68
+ result[key] = resolve(value);
69
+ continue;
70
+ }
71
+ const prefix = key.substring(0, indexOfColon);
72
+ const strippedKey = key.substring(indexOfColon + 1);
73
+ if (isSpecificPrefix(prefix)) specificMatches[strippedKey] = resolve(value);
74
+ else if (isFamilyPrefix(prefix)) familyMatches[strippedKey] = resolve(value);
75
+ }
76
+ for (const [strippedKey, value] of Object.entries(familyMatches))result[strippedKey] = value;
77
+ for (const [strippedKey, value] of Object.entries(specificMatches))result[strippedKey] = value;
78
+ return result;
79
+ }
80
+ return node;
81
+ };
82
+ return resolve(manifest);
83
+ }
84
+ const THEME_DISQUALIFYING_KEYS = [
85
+ 'background',
86
+ "content_scripts",
87
+ 'action',
88
+ 'browser_action',
89
+ 'page_action',
90
+ 'sidebar_action',
91
+ 'side_panel',
92
+ 'options_page',
93
+ 'options_ui',
94
+ 'devtools_page',
95
+ 'chrome_url_overrides',
96
+ 'sandbox',
97
+ "user_scripts",
98
+ 'declarative_net_request',
99
+ 'web_accessible_resources'
100
+ ];
101
+ function isStaticTheme(manifest) {
102
+ if (!manifest || 'object' != typeof manifest) return false;
103
+ const theme = manifest.theme;
104
+ if (!theme || 'object' != typeof theme) return false;
105
+ return !THEME_DISQUALIFYING_KEYS.some((key)=>{
106
+ const value = manifest[key];
107
+ return null != value;
108
+ });
109
+ }
110
+ function isStaticThemeSource(manifestPath, browser) {
111
+ if (!manifestPath) return false;
112
+ try {
113
+ const parsed = parseJsonSafe(__rspack_external_node_fs_5ea92f0c.readFileSync(manifestPath, 'utf-8'));
114
+ return isStaticTheme(filterKeysForThisBrowser(parsed, browser));
115
+ } catch {
116
+ return false;
117
+ }
118
+ }
40
119
  function normalizePath(filePath) {
41
120
  return filePath.replace(/\\/g, '/');
42
121
  }
@@ -436,6 +515,642 @@ function countFilesRecursively(dir) {
436
515
  return 0;
437
516
  }
438
517
  }
518
+ function isFromFilepathList(filePath, filepathList) {
519
+ return Object.values(filepathList || {}).some((value)=>value === filePath);
520
+ }
521
+ function getFilename(feature, filePath) {
522
+ const entryExt = __rspack_external_node_path_c5b9b54f.extname(filePath);
523
+ let fileOutputpath = feature;
524
+ if ([
525
+ '.js',
526
+ '.jsx',
527
+ '.tsx',
528
+ '.ts',
529
+ '.vue',
530
+ '.svelte'
531
+ ].includes(entryExt)) fileOutputpath = fileOutputpath.replace(entryExt, '.js');
532
+ if ([
533
+ '.html',
534
+ '.njk',
535
+ '.nunjucks'
536
+ ].includes(entryExt)) fileOutputpath = fileOutputpath.replace(entryExt, '.html');
537
+ if ([
538
+ '.css',
539
+ '.scss',
540
+ '.sass',
541
+ '.less'
542
+ ].includes(entryExt)) fileOutputpath = fileOutputpath.replace(entryExt, '.css');
543
+ return unixify(fileOutputpath || '');
544
+ }
545
+ function unixify(filePath) {
546
+ return filePath.replace(/\\/g, '/');
547
+ }
548
+ function resolveRootAbsoluteRef(ref, projectRoot, publicRoot) {
549
+ if (!ref || !ref.startsWith('/')) return;
550
+ if (projectRoot && ref.startsWith(projectRoot)) return;
551
+ if (ref.startsWith('//')) return;
552
+ const trimmed = ref.replace(/^\/+/, '');
553
+ if (!trimmed) return;
554
+ if (publicRoot && __rspack_external_node_fs_5ea92f0c.existsSync(__rspack_external_node_path_c5b9b54f.join(publicRoot, trimmed))) return;
555
+ const candidate = __rspack_external_node_path_c5b9b54f.resolve(projectRoot, trimmed);
556
+ const rel = __rspack_external_node_path_c5b9b54f.relative(projectRoot, candidate);
557
+ if (rel.startsWith('..') || __rspack_external_node_path_c5b9b54f.isAbsolute(rel)) return;
558
+ if (!__rspack_external_node_fs_5ea92f0c.existsSync(candidate) || !__rspack_external_node_fs_5ea92f0c.statSync(candidate).isFile()) return;
559
+ return candidate;
560
+ }
561
+ function collectRootAbsoluteRefs(source) {
562
+ const refs = new Set();
563
+ const attrRe = /(?:src|href)\s*=\s*["'](\/[^"'#?]*)["']/gi;
564
+ const urlRe = /url\(\s*["']?(\/[^"')#?]+)["']?\s*\)/gi;
565
+ let m;
566
+ while(m = attrRe.exec(source))refs.add(m[1]);
567
+ while(m = urlRe.exec(source))refs.add(m[1]);
568
+ return refs;
569
+ }
570
+ function backgroundPage(manifest) {
571
+ return manifest.background?.page && {
572
+ background: {
573
+ page: (()=>{
574
+ const raw = String(manifest.background.page);
575
+ return getFilename('background/index.html', raw);
576
+ })()
577
+ }
578
+ };
579
+ }
580
+ function normalizeManifestOutputPath(originalPath) {
581
+ if (!originalPath) return originalPath;
582
+ const unixPath = originalPath.replace(/\\/g, '/');
583
+ if (/^\/public\//i.test(unixPath)) return unixPath.replace(/^\/public\//i, '');
584
+ if (/^(?:\.\/)?public\//i.test(unixPath)) return unixPath.replace(/^(?:\.\/)?public\//i, '');
585
+ if (/^\//.test(unixPath)) return unixPath.replace(/^\//, '');
586
+ return unixPath;
587
+ }
588
+ function manifestPageOutputTarget(raw, compiledTarget, manifestPath) {
589
+ const unixPath = raw.replace(/\\/g, '/');
590
+ if (/^(?:\/public\/|(?:\.\/)?public\/)/i.test(unixPath)) return normalizeManifestOutputPath(unixPath);
591
+ if (/^\//.test(unixPath)) {
592
+ const rest = unixPath.replace(/^\/+/, '');
593
+ if (manifestPath && rest) {
594
+ const manifestDir = __rspack_external_node_path_c5b9b54f.dirname(manifestPath);
595
+ const inPublic = __rspack_external_node_fs_5ea92f0c.existsSync(__rspack_external_node_path_c5b9b54f.join(manifestDir, 'public', rest));
596
+ const inRoot = __rspack_external_node_fs_5ea92f0c.existsSync(__rspack_external_node_path_c5b9b54f.join(manifestDir, rest));
597
+ if (inRoot && !inPublic) return compiledTarget;
598
+ }
599
+ return normalizeManifestOutputPath(unixPath);
600
+ }
601
+ return compiledTarget;
602
+ }
603
+ function iconOutputPath(raw) {
604
+ if (/^(?:\/public\/|(?:\.\/)?public\/)/i.test(raw)) return normalizeManifestOutputPath(raw);
605
+ const normalized = normalizeManifestOutputPath(raw).replace(/^\.\//, '');
606
+ const escapes = !normalized || normalized.split('/').includes('..') || /^[A-Za-z]:/.test(normalized);
607
+ return escapes ? `icons/${normalized.split('/').pop() || ''}` : normalized;
608
+ }
609
+ function chromeUrlOverrides(manifest, manifestPath) {
610
+ return manifest.chrome_url_overrides && {
611
+ chrome_url_overrides: {
612
+ ...manifest.chrome_url_overrides.bookmarks && {
613
+ bookmarks: (()=>{
614
+ const raw = String(manifest.chrome_url_overrides.bookmarks);
615
+ return getFilename(manifestPageOutputTarget(raw, 'chrome_url_overrides/bookmarks.html', manifestPath), raw);
616
+ })()
617
+ },
618
+ ...manifest.chrome_url_overrides.history && {
619
+ history: (()=>{
620
+ const raw = String(manifest.chrome_url_overrides.history);
621
+ return getFilename(manifestPageOutputTarget(raw, 'chrome_url_overrides/history.html', manifestPath), raw);
622
+ })()
623
+ },
624
+ ...manifest.chrome_url_overrides.newtab && {
625
+ newtab: (()=>{
626
+ const raw = String(manifest.chrome_url_overrides.newtab);
627
+ return getFilename(manifestPageOutputTarget(raw, 'chrome_url_overrides/newtab.html', manifestPath), raw);
628
+ })()
629
+ }
630
+ }
631
+ };
632
+ }
633
+ function commands_commands(manifest) {
634
+ return manifest.commands && {
635
+ commands: manifest.commands
636
+ };
637
+ }
638
+ function isBundledContentPath(filePath, ext) {
639
+ const normalized = String(filePath || '').replace(/\\/g, '/');
640
+ const bundledAsset = parseCanonicalContentScriptAsset(normalized);
641
+ return bundledAsset?.extension === ext;
642
+ }
643
+ function isAlreadyBundledContentScripts(contentScripts) {
644
+ if (!Array.isArray(contentScripts) || 0 === contentScripts.length) return false;
645
+ return contentScripts.every((contentObj)=>{
646
+ const js = Array.isArray(contentObj?.js) ? contentObj.js : [];
647
+ const css = Array.isArray(contentObj?.css) ? contentObj.css : [];
648
+ return js.every((filePath)=>isBundledContentPath(filePath, 'js')) && css.every((filePath)=>isBundledContentPath(filePath, 'css'));
649
+ });
650
+ }
651
+ function content_scripts_contentScripts(manifest, manifestPath) {
652
+ if (!manifest.content_scripts) return;
653
+ const original = manifest.content_scripts;
654
+ if (isAlreadyBundledContentScripts(original)) return {
655
+ content_scripts: original
656
+ };
657
+ const originalCount = original.length;
658
+ const result = [];
659
+ let bridgeOrdinal = 0;
660
+ for(let index = 0; index < original.length; index++){
661
+ const contentObj = original[index] || {};
662
+ const contentJs = [
663
+ ...new Set(contentObj.js || [])
664
+ ];
665
+ const contentCss = [
666
+ ...new Set(contentObj.css || [])
667
+ ];
668
+ if ('MAIN' === contentObj.world) {
669
+ const bridgeIndex = originalCount + bridgeOrdinal++;
670
+ const { world: _ignoredWorld, js: _ignoredJs, css: _ignoredCss, ...rest } = contentObj;
671
+ result.push({
672
+ ...rest,
673
+ js: [
674
+ getFilename(getCanonicalContentScriptJsAssetName(bridgeIndex), 'main-world-bridge.js')
675
+ ],
676
+ css: []
677
+ });
678
+ }
679
+ result.push({
680
+ ...original[index] || {},
681
+ js: [
682
+ ...new Set(contentJs.map((js)=>getFilename(getCanonicalContentScriptJsAssetName(index), js)))
683
+ ],
684
+ css: [
685
+ ...new Set(contentCss.map((css)=>getFilename(getCanonicalContentScriptCssAssetName(index), css)))
686
+ ]
687
+ });
688
+ }
689
+ return {
690
+ content_scripts: result
691
+ };
692
+ }
693
+ function contentSecurityPolicy(manifest) {
694
+ return manifest.content_security_policy && {
695
+ content_security_policy: manifest.content_security_policy
696
+ };
697
+ }
698
+ function devtoolsPage(manifest, manifestPath) {
699
+ return manifest.devtools_page && {
700
+ devtools_page: (()=>{
701
+ const raw = String(manifest.devtools_page);
702
+ return getFilename(manifestPageOutputTarget(raw, 'devtools/index.html', manifestPath), raw);
703
+ })()
704
+ };
705
+ }
706
+ function icons_icons(manifest) {
707
+ return manifest.icons && {
708
+ icons: Object.fromEntries(Object.entries(manifest.icons).map(([size, icon])=>{
709
+ const raw = String(icon);
710
+ return [
711
+ size,
712
+ getFilename(iconOutputPath(raw), raw)
713
+ ];
714
+ }))
715
+ };
716
+ }
717
+ const getBasename = (filepath)=>__rspack_external_node_path_c5b9b54f.basename(filepath);
718
+ function omnibox_omnibox(manifest) {
719
+ const omnibox = manifest.omnibox;
720
+ return omnibox && {
721
+ omnibox: {
722
+ ...omnibox,
723
+ ...omnibox.default_icon && {
724
+ default_icon: 'string' == typeof omnibox.default_icon ? getFilename(`icons/${getBasename(omnibox.default_icon)}`, omnibox.default_icon) : Object.fromEntries(Object.entries(omnibox.default_icon).map(([size, icon])=>[
725
+ size,
726
+ getFilename(`icons/${getBasename(icon)}`, icon)
727
+ ]))
728
+ }
729
+ }
730
+ };
731
+ }
732
+ function optionsPage(manifest, manifestPath) {
733
+ return manifest.options_page && {
734
+ options_page: (()=>{
735
+ const raw = String(manifest.options_page);
736
+ return getFilename(manifestPageOutputTarget(raw, 'options/index.html', manifestPath), raw);
737
+ })()
738
+ };
739
+ }
740
+ function optionsUi(manifest, manifestPath) {
741
+ return manifest.options_ui && {
742
+ options_ui: {
743
+ ...manifest.options_ui,
744
+ ...manifest.options_ui.page && {
745
+ page: (()=>{
746
+ const raw = String(manifest.options_ui.page);
747
+ return getFilename(manifestPageOutputTarget(raw, 'options/index.html', manifestPath), raw);
748
+ })()
749
+ }
750
+ }
751
+ };
752
+ }
753
+ function permissions(manifest) {
754
+ return manifest.permissions && {
755
+ permissions: manifest.permissions
756
+ };
757
+ }
758
+ function sandbox(manifest) {
759
+ return manifest.sandbox && {
760
+ sandbox: {
761
+ ...manifest.sandbox,
762
+ ...manifest.sandbox.pages && {
763
+ pages: manifest.sandbox.pages.map((page, index)=>getFilename(`sandbox/page-${index}.html`, page))
764
+ }
765
+ }
766
+ };
767
+ }
768
+ function storage(manifest) {
769
+ return manifest.storage && {
770
+ storage: {
771
+ ...manifest.storage.managed_schema && {
772
+ managed_schema: getFilename('storage/managed_schema.json', manifest.storage.managed_schema)
773
+ }
774
+ }
775
+ };
776
+ }
777
+ const theme_getBasename = (filepath)=>__rspack_external_node_path_c5b9b54f.basename(filepath);
778
+ const rewriteThemeImage = (value)=>getFilename(`theme/images/${theme_getBasename(value)}`, value);
779
+ function theme_theme(manifest) {
780
+ return manifest.theme && {
781
+ theme: {
782
+ ...manifest.theme,
783
+ ...manifest.theme.images && {
784
+ images: Object.fromEntries(Object.entries(manifest.theme.images).map(([key, value])=>[
785
+ key,
786
+ Array.isArray(value) ? value.map(rewriteThemeImage) : rewriteThemeImage(value)
787
+ ]))
788
+ }
789
+ }
790
+ };
791
+ }
792
+ function userScripts(manifest) {
793
+ return manifest.user_scripts && {
794
+ user_scripts: {
795
+ ...manifest.user_scripts,
796
+ ...manifest.user_scripts.api_script && {
797
+ api_script: getFilename("user_scripts/api_script.js", manifest.user_scripts.api_script)
798
+ }
799
+ }
800
+ };
801
+ }
802
+ function normalizeOutputPath(originalPath) {
803
+ if (!originalPath) return originalPath;
804
+ const unix = originalPath.replace(/\\/g, '/');
805
+ if (/[*?[\]{}]/.test(unix)) return unix;
806
+ if (/^\/public\//i.test(unix)) return unix.replace(/^\/public\//i, '');
807
+ if (/^(?:\.\/)?public\//i.test(unix)) return unix.replace(/^(?:\.\/)?public\//i, '');
808
+ if (/^\//.test(unix)) return unix.replace(/^\//, '');
809
+ return unix;
810
+ }
811
+ function webAccessibleResources(manifest) {
812
+ if (!manifest.web_accessible_resources || !manifest.web_accessible_resources.length) return;
813
+ const mapResource = (resource)=>getFilename(normalizeOutputPath(resource), resource);
814
+ const entries = manifest.web_accessible_resources.map((entry)=>{
815
+ if ('string' == typeof entry) return mapResource(entry);
816
+ if (!entry || !Array.isArray(entry.resources)) return;
817
+ return {
818
+ ...entry,
819
+ resources: entry.resources.map(mapResource)
820
+ };
821
+ }).filter((entry)=>void 0 !== entry);
822
+ if (0 === entries.length) return;
823
+ return {
824
+ web_accessible_resources: entries
825
+ };
826
+ }
827
+ function manifestCommon(manifest, manifestPath) {
828
+ return {
829
+ ...backgroundPage(manifest),
830
+ ...chromeUrlOverrides(manifest, manifestPath),
831
+ ...content_scripts_contentScripts(manifest, manifestPath),
832
+ ...devtoolsPage(manifest, manifestPath),
833
+ ...icons_icons(manifest),
834
+ ...commands_commands(manifest),
835
+ ...permissions(manifest),
836
+ ...optionsPage(manifest, manifestPath),
837
+ ...optionsUi(manifest, manifestPath),
838
+ ...sandbox(manifest),
839
+ ...storage(manifest),
840
+ ...theme_theme(manifest),
841
+ ...userScripts(manifest),
842
+ ...webAccessibleResources(manifest),
843
+ ...contentSecurityPolicy(manifest),
844
+ ...omnibox_omnibox(manifest)
845
+ };
846
+ }
847
+ function pageAction(manifest) {
848
+ return manifest.page_action && {
849
+ page_action: {
850
+ ...manifest.page_action,
851
+ ...manifest.page_action.default_popup && {
852
+ default_popup: getFilename('action/index.html', manifest.page_action.default_popup)
853
+ },
854
+ ...manifest.page_action.default_icon && {
855
+ default_icon: 'string' == typeof manifest.page_action.default_icon ? (()=>{
856
+ const raw = String(manifest.page_action.default_icon);
857
+ return getFilename(iconOutputPath(raw), raw);
858
+ })() : Object.fromEntries(Object.entries(manifest.page_action.default_icon).map(([size, icon])=>{
859
+ const raw = String(icon);
860
+ return [
861
+ size,
862
+ getFilename(iconOutputPath(raw), raw)
863
+ ];
864
+ }))
865
+ }
866
+ }
867
+ };
868
+ }
869
+ function sidebarAction(manifest) {
870
+ return manifest.sidebar_action && {
871
+ sidebar_action: {
872
+ ...manifest.sidebar_action,
873
+ ...manifest.sidebar_action.default_panel && {
874
+ default_panel: getFilename("sidebar/index.html", manifest.sidebar_action.default_panel)
875
+ },
876
+ ...manifest.sidebar_action.default_icon && {
877
+ default_icon: 'string' == typeof manifest.sidebar_action.default_icon ? (()=>{
878
+ const raw = String(manifest.sidebar_action.default_icon);
879
+ return getFilename(iconOutputPath(raw), raw);
880
+ })() : Object.fromEntries(Object.entries(manifest.sidebar_action.default_icon).map(([size, icon])=>{
881
+ const raw = String(icon);
882
+ return [
883
+ size,
884
+ getFilename(iconOutputPath(raw), raw)
885
+ ];
886
+ }))
887
+ }
888
+ }
889
+ };
890
+ }
891
+ function background_background(manifest) {
892
+ return manifest.background?.scripts && {
893
+ background: {
894
+ scripts: [
895
+ ...new Set(manifest.background.scripts.map((script)=>getFilename("background/scripts.js", String(script))))
896
+ ]
897
+ }
898
+ };
899
+ }
900
+ function browserAction(manifest) {
901
+ return manifest.browser_action && {
902
+ browser_action: {
903
+ ...manifest.browser_action,
904
+ ...manifest.browser_action.default_popup && {
905
+ default_popup: getFilename('action/index.html', manifest.browser_action.default_popup)
906
+ },
907
+ ...manifest.browser_action.default_icon && {
908
+ default_icon: 'string' == typeof manifest.browser_action.default_icon ? (()=>{
909
+ const raw = String(manifest.browser_action.default_icon);
910
+ return getFilename(iconOutputPath(raw), raw);
911
+ })() : Object.fromEntries(Object.entries(manifest.browser_action.default_icon).map(([size, icon])=>{
912
+ const raw = String(icon);
913
+ return [
914
+ size,
915
+ getFilename(iconOutputPath(raw), raw)
916
+ ];
917
+ }))
918
+ },
919
+ ...manifest.browser_action.theme_icons && {
920
+ theme_icons: manifest.browser_action.theme_icons.map((themeIcon)=>({
921
+ ...themeIcon,
922
+ ...themeIcon.light && {
923
+ light: (()=>{
924
+ const raw = String(themeIcon.light);
925
+ const isPublic = /^(?:\/public\/|(?:\.\/)?public\/)/i.test(raw);
926
+ const target = isPublic ? normalizeManifestOutputPath(raw) : `browser_action/${__rspack_external_node_path_c5b9b54f.basename(raw)}`;
927
+ return getFilename(target, raw);
928
+ })()
929
+ },
930
+ ...themeIcon.dark && {
931
+ dark: (()=>{
932
+ const raw = String(themeIcon.dark);
933
+ const isPublic = /^(?:\/public\/|(?:\.\/)?public\/)/i.test(raw);
934
+ const target = isPublic ? normalizeManifestOutputPath(raw) : `browser_action/${__rspack_external_node_path_c5b9b54f.basename(raw)}`;
935
+ return getFilename(target, raw);
936
+ })()
937
+ }
938
+ }))
939
+ }
940
+ }
941
+ };
942
+ }
943
+ function chromeSettingsOverrides(manifest) {
944
+ return manifest.chrome_settings_overrides && {
945
+ chrome_settings_overrides: {
946
+ ...manifest.chrome_settings_overrides.homepage && {
947
+ homepage: manifest.chrome_settings_overrides.homepage
948
+ },
949
+ ...manifest.chrome_settings_overrides.search_provider && {
950
+ search_provider: {
951
+ ...manifest.chrome_settings_overrides.search_provider,
952
+ ...manifest.chrome_settings_overrides.search_provider.favicon_url && {
953
+ favicon_url: (()=>{
954
+ const fav = manifest.chrome_settings_overrides.search_provider.favicon_url;
955
+ const isUrl = /^(?:[a-z]+:)?\/\//i.test(fav);
956
+ return isUrl ? fav : getFilename(`chrome_settings_overrides/${fav.split('/').pop()}`, fav);
957
+ })()
958
+ }
959
+ }
960
+ },
961
+ ...manifest.chrome_settings_overrides.startup_pages && {
962
+ startup_pages: manifest.chrome_settings_overrides.startup_pages.map((page, index)=>getFilename(`chrome_settings_overrides/startup-${index}.html`, page))
963
+ }
964
+ }
965
+ };
966
+ }
967
+ const theme_experiment_getBasename = (filepath)=>__rspack_external_node_path_c5b9b54f.basename(filepath);
968
+ function themeExperiment(manifest) {
969
+ const te = manifest.theme_experiment;
970
+ return te && {
971
+ theme_experiment: {
972
+ ...te,
973
+ ...'string' == typeof te.stylesheet && {
974
+ stylesheet: getFilename(`theme_experiment/${theme_experiment_getBasename(te.stylesheet)}`, te.stylesheet)
975
+ },
976
+ ...Array.isArray(te.stylesheets) && {
977
+ stylesheets: te.stylesheets.map((s, i)=>getFilename(`theme_experiment/stylesheet-${i}.css`, s))
978
+ }
979
+ }
980
+ };
981
+ }
982
+ function manifestV2(manifest) {
983
+ return {
984
+ ...background_background(manifest),
985
+ ...browserAction(manifest),
986
+ ...pageAction(manifest),
987
+ ...sidebarAction(manifest),
988
+ ...chromeSettingsOverrides(manifest),
989
+ ...themeExperiment(manifest)
990
+ };
991
+ }
992
+ function action_action(manifest) {
993
+ return manifest.action && {
994
+ action: {
995
+ ...manifest.action,
996
+ ...manifest.action.default_popup && {
997
+ default_popup: getFilename("action/index.html", manifest.action.default_popup)
998
+ },
999
+ ...manifest.action.default_icon && {
1000
+ default_icon: 'string' == typeof manifest.action.default_icon ? (()=>{
1001
+ const raw = String(manifest.action.default_icon);
1002
+ return getFilename(iconOutputPath(raw), raw);
1003
+ })() : Object.fromEntries(Object.entries(manifest.action.default_icon).map(([size, icon])=>{
1004
+ const raw = String(icon);
1005
+ return [
1006
+ size,
1007
+ getFilename(iconOutputPath(raw), raw)
1008
+ ];
1009
+ }))
1010
+ }
1011
+ }
1012
+ };
1013
+ }
1014
+ function backgroundServiceWorker(manifest) {
1015
+ return manifest.background?.service_worker && {
1016
+ background: {
1017
+ service_worker: (()=>{
1018
+ const raw = String(manifest.background.service_worker);
1019
+ return getFilename('background/service_worker.js', raw);
1020
+ })()
1021
+ }
1022
+ };
1023
+ }
1024
+ function declarativeNetRequest(manifest) {
1025
+ return manifest.declarative_net_request && {
1026
+ declarative_net_request: {
1027
+ ...manifest.declarative_net_request,
1028
+ rule_resources: manifest.declarative_net_request.rule_resources.map((resourceObj)=>({
1029
+ ...resourceObj,
1030
+ path: resourceObj.path && getFilename(`declarative_net_request/${resourceObj.id}.json`, resourceObj.path)
1031
+ }))
1032
+ }
1033
+ };
1034
+ }
1035
+ function hostPermissions(manifest) {
1036
+ return manifest.host_permissions && {
1037
+ host_permissions: manifest.host_permissions
1038
+ };
1039
+ }
1040
+ function sidePanel(manifest) {
1041
+ return manifest.side_panel && {
1042
+ side_panel: {
1043
+ ...manifest.side_panel,
1044
+ ...manifest.side_panel.default_path && {
1045
+ default_path: (()=>{
1046
+ const raw = String(manifest.side_panel.default_path);
1047
+ return getFilename('sidebar/index.html', raw);
1048
+ })()
1049
+ }
1050
+ }
1051
+ };
1052
+ }
1053
+ function manifestV3(manifest) {
1054
+ return {
1055
+ ...action_action(manifest),
1056
+ ...backgroundServiceWorker(manifest),
1057
+ ...declarativeNetRequest(manifest),
1058
+ ...hostPermissions(manifest),
1059
+ ...sidePanel(manifest)
1060
+ };
1061
+ }
1062
+ function getManifestOverrides(manifestPath, manifest) {
1063
+ const manifestContent = manifest || JSON.parse(stripBom(__rspack_external_node_fs_5ea92f0c.readFileSync(manifestPath, 'utf8')));
1064
+ const omit = (obj, key)=>{
1065
+ if (!obj) return {};
1066
+ const { [key]: _ignored, ...rest } = obj;
1067
+ return rest;
1068
+ };
1069
+ const pickBackground = (obj)=>{
1070
+ const value = obj.background;
1071
+ return value && 'object' == typeof value ? value : {};
1072
+ };
1073
+ const common = manifestCommon(manifestContent, manifestPath);
1074
+ const mv2 = manifestV2(manifestContent);
1075
+ const mv3 = manifestV3(manifestContent);
1076
+ const backgroundMerged = {
1077
+ ...manifestContent.background || {},
1078
+ ...pickBackground(common),
1079
+ ...pickBackground(mv2),
1080
+ ...pickBackground(mv3)
1081
+ };
1082
+ const merged = {
1083
+ ...manifestContent,
1084
+ ...omit(common, 'background'),
1085
+ ...omit(mv2, 'background'),
1086
+ ...omit(mv3, 'background')
1087
+ };
1088
+ if (Object.keys(backgroundMerged).length) merged.background = backgroundMerged;
1089
+ return JSON.stringify(merged, null, 2);
1090
+ }
1091
+ const manifest_cjsRequire = createRequire(import.meta.url);
1092
+ const INTERNAL_MANIFEST_SOURCE = '__extensionjs_manifest_source__';
1093
+ const INTERNAL_MANIFEST_CURRENT_SOURCE = '__extensionjs_manifest_current_source__';
1094
+ function readAssetSource(asset) {
1095
+ if (!asset) return '';
1096
+ const source = asset.source;
1097
+ if ('string' == typeof source) return source;
1098
+ if ('function' == typeof source) {
1099
+ const out = source();
1100
+ return 'string' == typeof out ? out : String(out || '');
1101
+ }
1102
+ if (source && 'function' == typeof source.source) {
1103
+ const out = source.source();
1104
+ return 'string' == typeof out ? out : String(out || '');
1105
+ }
1106
+ return '';
1107
+ }
1108
+ function setOriginalManifestContent(compilation, source) {
1109
+ compilation[INTERNAL_MANIFEST_SOURCE] = source;
1110
+ }
1111
+ function getOriginalManifestContent(compilation) {
1112
+ return compilation[INTERNAL_MANIFEST_SOURCE];
1113
+ }
1114
+ function setCurrentManifestContent(compilation, source) {
1115
+ compilation[INTERNAL_MANIFEST_CURRENT_SOURCE] = source;
1116
+ }
1117
+ function getCurrentManifestContent(compilation) {
1118
+ return compilation[INTERNAL_MANIFEST_CURRENT_SOURCE];
1119
+ }
1120
+ function getManifestContent(compilation, manifestPath) {
1121
+ const currentManifest = getCurrentManifestContent(compilation);
1122
+ if (currentManifest) return parseJsonSafe(currentManifest);
1123
+ const getAsset = compilation.getAsset;
1124
+ if ('function' == typeof getAsset) {
1125
+ const manifestAsset = getAsset.call(compilation, 'manifest.json');
1126
+ const manifest = readAssetSource(manifestAsset);
1127
+ if (manifest) return parseJsonSafe(manifest);
1128
+ }
1129
+ const manifestAsset = compilation.assets?.['manifest.json'];
1130
+ if (manifestAsset) {
1131
+ const manifest = readAssetSource(manifestAsset);
1132
+ if (manifest) return parseJsonSafe(manifest);
1133
+ }
1134
+ const originalManifest = getOriginalManifestContent(compilation);
1135
+ if (originalManifest) return parseJsonSafe(originalManifest);
1136
+ try {
1137
+ const text = __rspack_external_node_fs_5ea92f0c.readFileSync(manifestPath, 'utf8');
1138
+ return parseJsonSafe(text);
1139
+ } catch {
1140
+ try {
1141
+ const resolved = manifest_cjsRequire.resolve(manifestPath);
1142
+ delete manifest_cjsRequire.cache[resolved];
1143
+ } catch {}
1144
+ return manifest_cjsRequire(manifestPath);
1145
+ }
1146
+ }
1147
+ function buildCanonicalManifest(manifestPath, manifest, browser) {
1148
+ const filteredManifest = filterKeysForThisBrowser(manifest, browser);
1149
+ return {
1150
+ ...filteredManifest,
1151
+ ...JSON.parse(getManifestOverrides(manifestPath, filteredManifest))
1152
+ };
1153
+ }
439
1154
  function resolveProcessShim() {
440
1155
  const candidate = __rspack_external_node_path_c5b9b54f.join(__rspack_import_meta_dirname__, '..', 'runtime', 'process-shim.cjs');
441
1156
  try {
@@ -511,7 +1226,7 @@ class EnvPlugin {
511
1226
  unmatchedEnvFiles = __rspack_external_node_fs_5ea92f0c.readdirSync(projectPath).filter((file)=>file.startsWith('.env') && '.env.defaults' !== file && '.env.example' !== file).sort();
512
1227
  } catch {}
513
1228
  if (unmatchedEnvFiles.length > 0) compiler.hooks.thisCompilation.tap('env:warn-unmatched', (compilation)=>{
514
- const warn = new WebpackError(envNoMatchingFile(String(this.browser), String(mode), unmatchedEnvFiles, envFiles));
1229
+ const warn = new core_WebpackError(envNoMatchingFile(String(this.browser), String(mode), unmatchedEnvFiles, envFiles));
515
1230
  warn.name = 'EnvNoMatchingFile';
516
1231
  compilation.warnings.push(warn);
517
1232
  });
@@ -559,7 +1274,7 @@ class EnvPlugin {
559
1274
  compiler.hooks.thisCompilation.tap('manifest:update-manifest', (compilation)=>{
560
1275
  compilation.hooks.processAssets.tap({
561
1276
  name: 'env:module',
562
- stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE
1277
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE
563
1278
  }, (assets)=>{
564
1279
  const files = Object.keys(compilation.assets || assets);
565
1280
  files.forEach((filename)=>{
@@ -1992,34 +2707,6 @@ async function cssInHtmlLoader(projectPath, mode, manifestPath, usage = {}) {
1992
2707
  issuer: (issuer)=>!isContentScriptEntry(issuer, manifestPath, projectPath)
1993
2708
  });
1994
2709
  }
1995
- function injectCssLink(headNode, feature, firstLinkAttrs, hrefOverride) {
1996
- const linkTag = __rspack_external_parse5_utilities_78b19c6a.createNode('link');
1997
- linkTag.attrs = [
1998
- {
1999
- name: 'rel',
2000
- value: 'stylesheet'
2001
- },
2002
- {
2003
- name: 'href',
2004
- value: hrefOverride || `/${feature}.css`
2005
- }
2006
- ];
2007
- const propagateLinkAttrs = new Set([
2008
- 'media',
2009
- 'crossorigin',
2010
- 'integrity',
2011
- 'referrerpolicy',
2012
- 'type',
2013
- 'disabled'
2014
- ]);
2015
- if (firstLinkAttrs) {
2016
- for (const attr of firstLinkAttrs)if (propagateLinkAttrs.has(attr.name) && !linkTag.attrs.find((a)=>a.name === attr.name)) linkTag.attrs.push({
2017
- name: attr.name,
2018
- value: attr.value
2019
- });
2020
- }
2021
- __rspack_external_parse5_utilities_78b19c6a.append(headNode, linkTag);
2022
- }
2023
2710
  class CssPlugin {
2024
2711
  static name = 'plugin-css';
2025
2712
  manifestPath;
@@ -2107,7 +2794,7 @@ class CssPlugin {
2107
2794
  const key = `${issuer}|${req}`;
2108
2795
  if (!warned.has(key) && compilation?.warnings) {
2109
2796
  warned.add(key);
2110
- const warning = new WebpackError(deadCssUrlRef(__rspack_external_node_path_c5b9b54f.relative(manifestDir, issuer) || issuer, raw));
2797
+ const warning = new core_WebpackError(deadCssUrlRef(__rspack_external_node_path_c5b9b54f.relative(manifestDir, issuer) || issuer, raw));
2111
2798
  warning.file = __rspack_external_node_path_c5b9b54f.relative(manifestDir, issuer);
2112
2799
  compilation?.warnings.push(warning);
2113
2800
  }
@@ -2145,7 +2832,7 @@ function findBrowserslistSource(projectPath) {
2145
2832
  if (__rspack_external_node_fs_5ea92f0c.existsSync(p)) return p;
2146
2833
  }
2147
2834
  }
2148
- function isUrl(src) {
2835
+ function parse_html_isUrl(src) {
2149
2836
  try {
2150
2837
  new URL(src);
2151
2838
  return true;
@@ -2158,7 +2845,7 @@ function parseHtml(node, onResourceFound) {
2158
2845
  if ("script" === node.nodeName) {
2159
2846
  const src = node.attrs?.find((attr)=>'src' === attr.name)?.value;
2160
2847
  if (!src) return;
2161
- if (isUrl(src)) return;
2848
+ if (parse_html_isUrl(src)) return;
2162
2849
  onResourceFound({
2163
2850
  filePath: src,
2164
2851
  childNode: node,
@@ -2172,14 +2859,14 @@ function parseHtml(node, onResourceFound) {
2172
2859
  const url = candidate.trim().split(/\s+/)[0];
2173
2860
  if (!url) continue;
2174
2861
  const { cleanPath } = cleanAssetUrl(url);
2175
- if (cleanPath && !isUrl(cleanPath)) onResourceFound({
2862
+ if (cleanPath && !parse_html_isUrl(cleanPath)) onResourceFound({
2176
2863
  filePath: cleanPath,
2177
2864
  childNode: node,
2178
2865
  assetType: 'staticHref'
2179
2866
  });
2180
2867
  }
2181
2868
  if (!href) return;
2182
- if (isUrl(href)) return;
2869
+ if (parse_html_isUrl(href)) return;
2183
2870
  const nonStylesheetRelTokens = [
2184
2871
  'dns-prefetch',
2185
2872
  'icon',
@@ -2206,7 +2893,7 @@ function parseHtml(node, onResourceFound) {
2206
2893
  } else if ('audio' === node.nodeName || 'embed' === node.nodeName || 'iframe' === node.nodeName || 'img' === node.nodeName || 'input' === node.nodeName || 'source' === node.nodeName || 'track' === node.nodeName || 'video' === node.nodeName) {
2207
2894
  const src = node.attrs?.find((attr)=>'src' === attr.name)?.value;
2208
2895
  if (!src) return;
2209
- if (isUrl(src)) return;
2896
+ if (parse_html_isUrl(src)) return;
2210
2897
  onResourceFound({
2211
2898
  filePath: src,
2212
2899
  childNode: node,
@@ -2220,7 +2907,7 @@ function parseHtml(node, onResourceFound) {
2220
2907
  const url = parts[0];
2221
2908
  if (!url) continue;
2222
2909
  const { cleanPath } = cleanAssetUrl(url);
2223
- if (cleanPath && !isUrl(cleanPath)) onResourceFound({
2910
+ if (cleanPath && !parse_html_isUrl(cleanPath)) onResourceFound({
2224
2911
  filePath: cleanPath,
2225
2912
  childNode: node,
2226
2913
  assetType: 'staticSrc'
@@ -2229,7 +2916,7 @@ function parseHtml(node, onResourceFound) {
2229
2916
  }
2230
2917
  if ('video' === node.nodeName) {
2231
2918
  const poster = node.attrs?.find((attr)=>'poster' === attr.name)?.value;
2232
- if (poster && !isUrl(poster)) {
2919
+ if (poster && !parse_html_isUrl(poster)) {
2233
2920
  const { cleanPath } = cleanAssetUrl(poster);
2234
2921
  if (cleanPath) onResourceFound({
2235
2922
  filePath: cleanPath,
@@ -2851,7 +3538,7 @@ class JsFrameworksPlugin {
2851
3538
  }
2852
3539
  const platformModulePaths = new Set();
2853
3540
  try {
2854
- const browserManifest = filterKeysForThisBrowser(manifest, this.browser);
3541
+ const browserManifest = external_browser_extension_manifest_fields_filterKeysForThisBrowser(manifest, this.browser);
2855
3542
  const background = browserManifest?.background;
2856
3543
  if (background?.type === 'module' && 'string' == typeof background?.service_worker) platformModulePaths.add(toResourceKey(__rspack_external_node_path_c5b9b54f.resolve(manifestDir, background.service_worker)));
2857
3544
  const htmlPages = {
@@ -3092,119 +3779,2869 @@ class JsFrameworksPlugin {
3092
3779
  await configuring;
3093
3780
  }
3094
3781
  }
3095
- const PAGE_DIRS = [
3096
- 'pages',
3097
- 'sidebar',
3098
- 'popup',
3099
- 'options',
3100
- 'options_ui',
3101
- 'devtools',
3102
- 'newtab',
3103
- 'sandbox',
3104
- 'action',
3105
- 'browser_action',
3106
- 'page_action'
3107
- ];
3108
- const isCodeAsset = (name)=>/\.(js|css|wasm)$/i.test(name);
3109
- const categorize_isSourceMap = (name)=>/\.map$/i.test(name);
3110
- const isHotUpdate = (name)=>/(^|\/)hot\//.test(name);
3111
- function categorizeAsset(rawName) {
3112
- const name = String(rawName || '').replace(/\\/g, '/');
3113
- if (!name) return 'ignored';
3114
- if (!isCodeAsset(name)) return 'ignored';
3115
- if (categorize_isSourceMap(name)) return 'ignored';
3116
- if (isHotUpdate(name)) return 'ignored';
3117
- if (/(^|\/)content_scripts\//.test(name)) return "content-script";
3118
- if (/(^|\/)background\//.test(name) || /(^|\/)service[-_]?worker\.(js|css|wasm)$/i.test(name)) return 'service-worker';
3119
- for (const dir of PAGE_DIRS)if (new RegExp(`(^|\\/)${dir}\\/`).test(name)) return 'page';
3120
- return 'ignored';
3782
+ const PAGE_DIRS = [
3783
+ 'pages',
3784
+ 'sidebar',
3785
+ 'popup',
3786
+ 'options',
3787
+ 'options_ui',
3788
+ 'devtools',
3789
+ 'newtab',
3790
+ 'sandbox',
3791
+ 'action',
3792
+ 'browser_action',
3793
+ 'page_action'
3794
+ ];
3795
+ const isCodeAsset = (name)=>/\.(js|css|wasm)$/i.test(name);
3796
+ const categorize_isSourceMap = (name)=>/\.map$/i.test(name);
3797
+ const isHotUpdate = (name)=>/(^|\/)hot\//.test(name);
3798
+ function categorizeAsset(rawName) {
3799
+ const name = String(rawName || '').replace(/\\/g, '/');
3800
+ if (!name) return 'ignored';
3801
+ if (!isCodeAsset(name)) return 'ignored';
3802
+ if (categorize_isSourceMap(name)) return 'ignored';
3803
+ if (isHotUpdate(name)) return 'ignored';
3804
+ if (/(^|\/)content_scripts\//.test(name)) return "content-script";
3805
+ if (/(^|\/)background\//.test(name) || /(^|\/)service[-_]?worker\.(js|css|wasm)$/i.test(name)) return 'service-worker';
3806
+ for (const dir of PAGE_DIRS)if (new RegExp(`(^|\\/)${dir}\\/`).test(name)) return 'page';
3807
+ return 'ignored';
3808
+ }
3809
+ const BUDGET_BYTES = {
3810
+ "content-script": 524288,
3811
+ 'service-worker': 524288,
3812
+ page: 1048576,
3813
+ ignored: 1 / 0
3814
+ };
3815
+ function fmtKiB(bytes) {
3816
+ const kib = bytes / 1024;
3817
+ if (kib >= 1024) return `${(kib / 1024).toFixed(2)} MiB`;
3818
+ return `${kib.toFixed(1)} KiB`;
3819
+ }
3820
+ function perfBudgetWarning(assets) {
3821
+ const header = `${1 === assets.length ? 'asset exceeds' : 'assets exceed'} the extension performance budget. Browser extensions inject content scripts on every navigation and wake service workers from cold, so we apply tighter budgets than the rspack default.`;
3822
+ const lines = assets.map((a)=>{
3823
+ const over = (a.size / a.budget * 100 - 100).toFixed(0);
3824
+ return ` ${a.name}\n size: ${fmtKiB(a.size)}\n budget: ${fmtKiB(a.budget)} (over by ${over}%)\n role: ${categoryRole(a.category)}`;
3825
+ });
3826
+ const remediation = "Lazy-load with dynamic import(), code-split per route, or replace large SDKs with thin fetch wrappers.\nSee https://rspack.rs/guide/optimization/code-splitting";
3827
+ return `${header}\n\n${lines.join('\n')}\n\n${remediation}`;
3828
+ }
3829
+ function categoryRole(c) {
3830
+ switch(c){
3831
+ case "content-script":
3832
+ return "content script, injected on every page navigation";
3833
+ case 'service-worker':
3834
+ return 'service worker / background, wakes from cold each session';
3835
+ case 'page':
3836
+ return 'UI page, opened on demand';
3837
+ default:
3838
+ return 'asset';
3839
+ }
3840
+ }
3841
+ class PerfBudgetsPlugin {
3842
+ static name = 'plugin-perf-budgets';
3843
+ options;
3844
+ constructor(options = {}){
3845
+ this.options = options;
3846
+ }
3847
+ apply(compiler) {
3848
+ const enabled = 'boolean' == typeof this.options.enabled ? this.options.enabled : 'production' === compiler.options.mode;
3849
+ if (!enabled) return;
3850
+ const budgets = {
3851
+ ...BUDGET_BYTES,
3852
+ ...this.options.budgets || {}
3853
+ };
3854
+ const report = (compilation)=>{
3855
+ if (compilation.errors?.length) return;
3856
+ const oversized = [];
3857
+ const assets = compilation.assets || {};
3858
+ for (const [name, source] of Object.entries(assets)){
3859
+ const size = source?.size?.() ?? 0;
3860
+ if (!size) continue;
3861
+ const category = categorizeAsset(name);
3862
+ if ('ignored' === category) continue;
3863
+ const budget = budgets[category];
3864
+ if (Number.isFinite(budget)) {
3865
+ if (!(size <= budget)) oversized.push({
3866
+ name,
3867
+ size,
3868
+ budget,
3869
+ category
3870
+ });
3871
+ }
3872
+ }
3873
+ if (0 === oversized.length) return;
3874
+ oversized.sort((a, b)=>b.size - a.size);
3875
+ const ErrorConstructor = compiler?.rspack?.WebpackError || Error;
3876
+ const warning = new ErrorConstructor(perfBudgetWarning(oversized));
3877
+ warning.name = 'PerfBudgetWarning';
3878
+ if (!compilation.warnings) compilation.warnings = [];
3879
+ compilation.warnings.push(warning);
3880
+ };
3881
+ const REPORT_STAGE = compiler?.rspack?.Compilation?.PROCESS_ASSETS_STAGE_REPORT ?? 5000;
3882
+ compiler.hooks.thisCompilation.tap(PerfBudgetsPlugin.name, (compilation)=>{
3883
+ compilation.hooks.processAssets.tap({
3884
+ name: PerfBudgetsPlugin.name,
3885
+ stage: REPORT_STAGE
3886
+ }, ()=>report(compilation));
3887
+ });
3888
+ }
3889
+ }
3890
+ const BRIDGE_PRODUCER_SOURCE = `;(function () {
3891
+ try {
3892
+ var g = (typeof globalThis === "object" && globalThis) ? globalThis : this;
3893
+ if (!g || g.__extjsBridgeProducerInstalled) return;
3894
+
3895
+ var PORT = parseInt("__EXTJS_CONTROL_PORT__", 10);
3896
+ var INSTANCE_ID = "__EXTJS_INSTANCE_ID__";
3897
+ var CONTEXT = "__EXTJS_CONTEXT__";
3898
+ // Connectable host of the dev-server control WS. Baked from the resolved
3899
+ // connectable host (loopback locally; the public host for remote/devcontainer).
3900
+ var HOST = "__EXTJS_CONTROL_HOST__";
3901
+ if (!HOST || HOST.indexOf("__EXTJS") === 0) HOST = "127.0.0.1";
3902
+ if (!PORT || PORT < 1) return;
3903
+
3904
+ var WS = g.WebSocket;
3905
+ if (typeof WS !== "function") return;
3906
+
3907
+ var consoleRef = g.console || {};
3908
+ g.__extjsBridgeProducerInstalled = true;
3909
+
3910
+ // Capture extension event listeners at install time: these wraps run
3911
+ // BEFORE user code, and no platform API exists to dispatch these events.
3912
+ function captureEvent(event, sink) {
3913
+ try {
3914
+ if (!event || typeof event.addListener !== "function") return;
3915
+ var origAdd = event.addListener.bind(event);
3916
+ event.addListener = function (cb) {
3917
+ if (typeof cb === "function" && sink.indexOf(cb) === -1) sink.push(cb);
3918
+ return origAdd(cb);
3919
+ };
3920
+ if (typeof event.removeListener === "function") {
3921
+ var origRemove = event.removeListener.bind(event);
3922
+ event.removeListener = function (cb) {
3923
+ var i = sink.indexOf(cb);
3924
+ if (i !== -1) sink.splice(i, 1);
3925
+ return origRemove(cb);
3926
+ };
3927
+ }
3928
+ } catch (e) { /* non-fatal: trigger falls back to its no-listener reply */ }
3929
+ }
3930
+
3931
+ // Use g.chrome (not the hoisted chrome var below). This runs first.
3932
+ var actionClickedListeners = [];
3933
+ var commandListeners = [];
3934
+ if (g.chrome) {
3935
+ captureEvent(g.chrome.action && g.chrome.action.onClicked, actionClickedListeners);
3936
+ captureEvent(g.chrome.commands && g.chrome.commands.onCommand, commandListeners);
3937
+ }
3938
+
3939
+ var LEVELS = ["log", "info", "warn", "error", "debug", "trace"];
3940
+ var socket = null;
3941
+ var open = false;
3942
+ var queue = [];
3943
+ var backoff = 250;
3944
+ var MAX_BACKOFF = 5000;
3945
+ var MAX_QUEUE = 1000;
3946
+ var MAX_RESULT_BYTES = 256 * 1024;
3947
+
3948
+ function nowId() {
3949
+ return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
3950
+ }
3951
+
3952
+ function engineName() {
3953
+ try {
3954
+ return (typeof navigator !== "undefined" && /firefox/i.test(navigator.userAgent)) ? "firefox" : "chromium";
3955
+ } catch (e) { return "chromium"; }
3956
+ }
3957
+
3958
+ function safeValue(v) {
3959
+ try {
3960
+ var s = JSON.stringify(v);
3961
+ if (s === undefined) return {value: undefined, truncated: false};
3962
+ if (s.length > MAX_RESULT_BYTES) {
3963
+ return {value: {__type: "truncated", preview: s.slice(0, 1024)}, truncated: true};
3964
+ }
3965
+ return {value: JSON.parse(s), truncated: false};
3966
+ } catch (e) {
3967
+ return {value: {__type: "unserializable", preview: String(v).slice(0, 512)}, truncated: false};
3968
+ }
3969
+ }
3970
+
3971
+ function replyOk(cmdId, value) {
3972
+ var s = safeValue(value);
3973
+ var frame = {type: "result", cmdId: cmdId, ok: true, value: s.value};
3974
+ if (s.truncated) frame.truncated = true;
3975
+ try { socket && socket.send(JSON.stringify(frame)); } catch (e) {
3976
+ // Ignore
3977
+ }
3978
+ }
3979
+
3980
+ function replyErr(cmdId, name, message, code) {
3981
+ var frame = {type: "result", cmdId: cmdId, ok: false, error: {name: name, message: String(message), engine: engineName()}};
3982
+ if (code) frame.error.code = code;
3983
+ try { socket && socket.send(JSON.stringify(frame)); } catch (e) {
3984
+ // Ignore
3985
+ }
3986
+ }
3987
+
3988
+ // Name the refusal the browser wrote as prose, so a consumer branches on
3989
+ // error.code instead of matching the engine's sentence.
3990
+ function openRefusalCode(message) {
3991
+ var m = String(message == null ? "" : message);
3992
+ if (/user gesture|user input|user interaction|user action/i.test(m)) return "needs_user_gesture";
3993
+ if (/browser window|active window/i.test(m)) return "needs_headed_window";
3994
+ return undefined;
3995
+ }
3996
+
3997
+ // Resolve a numeric tab id when the caller did not pass one: match
3998
+ // target.url against open tabs, else default to the active tab. (#51)
3999
+ function resolveTargetTab(target, cb) {
4000
+ var chrome = g.chrome;
4001
+ if (!chrome || !chrome.tabs) { cb(null, "chrome.tabs is not available"); return; }
4002
+ var url = target && target.url;
4003
+ if (url) {
4004
+ var substringMatch = function () {
4005
+ try {
4006
+ chrome.tabs.query({}, function (all) {
4007
+ var hit = (all || []).filter(function (t) { return t.url && String(t.url).indexOf(url) !== -1; })[0];
4008
+ if (hit && typeof hit.id === "number") { cb(hit.id, null); return; }
4009
+ cb(null, "no open tab matches url: " + url);
4010
+ });
4011
+ } catch (e2) { cb(null, "tab query failed"); }
4012
+ };
4013
+ try {
4014
+ // chrome.tabs.query accepts URL match patterns; a bare url/glob may
4015
+ // not be a valid pattern, so fall back to a substring scan.
4016
+ chrome.tabs.query({url: url}, function (tabs) {
4017
+ if (chrome.runtime && chrome.runtime.lastError) { substringMatch(); return; }
4018
+ if (tabs && tabs.length && typeof tabs[0].id === "number") { cb(tabs[0].id, null); return; }
4019
+ substringMatch();
4020
+ });
4021
+ } catch (e) { substringMatch(); }
4022
+ return;
4023
+ }
4024
+ try {
4025
+ chrome.tabs.query({active: true, lastFocusedWindow: true}, function (tabs) {
4026
+ var t = tabs && tabs[0];
4027
+ if (t && typeof t.id === "number") { cb(t.id, null); return; }
4028
+ cb(null, "no active tab to target");
4029
+ });
4030
+ } catch (e3) { cb(null, "tab query failed"); }
4031
+ }
4032
+
4033
+ // chrome.* async APIs are callback-only on Gecko. Prefer promisified
4034
+ // browser.* when present, else the chrome.* callback form. cb(err, result).
4035
+ function nsCall(nsName, method, callArgs, cb) {
4036
+ var chromeG = g.chrome;
4037
+ var done = false;
4038
+ var once = function (err, r) { if (!done) { done = true; cb(err, r); } };
4039
+ var viaPromise = function (ns) {
4040
+ try {
4041
+ var ret = ns[method].apply(ns, callArgs);
4042
+ if (ret && typeof ret.then === "function") {
4043
+ ret.then(function (r) { once(null, r); }, function (e) { once(e); });
4044
+ return true;
4045
+ }
4046
+ } catch (e) { once(e); return true; }
4047
+ return false;
4048
+ };
4049
+ var bNS = g.browser && g.browser[nsName];
4050
+ if (bNS && typeof bNS[method] === "function" && viaPromise(bNS)) return;
4051
+ var cNS = chromeG && chromeG[nsName];
4052
+ if (!cNS || typeof cNS[method] !== "function") {
4053
+ once(new Error("chrome." + nsName + "." + method + " is not available (engine: " + engineName() + ")"));
4054
+ return;
4055
+ }
4056
+ try {
4057
+ var ret2 = cNS[method].apply(cNS, callArgs.concat([function (r) {
4058
+ var le = chromeG.runtime && chromeG.runtime.lastError;
4059
+ if (le) once(le); else once(null, r);
4060
+ }]));
4061
+ // A promise-only implementation ignores the trailing callback and
4062
+ // returns a thenable instead; resolve through it (once() dedupes).
4063
+ if (ret2 && typeof ret2.then === "function") {
4064
+ ret2.then(function (r) { once(null, r); }, function (e) { once(e); });
4065
+ }
4066
+ } catch (e2) {
4067
+ if (!viaPromise(cNS)) once(e2);
4068
+ }
4069
+ }
4070
+
4071
+ function executeCommand(cmd) {
4072
+ var op = cmd.op, target = cmd.target || {}, args = cmd.args || {};
4073
+ var ctx = target.context || "background";
4074
+ var chrome = g.chrome;
4075
+ var cmdId = cmd.cmdId;
4076
+ // content/page eval & inspect need a numeric tab id: resolve --url (or
4077
+ // nothing) to a tab and re-dispatch with target.tabId filled in. (#51)
4078
+ if ((op === "eval" || op === "inspect") && (ctx === "content" || ctx === "page") && target.tabId == null) {
4079
+ resolveTargetTab(target, function (tabId, err) {
4080
+ if (tabId == null) {
4081
+ replyErr(cmdId, "Unsupported", err || ("eval/inspect in context " + ctx + " needs a --tab id, a --url to match, or an active tab"));
4082
+ return;
4083
+ }
4084
+ var next = {};
4085
+ for (var k in cmd) { if (Object.prototype.hasOwnProperty.call(cmd, k)) next[k] = cmd[k]; }
4086
+ next.target = {};
4087
+ for (var tk in target) { if (Object.prototype.hasOwnProperty.call(target, tk)) next.target[tk] = target[tk]; }
4088
+ next.target.tabId = tabId;
4089
+ executeCommand(next);
4090
+ });
4091
+ return;
4092
+ }
4093
+ try {
4094
+ if (!chrome) { replyErr(cmdId, "Unsupported", "chrome.* not available in this context"); return; }
4095
+ if (op === "eval") {
4096
+ if (ctx === "background") {
4097
+ Promise.resolve().then(function () { return (0, eval)(args.expression); })
4098
+ .then(function (r) { replyOk(cmdId, r); }, function (e) {
4099
+ var msg = (e && e.message) || String(e);
4100
+ // MV3 forbids eval of strings in the SW/extension pages and
4101
+ // rejects 'unsafe-eval'. Surface it honestly with an alternative.
4102
+ if (/Content Security Policy|unsafe-eval/i.test(msg)) {
4103
+ replyErr(cmdId, "Unsupported", "eval is blocked in the MV3 service worker by CSP. Use --context page --tab <id> (eval runs in the page's MAIN world), or run on an MV2/Firefox build. Engine: " + engineName());
4104
+ } else {
4105
+ replyErr(cmdId, (e && e.name) || "EvalError", msg);
4106
+ }
4107
+ });
4108
+ } else if ((ctx === "content" || ctx === "page") && target.tabId && chrome.scripting) {
4109
+ // Wrap the eval so a throw INSIDE the tab comes back as data:
4110
+ // Chrome swallows an injected function's exception (null result).
4111
+ nsCall("scripting", "executeScript", [{
4112
+ target: {tabId: target.tabId},
4113
+ world: ctx === "page" ? "MAIN" : "ISOLATED",
4114
+ func: function (src) {
4115
+ try { return {__extjsEval: 1, ok: true, value: eval(src)}; }
4116
+ catch (e) { return {__extjsEval: 1, ok: false, name: (e && e.name) || "EvalError", message: (e && e.message) || String(e)}; }
4117
+ },
4118
+ args: [String(args.expression)]
4119
+ }], function (err, res) {
4120
+ if (err) { replyErr(cmdId, "TargetNotFound", (err && err.message) || err); return; }
4121
+ var frame = res && res[0] ? res[0].result : undefined;
4122
+ if (!frame || frame.__extjsEval !== 1) {
4123
+ replyErr(cmdId, "TargetNotFound", "the expression never executed in tab " + target.tabId + ": no injectable frame returned a result (restricted page, or outside host_permissions)");
4124
+ return;
4125
+ }
4126
+ if (frame.ok) { replyOk(cmdId, frame.value); return; }
4127
+ if (ctx === "content" && /Content Security Policy|unsafe-eval|EvalError/i.test(String(frame.name) + " " + String(frame.message))) {
4128
+ replyErr(cmdId, "Unsupported", "eval of a string is blocked in the ISOLATED (content) world by the extension CSP. Use --context page (runs in the page's MAIN world). Original: " + frame.message);
4129
+ return;
4130
+ }
4131
+ replyErr(cmdId, frame.name || "EvalError", frame.message);
4132
+ });
4133
+ } else if (ctx === "popup" || ctx === "options" || ctx === "sidebar" || ctx === "devtools" || ctx === "newtab" || ctx === "history" || ctx === "bookmarks") {
4134
+ // The SW can't eval in another extension page; ask the surface's
4135
+ // own in-bundle relay (only the open, matching-context page responds).
4136
+ chrome.runtime.sendMessage(
4137
+ {__extjsEvalRequest: true, target: target, args: {expression: String(args.expression)}},
4138
+ function (resp) {
4139
+ if ((chrome.runtime && chrome.runtime.lastError) || !resp) {
4140
+ replyErr(cmdId, "Unsupported", "surface '" + ctx + "' is not open (open it first: extension open " + ctx + ")", "surface_not_open");
4141
+ } else if (resp.ok) {
4142
+ replyOk(cmdId, resp.value);
4143
+ } else {
4144
+ replyErr(cmdId, (resp.error && resp.error.name) || "EvalError", (resp.error && resp.error.message) || "eval failed");
4145
+ }
4146
+ }
4147
+ );
4148
+ } else if (ctx === "content" || ctx === "page") {
4149
+ replyErr(cmdId, "Unsupported", target.tabId
4150
+ ? "chrome.scripting is not available on this engine (MV2 has no scripting API); use --context background"
4151
+ : "eval in context " + ctx + " requires a tabId");
4152
+ } else {
4153
+ replyErr(cmdId, "BadRequest", "unknown eval context: " + ctx);
4154
+ }
4155
+ return;
4156
+ }
4157
+ if (op === "storage.get" || op === "storage.set") {
4158
+ // chrome.* storage is callback-only on Gecko and older Chromium.
4159
+ // Prefer promisified browser.*, then fall back to the callback shape.
4160
+ var storageNS = (g.browser && g.browser.storage) ? g.browser.storage : chrome.storage;
4161
+ var storageArea = storageNS && storageNS[args.area || "local"];
4162
+ if (!storageArea) { replyErr(cmdId, "StorageError", "storage." + (args.area || "local") + " unavailable"); return; }
4163
+ var isSet = op === "storage.set";
4164
+ var callArgs = isSet ? [args.items || {}] : [args.key != null ? args.key : null];
4165
+ var onStorageOk = function (r) { replyOk(cmdId, isSet ? {set: Object.keys(args.items || {})} : r); };
4166
+ var onStorageErr = function (e) { replyErr(cmdId, "StorageError", (e && e.message) || String(e)); };
4167
+ var storageFn = isSet ? storageArea.set : storageArea.get;
4168
+ try {
4169
+ var storageRet = storageFn.apply(storageArea, callArgs);
4170
+ if (storageRet && typeof storageRet.then === "function") {
4171
+ storageRet.then(onStorageOk, onStorageErr);
4172
+ return;
4173
+ }
4174
+ } catch (e) { /* not thenable / threw synchronously: fall back to callback */ }
4175
+ try {
4176
+ storageFn.apply(storageArea, callArgs.concat([function (r) {
4177
+ var le = chrome.runtime && chrome.runtime.lastError;
4178
+ if (le) onStorageErr(le); else onStorageOk(r);
4179
+ }]));
4180
+ } catch (e) { onStorageErr(e); }
4181
+ return;
4182
+ }
4183
+ if (op === "reload") {
4184
+ if (ctx === "background") {
4185
+ replyOk(cmdId, {reloading: true});
4186
+ setTimeout(function () { try { chrome.runtime.reload(); } catch (e) {
4187
+ // Ignore
4188
+ } }, 50);
4189
+ } else if (target.tabId) {
4190
+ nsCall("tabs", "reload", [target.tabId], function (err) {
4191
+ if (err) replyErr(cmdId, "ReloadError", (err && err.message) || err);
4192
+ else replyOk(cmdId, {reloaded: target.tabId});
4193
+ });
4194
+ } else {
4195
+ replyErr(cmdId, "Unsupported", "reload needs background or a tabId");
4196
+ }
4197
+ return;
4198
+ }
4199
+ if (op === "open") {
4200
+ var surface = args.surface || ctx;
4201
+ // getPopup is promise-style on Gecko and (MV3) Chromium; fall back to
4202
+ // callback-style for older Chromium. Always resolves to a string.
4203
+ var getActionPopup = function (cb) {
4204
+ try {
4205
+ var r = chrome.action && chrome.action.getPopup && chrome.action.getPopup({});
4206
+ if (r && typeof r.then === "function") {
4207
+ r.then(function (p) { cb(p || ""); }, function () { cb(""); });
4208
+ return;
4209
+ }
4210
+ } catch (e) {
4211
+ // Ignore
4212
+ }
4213
+ try { chrome.action.getPopup({}, function (p) { cb(p || ""); }); }
4214
+ catch (e) { cb(""); }
4215
+ };
4216
+ var resolveActiveTab = function (a, cb) {
4217
+ if (a && typeof a.tabId === "number") {
4218
+ try { chrome.tabs.get(a.tabId, function (t) { cb(t || {id: a.tabId}); }); return; }
4219
+ catch (e) {
4220
+ // Ignore
4221
+ }
4222
+ }
4223
+ try { chrome.tabs.query({active: true, lastFocusedWindow: true}, function (tabs) { cb((tabs && tabs[0]) || undefined); }); }
4224
+ catch (e) { cb(undefined); }
4225
+ };
4226
+ // Replaying a listener carries no user gesture, so activeTab is never
4227
+ // granted. Warn when the manifest declares it.
4228
+ var activeTabWarning = function () {
4229
+ try {
4230
+ var m = chrome.runtime.getManifest();
4231
+ var perms = (m && m.permissions) || [];
4232
+ if (perms.indexOf("activeTab") !== -1) {
4233
+ return "replayed without a user gesture: activeTab is NOT granted, so APIs that depend on it (scripting on the active tab, captureVisibleTab) behave differently than a real click";
4234
+ }
4235
+ } catch (e) {
4236
+ // Ignore
4237
+ }
4238
+ return null;
4239
+ };
4240
+ if (surface === "popup") {
4241
+ if (chrome.action && chrome.action.openPopup) {
4242
+ nsCall("action", "openPopup", [], function (err) {
4243
+ if (err) {
4244
+ var openMsg = (err && err.message) || err;
4245
+ replyErr(cmdId, "Unsupported", "openPopup: " + openMsg, openRefusalCode(openMsg));
4246
+ } else replyOk(cmdId, {opened: "popup"});
4247
+ });
4248
+ } else { replyErr(cmdId, "Unsupported", "action.openPopup not available", "api_unavailable"); }
4249
+ } else if (surface === "options") {
4250
+ try { chrome.runtime.openOptionsPage(function () { replyOk(cmdId, {opened: "options"}); }); }
4251
+ catch (e) { replyErr(cmdId, "Unsupported", "openOptionsPage: " + e); }
4252
+ } else if (surface === "sidebar") {
4253
+ if (chrome.sidePanel && chrome.sidePanel.open && chrome.windows) {
4254
+ chrome.windows.getCurrent(function (w) {
4255
+ nsCall("sidePanel", "open", [{windowId: w.id}], function (err) {
4256
+ if (err) {
4257
+ var sideMsg = (err && err.message) || err;
4258
+ replyErr(cmdId, "Unsupported", "sidePanel.open: " + sideMsg, openRefusalCode(sideMsg));
4259
+ } else replyOk(cmdId, {opened: "sidebar"});
4260
+ });
4261
+ });
4262
+ } else { replyErr(cmdId, "Unsupported", "sidePanel not available (engine: " + engineName() + ")", "api_unavailable"); }
4263
+ } else if (surface === "action") {
4264
+ // With a default_popup the click opens it; without one, replay the
4265
+ // onClicked listeners captured at install time.
4266
+ if (chrome.action) {
4267
+ getActionPopup(function (popup) {
4268
+ if (popup) {
4269
+ nsCall("action", "openPopup", [], function (err) {
4270
+ if (err) {
4271
+ var actMsg = (err && err.message) || err;
4272
+ replyErr(cmdId, "Unsupported", "openPopup: " + actMsg, openRefusalCode(actMsg));
4273
+ } else replyOk(cmdId, {triggered: "popup"});
4274
+ });
4275
+ } else if (actionClickedListeners.length) {
4276
+ resolveActiveTab(args, function (tab) {
4277
+ var fired = 0;
4278
+ for (var i = 0; i < actionClickedListeners.length; i++) {
4279
+ try { actionClickedListeners[i](tab); fired++; } catch (e) {
4280
+ // Ignore
4281
+ }
4282
+ }
4283
+ var reply = {triggered: "onClicked", listeners: fired, gesture: false};
4284
+ var warning = activeTabWarning();
4285
+ if (warning) reply.warning = warning;
4286
+ replyOk(cmdId, reply);
4287
+ });
4288
+ } else {
4289
+ replyErr(cmdId, "Unsupported", "action has no popup and no onClicked listener registered");
4290
+ }
4291
+ });
4292
+ } else { replyErr(cmdId, "Unsupported", "chrome.action not available (engine: " + engineName() + ")", "api_unavailable"); }
4293
+ } else if (surface === "command") {
4294
+ var commandName = (args && args.name) || undefined;
4295
+ if (!commandListeners.length) {
4296
+ replyErr(cmdId, "Unsupported", "no chrome.commands.onCommand listener registered");
4297
+ } else {
4298
+ resolveActiveTab(args, function (tab) {
4299
+ var fired = 0;
4300
+ for (var i = 0; i < commandListeners.length; i++) {
4301
+ try { commandListeners[i](commandName, tab); fired++; } catch (e) {
4302
+ // Ignore
4303
+ }
4304
+ }
4305
+ replyOk(cmdId, {triggered: "command", command: commandName || null, listeners: fired, gesture: false});
4306
+ });
4307
+ }
4308
+ } else { replyErr(cmdId, "BadRequest", "unknown surface: " + surface); }
4309
+ return;
4310
+ }
4311
+ if (op === "tabs.query") {
4312
+ nsCall("tabs", "query", [args || {}], function (err, tabs) {
4313
+ if (err) { replyErr(cmdId, "TabsError", (err && err.message) || err); return; }
4314
+ replyOk(cmdId, (tabs || []).map(function (t) { return {id: t.id, url: t.url, title: t.title, active: t.active, windowId: t.windowId}; }));
4315
+ });
4316
+ return;
4317
+ }
4318
+ if (op === "inspect") {
4319
+ // Extract a DOM snapshot from the target page via chrome.scripting
4320
+ // (CDP-free). Closed shadow roots need CDP; here we read open ones.
4321
+ if ((ctx === "content" || ctx === "page") && target.tabId && chrome.scripting) {
4322
+ var maxBytes = (args && args.maxBytes) || 262144;
4323
+ var includeHtml = !args || !args.include || args.include.indexOf("html") !== -1;
4324
+ nsCall("scripting", "executeScript", [{
4325
+ target: {tabId: target.tabId},
4326
+ world: ctx === "page" ? "MAIN" : "ISOLATED",
4327
+ func: function (wantHtml, cap) {
4328
+ function countShadow(root) {
4329
+ var n = 0, els = root.querySelectorAll("*");
4330
+ for (var i = 0; i < els.length; i++) { if (els[i].shadowRoot) { n++; n += countShadow(els[i].shadowRoot); } }
4331
+ return n;
4332
+ }
4333
+ var doc = document;
4334
+ var roots = doc.querySelectorAll('#extension-root,[data-extension-root]');
4335
+ var out = {
4336
+ url: location.href,
4337
+ title: doc.title,
4338
+ summary: {
4339
+ htmlLength: doc.documentElement.outerHTML.length,
4340
+ scriptCount: doc.querySelectorAll("script").length,
4341
+ styleCount: doc.querySelectorAll("style,link[rel=stylesheet]").length,
4342
+ extensionRootCount: roots.length,
4343
+ openShadowRoots: countShadow(doc),
4344
+ bodyChildCount: doc.body ? doc.body.children.length : 0
4345
+ }
4346
+ };
4347
+ if (wantHtml) {
4348
+ var html = doc.documentElement.outerHTML;
4349
+ if (cap > 0 && html.length > cap) { out.html = html.slice(0, cap); out.htmlTruncated = true; }
4350
+ else { out.html = html; }
4351
+ }
4352
+ return out;
4353
+ },
4354
+ args: [includeHtml, maxBytes]
4355
+ }], function (err, res) {
4356
+ if (err) { replyErr(cmdId, "TargetNotFound", (err && err.message) || err); return; }
4357
+ var snap = res && res[0] ? res[0].result : undefined;
4358
+ if (snap == null) { replyErr(cmdId, "TargetNotFound", "no injectable frame returned a snapshot for tab " + target.tabId + " (restricted page, or outside host_permissions)"); return; }
4359
+ replyOk(cmdId, snap);
4360
+ });
4361
+ } else if (ctx === "popup" || ctx === "options" || ctx === "sidebar" || ctx === "devtools" || ctx === "newtab" || ctx === "history" || ctx === "bookmarks") {
4362
+ // The SW can't read a surface page's DOM; ask the surface's own
4363
+ // in-bundle relay (only the open, matching-context page responds).
4364
+ chrome.runtime.sendMessage(
4365
+ {__extjsInspectRequest: true, target: target, args: args},
4366
+ function (resp) {
4367
+ if (chrome.runtime.lastError || !resp) {
4368
+ replyErr(cmdId, "Unsupported", "surface '" + ctx + "' is not open (open it first: extension open " + ctx + ")", "surface_not_open");
4369
+ } else if (resp.ok) {
4370
+ replyOk(cmdId, resp.value);
4371
+ } else {
4372
+ replyErr(cmdId, (resp.error && resp.error.name) || "InspectError", (resp.error && resp.error.message) || "inspect failed");
4373
+ }
4374
+ }
4375
+ );
4376
+ } else if (ctx === "background") {
4377
+ replyErr(cmdId, "Unsupported", "the service worker has no DOM; inspect a content/page (with --tab) or an open surface");
4378
+ } else {
4379
+ replyErr(cmdId, "Unsupported", "inspect of " + ctx + " requires a tabId (content/page) or an open surface");
4380
+ }
4381
+ return;
4382
+ }
4383
+ replyErr(cmdId, "BadRequest", "unknown op: " + op);
4384
+ } catch (e) {
4385
+ replyErr(cmdId, "ExecutorError", e);
4386
+ }
4387
+ }
4388
+
4389
+ function noopLastError() { try { void g.chrome.runtime.lastError; } catch (e) {
4390
+ // Ignore
4391
+ } }
4392
+
4393
+ // Only http(s)/file/ftp tabs can run a content script; skip chrome://, the
4394
+ // extension's own pages, about:blank, etc.
4395
+ function isInjectableUrl(url) {
4396
+ return typeof url === "string" && /^(https?|file|ftp):/i.test(url);
4397
+ }
4398
+
4399
+ // Re-inject one declared entry's fresh files into every matching open tab.
4400
+ // exclude_matches MUST be honored: query the excludes and subtract them.
4401
+ function reinjectContentScriptEntry(entry) {
4402
+ var chrome = g.chrome;
4403
+ var matches = (entry && entry.matches) || [];
4404
+ if (!Array.isArray(matches) || !matches.length) return;
4405
+ var excludeMatches = (entry && entry.exclude_matches) || [];
4406
+ var jsFiles = ((entry && entry.js) || []).filter(function (f) { return typeof f === "string"; });
4407
+ var cssFiles = ((entry && entry.css) || []).filter(function (f) { return typeof f === "string"; });
4408
+ if (!jsFiles.length && !cssFiles.length) return;
4409
+ var world = entry.world === "MAIN" ? "MAIN" : "ISOLATED";
4410
+ var allFrames = !!entry.all_frames;
4411
+ function injectInto(excludedTabIds) {
4412
+ try {
4413
+ chrome.tabs.query({url: matches}, function (tabs) {
4414
+ var err = null;
4415
+ try { err = chrome.runtime.lastError; } catch (e) {
4416
+ // Ignore
4417
+ }
4418
+ if (err || !tabs) return;
4419
+ for (var i = 0; i < tabs.length; i++) {
4420
+ (function (tab) {
4421
+ if (!tab || tab.id == null || !isInjectableUrl(tab.url)) return;
4422
+ if (excludedTabIds[tab.id]) return;
4423
+ var target = {tabId: tab.id, allFrames: allFrames};
4424
+ if (cssFiles.length && chrome.scripting.insertCSS) {
4425
+ try { chrome.scripting.insertCSS({target: target, files: cssFiles}, noopLastError); } catch (e) {
4426
+ // Ignore
4427
+ }
4428
+ }
4429
+ if (jsFiles.length && chrome.scripting.executeScript) {
4430
+ try {
4431
+ chrome.scripting.executeScript(
4432
+ {target: target, files: jsFiles, world: world, injectImmediately: true},
4433
+ noopLastError
4434
+ );
4435
+ } catch (e) {
4436
+ // Ignore
4437
+ }
4438
+ }
4439
+ })(tabs[i]);
4440
+ }
4441
+ });
4442
+ } catch (e) {
4443
+ // Ignore
4444
+ }
4445
+ }
4446
+ if (Array.isArray(excludeMatches) && excludeMatches.length) {
4447
+ try {
4448
+ chrome.tabs.query({url: excludeMatches}, function (excludedTabs) {
4449
+ try { void chrome.runtime.lastError; } catch (e) {
4450
+ // Ignore
4451
+ }
4452
+ var excluded = {};
4453
+ if (excludedTabs) {
4454
+ for (var i = 0; i < excludedTabs.length; i++) {
4455
+ if (excludedTabs[i] && excludedTabs[i].id != null) excluded[excludedTabs[i].id] = true;
4456
+ }
4457
+ }
4458
+ injectInto(excluded);
4459
+ });
4460
+ return;
4461
+ } catch (e) {
4462
+ // Ignore
4463
+ }
4464
+ }
4465
+ injectInto({});
4466
+ }
4467
+
4468
+ // Re-inject all content scripts into their open tabs. Reads the manifest
4469
+ // FROM DISK: getManifest() is frozen and dev filenames are content-hashed.
4470
+ function reinjectContentScripts(onDone) {
4471
+ var chrome = g.chrome;
4472
+ try {
4473
+ if (typeof g.fetch !== "function" || !chrome.scripting) return false;
4474
+ g.fetch(chrome.runtime.getURL("manifest.json"), {cache: "no-store"})
4475
+ .then(function (r) { return r.json(); })
4476
+ .then(function (manifest) {
4477
+ var entries = (manifest && manifest.content_scripts) || [];
4478
+ for (var i = 0; i < entries.length; i++) reinjectContentScriptEntry(entries[i]);
4479
+ reregisterForFutureNavigations(entries);
4480
+ if (onDone) { try { onDone(); } catch (e) {
4481
+ // Ignore
4482
+ } }
4483
+ })
4484
+ .catch(function () {
4485
+ // Ignore
4486
+ });
4487
+ return true;
4488
+ } catch (e) { return false; }
4489
+ }
4490
+
4491
+ function mapRunAt(runAt) {
4492
+ if (runAt === "document_start") return "document_start";
4493
+ if (runAt === "document_end") return "document_end";
4494
+ return "document_idle";
4495
+ }
4496
+
4497
+ // Re-register content scripts dynamically at the fresh files, so tabs
4498
+ // opened AFTER an edit also get the new build. Idempotent per entry id.
4499
+ function reregisterForFutureNavigations(entries) {
4500
+ var chrome = g.chrome;
4501
+ if (
4502
+ !chrome.scripting ||
4503
+ !chrome.scripting.registerContentScripts ||
4504
+ !chrome.scripting.getRegisteredContentScripts
4505
+ ) return;
4506
+
4507
+ var scripts = [];
4508
+ for (var i = 0; i < entries.length; i++) {
4509
+ var e = entries[i] || {};
4510
+ if (!Array.isArray(e.matches) || !e.matches.length) continue;
4511
+ var js = (e.js || []).filter(function (f) { return typeof f === "string"; });
4512
+ var css = (e.css || []).filter(function (f) { return typeof f === "string"; });
4513
+ if (!js.length && !css.length) continue;
4514
+ var s = {
4515
+ id: "extjs-dev-cs-" + i,
4516
+ matches: e.matches,
4517
+ runAt: mapRunAt(e.run_at),
4518
+ allFrames: !!e.all_frames,
4519
+ world: e.world === "MAIN" ? "MAIN" : "ISOLATED"
4520
+ };
4521
+ // Dev registration must not be BROADER than the static one: dropping
4522
+ // exclude_matches injects into pages the browser itself would skip.
4523
+ if (Array.isArray(e.exclude_matches) && e.exclude_matches.length) {
4524
+ s.excludeMatches = e.exclude_matches;
4525
+ }
4526
+ if (js.length) s.js = js;
4527
+ if (css.length) s.css = css;
4528
+ scripts.push(s);
4529
+ }
4530
+ if (!scripts.length) return;
4531
+
4532
+ try {
4533
+ chrome.scripting.getRegisteredContentScripts(function (existing) {
4534
+ try { void chrome.runtime.lastError; } catch (e) {
4535
+ // Ignore
4536
+ }
4537
+ var have = {};
4538
+ if (existing) for (var k = 0; k < existing.length; k++) have[existing[k].id] = true;
4539
+ var toRegister = [], toUpdate = [];
4540
+ for (var j = 0; j < scripts.length; j++) {
4541
+ (have[scripts[j].id] ? toUpdate : toRegister).push(scripts[j]);
4542
+ }
4543
+ if (toRegister.length) {
4544
+ try { chrome.scripting.registerContentScripts(toRegister, noopLastError); } catch (e) {
4545
+ // Ignore
4546
+ }
4547
+ }
4548
+ if (toUpdate.length) {
4549
+ try { chrome.scripting.updateContentScripts(toUpdate, noopLastError); } catch (e) {
4550
+ // Ignore
4551
+ }
4552
+ }
4553
+ });
4554
+ } catch (e) {
4555
+ // Ignore
4556
+ }
4557
+ }
4558
+
4559
+ // Dev-loop reload without the CDP controller: content-scripts re-inject in
4560
+ // place into open tabs; service-worker/full/manifest restart the extension.
4561
+ function performDevReload(type, onDone) {
4562
+ var chrome = g.chrome;
4563
+ if (!chrome) return;
4564
+
4565
+ var fullReload = function () {
4566
+ // Flag a pending reinject for the NEXT producer generation: the
4567
+ // boot-time heal is what converges open tabs after a SW/full reload.
4568
+ try {
4569
+ if (chrome.storage && chrome.storage.local) {
4570
+ chrome.storage.local.set({__extjsDevPendingReinject: Date.now()}, noopLastError);
4571
+ }
4572
+ } catch (e) {
4573
+ // Ignore
4574
+ }
4575
+ // Deferred so in-flight frames and the tab console announcement flush
4576
+ // before the SW dies; the devtools companion confirms completion.
4577
+ try { setTimeout(function () { try { chrome.runtime.reload(); } catch (e) {
4578
+ // Ignore
4579
+ } }, 150); } catch (e) {}
4580
+ };
4581
+
4582
+ if (type === "content-scripts" && chrome.scripting && chrome.tabs && chrome.tabs.query) {
4583
+ if (reinjectContentScripts(onDone)) return;
4584
+ }
4585
+
4586
+ fullReload();
4587
+ }
4588
+
4589
+ // One server-built label travels the ReloadFrame; both announcement
4590
+ // surfaces fire HERE, next to the actual reload action, so they stay true.
4591
+
4592
+ // Stable IDs of the bundled extension-js-devtools companion: Chromium pins
4593
+ // via the manifest "key"; Firefox via browser_specific_settings.gecko.id.
4594
+ var DEVTOOLS_COMPANION_ID_CHROMIUM = "kgdaecdpfkikjncaalnmmnjjfpofkcbl";
4595
+ var DEVTOOLS_COMPANION_ID_FIREFOX = "devtools@extension.js";
4596
+
4597
+ function notifyDevtoolsCompanion(phase, label, kind) {
4598
+ try {
4599
+ var chrome = g.chrome;
4600
+ if (!chrome || !chrome.runtime || typeof chrome.runtime.sendMessage !== "function") return;
4601
+ var id = engineName() === "firefox" ? DEVTOOLS_COMPANION_ID_FIREFOX : DEVTOOLS_COMPANION_ID_CHROMIUM;
4602
+ chrome.runtime.sendMessage(
4603
+ id,
4604
+ {type: "extjs-dev-reload-state", phase: phase, label: label || "", kind: kind || "", instanceId: INSTANCE_ID},
4605
+ function () { noopLastError(); }
4606
+ );
4607
+ } catch (e) {
4608
+ // Ignore
4609
+ }
4610
+ }
4611
+
4612
+ // console.log the reload line into every open injectable tab; the patched
4613
+ // ISOLATED-world console also lands it in the dev-server log stream.
4614
+ function announceReloadInTabs(text) {
4615
+ var chrome = g.chrome;
4616
+ if (!chrome || !chrome.tabs || !chrome.tabs.query) return;
4617
+ try {
4618
+ chrome.tabs.query({}, function (tabs) {
4619
+ noopLastError();
4620
+ if (!tabs) return;
4621
+ for (var i = 0; i < tabs.length; i++) {
4622
+ (function (tab) {
4623
+ if (!tab || tab.id == null || !isInjectableUrl(tab.url)) return;
4624
+ if (chrome.scripting && chrome.scripting.executeScript) {
4625
+ try {
4626
+ chrome.scripting.executeScript(
4627
+ {target: {tabId: tab.id}, func: function (t) { console.log(t); }, args: [text]},
4628
+ noopLastError
4629
+ );
4630
+ return;
4631
+ } catch (e) {
4632
+ // Ignore
4633
+ }
4634
+ }
4635
+ // MV2 fallback (Firefox without the scripting API).
4636
+ if (chrome.tabs.executeScript) {
4637
+ try {
4638
+ chrome.tabs.executeScript(tab.id, {code: "console.log(" + JSON.stringify(text) + ");"}, noopLastError);
4639
+ } catch (e) {
4640
+ // Ignore
4641
+ }
4642
+ }
4643
+ })(tabs[i]);
4644
+ }
4645
+ });
4646
+ } catch (e) {
4647
+ // Ignore
4648
+ }
4649
+ }
4650
+
4651
+ function handleDevReloadFrame(frame) {
4652
+ var kind = frame.reloadType || "full";
4653
+ var label = typeof frame.label === "string" ? frame.label : "";
4654
+ // The server always builds the label; the kind-derived fallback only
4655
+ // covers a malformed frame so the announcement stays truthful.
4656
+ var fallback = kind === "content-scripts" ? "content_script"
4657
+ : kind === "service-worker" ? "service_worker"
4658
+ : "extension";
4659
+ var announced = "[Extension.js] Reloading " + (label || fallback) + "…";
4660
+
4661
+ notifyDevtoolsCompanion("reloading", label, kind);
4662
+
4663
+ if (kind === "page") {
4664
+ // Notify-only: rspack-dev-server's livereload refreshes the open
4665
+ // surface; reloading the extension here would race it.
4666
+ return;
4667
+ }
4668
+
4669
+ // Delivery ack for the broker: proves this SW's message pump processed
4670
+ // the frame (a socket write proves nothing). Sent on receipt, by design.
4671
+ send({type: "reload-ack", reloadType: kind, label: label});
4672
+
4673
+ announceReloadInTabs(announced);
4674
+ performDevReload(kind, function () {
4675
+ // Only the content-scripts path confirms from here; full/SW reloads
4676
+ // are confirmed by the devtools companion's chrome.management listeners.
4677
+ notifyDevtoolsCompanion("reloaded", label, kind);
4678
+ });
4679
+ }
4680
+
4681
+ function sanitize(args) {
4682
+ var out = [];
4683
+ for (var i = 0; i < args.length; i++) {
4684
+ var p = args[i];
4685
+ try {
4686
+ if (typeof p === "string") out.push(p.length > 2048 ? p.slice(0, 2048) + "..." : p);
4687
+ else if (p instanceof Error) out.push(p.name + ": " + p.message);
4688
+ else out.push(JSON.stringify(p));
4689
+ } catch (e) {
4690
+ out.push(String(p));
4691
+ }
4692
+ if (out.join(" ").length > 8192) break;
4693
+ }
4694
+ return out;
4695
+ }
4696
+
4697
+ function flush() {
4698
+ while (queue.length && open && socket) {
4699
+ var f = queue.shift();
4700
+ try { socket.send(JSON.stringify(f)); } catch (e) { break; }
4701
+ }
4702
+ }
4703
+
4704
+ function send(frame) {
4705
+ if (open && socket) {
4706
+ try { socket.send(JSON.stringify(frame)); return; } catch (e) {
4707
+ // Ignore
4708
+ }
4709
+ }
4710
+ if (queue.length < MAX_QUEUE) queue.push(frame);
4711
+ }
4712
+
4713
+ function schedule() {
4714
+ var delay = backoff;
4715
+ backoff = Math.min(backoff * 2, MAX_BACKOFF);
4716
+ try { setTimeout(connect, delay); } catch (e) {
4717
+ // Ignore
4718
+ }
4719
+ }
4720
+
4721
+ function connect() {
4722
+ try {
4723
+ socket = new WS("ws://" + HOST + ":" + PORT + "/extjs-control");
4724
+ } catch (e) {
4725
+ schedule();
4726
+ return;
4727
+ }
4728
+ socket.onopen = function () {
4729
+ open = true;
4730
+ backoff = 250;
4731
+ try {
4732
+ socket.send(JSON.stringify({type: "hello", v: 1, role: "producer", instanceId: INSTANCE_ID}));
4733
+ } catch (e) {
4734
+ // Ignore
4735
+ }
4736
+ flush();
4737
+ };
4738
+ socket.onmessage = function (ev) {
4739
+ var frame;
4740
+ try { frame = JSON.parse(typeof ev.data === "string" ? ev.data : ""); } catch (e) { return; }
4741
+ if (frame && frame.type === "command") {
4742
+ try { executeCommand(frame); } catch (e) { replyErr(frame.cmdId, "ExecutorError", e); }
4743
+ } else if (frame && frame.type === "reload") {
4744
+ // Dev-loop reload broadcast (see broker.broadcastReload).
4745
+ // Fire-and-forget: no result frame is expected.
4746
+ try { handleDevReloadFrame(frame); } catch (e) {
4747
+ // Ignore
4748
+ }
4749
+ } else if (frame && frame.type === "ping") {
4750
+ // Server keepalive: merely RECEIVING this resets the MV3 SW's ~30s
4751
+ // idle timer (Chrome 116+), keeping it reachable. Nothing to do.
4752
+ }
4753
+ };
4754
+ socket.onclose = function () {
4755
+ open = false;
4756
+ socket = null;
4757
+ schedule();
4758
+ };
4759
+ socket.onerror = function () {
4760
+ try { socket && socket.close(); } catch (e) {
4761
+ // Ignore
4762
+ }
4763
+ };
4764
+ }
4765
+
4766
+ // Uncaught errors don't route through the patched console; the handlers
4767
+ // below forward them, using these signatures to skip already-logged throws.
4768
+ var recentErrorSigs = [];
4769
+ function errorSig(message, stack) {
4770
+ return String(message == null ? "" : message) + "::" + String(stack == null ? "" : stack).slice(0, 200);
4771
+ }
4772
+ function noteErrorSig(sig) {
4773
+ recentErrorSigs.push({sig: sig, t: Date.now()});
4774
+ if (recentErrorSigs.length > 50) recentErrorSigs.shift();
4775
+ }
4776
+ function errorSigSeenRecently(sig) {
4777
+ var now = Date.now();
4778
+ for (var i = recentErrorSigs.length - 1; i >= 0; i--) {
4779
+ if (now - recentErrorSigs[i].t > 3000) { recentErrorSigs.splice(i, 1); continue; }
4780
+ if (recentErrorSigs[i].sig === sig) return true;
4781
+ }
4782
+ return false;
4783
+ }
4784
+
4785
+ LEVELS.forEach(function (level) {
4786
+ var orig = typeof consoleRef[level] === "function"
4787
+ ? consoleRef[level].bind(consoleRef)
4788
+ : function () {};
4789
+ consoleRef[level] = function () {
4790
+ try {
4791
+ if (level === "error") {
4792
+ for (var ai = 0; ai < arguments.length; ai++) {
4793
+ if (arguments[ai] instanceof Error) noteErrorSig(errorSig(arguments[ai].message, arguments[ai].stack));
4794
+ }
4795
+ }
4796
+ send({
4797
+ type: "log",
4798
+ event: {
4799
+ v: 1,
4800
+ id: nowId(),
4801
+ timestamp: Date.now(),
4802
+ level: level,
4803
+ context: CONTEXT,
4804
+ messageParts: sanitize([].slice.call(arguments)),
4805
+ runId: INSTANCE_ID
4806
+ }
4807
+ });
4808
+ } catch (e) {
4809
+ // Ignore
4810
+ }
4811
+ return orig.apply(consoleRef, arguments);
4812
+ };
4813
+ });
4814
+
4815
+ // Forward uncaught throws / unhandled rejections as level:"error", deduped
4816
+ // so a logged-then-rethrown Error emits once.
4817
+ function shipUncaughtError(message, stack, url) {
4818
+ try {
4819
+ var sig = errorSig(message, stack);
4820
+ if (errorSigSeenRecently(sig)) return;
4821
+ noteErrorSig(sig);
4822
+ send({
4823
+ type: "log",
4824
+ event: {
4825
+ v: 1,
4826
+ id: nowId(),
4827
+ timestamp: Date.now(),
4828
+ level: "error",
4829
+ context: CONTEXT,
4830
+ messageParts: sanitize([String(message) + (stack ? "\\n" + stack : "")]),
4831
+ url: url,
4832
+ runId: INSTANCE_ID
4833
+ }
4834
+ });
4835
+ } catch (e) {
4836
+ // Ignore
4837
+ }
4838
+ }
4839
+ try {
4840
+ if (typeof g.addEventListener === "function") {
4841
+ g.addEventListener("error", function (ev) {
4842
+ var err = ev && ev.error;
4843
+ var message = (err && err.message) || (ev && ev.message) || "Uncaught error";
4844
+ shipUncaughtError(message, err && err.stack, ev && ev.filename);
4845
+ });
4846
+ g.addEventListener("unhandledrejection", function (ev) {
4847
+ var reason = ev && ev.reason;
4848
+ var message = (reason && reason.message) || (reason != null ? String(reason) : "Unhandled rejection");
4849
+ shipUncaughtError("Unhandled promise rejection: " + message, reason && reason.stack, undefined);
4850
+ });
4851
+ }
4852
+ } catch (e) {
4853
+ // Ignore
4854
+ }
4855
+
4856
+ // Other contexts relay console logs to this SW over a NAMED runtime.Port:
4857
+ // they can't open the WS under page CSP, and sendMessage can echo-loop.
4858
+ function shipRelayedLog(ev, sender) {
4859
+ try {
4860
+ send({
4861
+ type: "log",
4862
+ event: {
4863
+ v: 1,
4864
+ id: nowId(),
4865
+ timestamp: Date.now(),
4866
+ level: ev.level || "log",
4867
+ context: ev.context || "content",
4868
+ messageParts: Array.isArray(ev.messageParts) ? ev.messageParts : [],
4869
+ url: ev.url || (sender ? sender.url : undefined),
4870
+ tabId: sender && sender.tab ? sender.tab.id : undefined,
4871
+ frameId: sender ? sender.frameId : undefined,
4872
+ runId: INSTANCE_ID
4873
+ }
4874
+ });
4875
+ } catch (e) {
4876
+ // Ignore
4877
+ }
4878
+ }
4879
+ try {
4880
+ var rtPort = g.chrome;
4881
+ if (rtPort && rtPort.runtime && rtPort.runtime.onConnect) {
4882
+ rtPort.runtime.onConnect.addListener(function (port) {
4883
+ if (!port || port.name !== "__extjs-bridge-log__") return;
4884
+ try {
4885
+ port.onMessage.addListener(function (msg) {
4886
+ if (!msg || !msg.__extjsBridgeLog) return;
4887
+ shipRelayedLog(msg.__extjsBridgeLog, port.sender);
4888
+ });
4889
+ } catch (e) {
4890
+ // Ignore
4891
+ }
4892
+ });
4893
+ }
4894
+ } catch (e) {
4895
+ // Ignore
4896
+ }
4897
+ // Legacy sendMessage path: kept for any surface still relaying the old way
4898
+ // (harmless one-predicate listener; new relays never use it).
4899
+ try {
4900
+ var rt = g.chrome;
4901
+ if (rt && rt.runtime && rt.runtime.onMessage) {
4902
+ rt.runtime.onMessage.addListener(function (msg, sender) {
4903
+ if (!msg || !msg.__extjsBridgeLog) return;
4904
+ shipRelayedLog(msg.__extjsBridgeLog, sender);
4905
+ });
4906
+ }
4907
+ } catch (e) {
4908
+ // Ignore
4909
+ }
4910
+
4911
+ // Chrome never injects manifest content scripts into already-open tabs, so
4912
+ // fire one reinject on onInstalled (which skips idle-stop SW wakes).
4913
+ try {
4914
+ var rtBoot = g.chrome;
4915
+ if (rtBoot && rtBoot.runtime && rtBoot.runtime.onInstalled) {
4916
+ rtBoot.runtime.onInstalled.addListener(function () {
4917
+ try {
4918
+ setTimeout(function () {
4919
+ try { reinjectContentScripts(); } catch (e) {
4920
+ // Ignore
4921
+ }
4922
+ }, 250);
4923
+ } catch (e) {
4924
+ // Ignore
4925
+ }
4926
+ });
4927
+ }
4928
+ } catch (e) {
4929
+ // Ignore
4930
+ }
4931
+
4932
+ // runtime.reload() does NOT fire onInstalled (verified Chromium 146), so
4933
+ // consume the pre-reload storage flag here (fresh only) to heal open tabs.
4934
+ try {
4935
+ var stBoot = g.chrome;
4936
+ if (stBoot && stBoot.storage && stBoot.storage.local) {
4937
+ stBoot.storage.local.get("__extjsDevPendingReinject", function (res) {
4938
+ noopLastError();
4939
+ var ts = res && res.__extjsDevPendingReinject;
4940
+ if (ts == null) return;
4941
+ try { stBoot.storage.local.remove("__extjsDevPendingReinject", noopLastError); } catch (e) {
4942
+ // Ignore
4943
+ }
4944
+ if (typeof ts !== "number" || Date.now() - ts > 30000) return;
4945
+ setTimeout(function () {
4946
+ try { reinjectContentScripts(); } catch (e) {
4947
+ // Ignore
4948
+ }
4949
+ }, 250);
4950
+ });
4951
+ }
4952
+ } catch (e) {
4953
+ // Ignore
4954
+ }
4955
+
4956
+ connect();
4957
+ } catch (e) {
4958
+ // Ignore
4959
+ }
4960
+ })();
4961
+ `;
4962
+ const BRIDGE_RELAY_SOURCE = `;(function () {
4963
+ try {
4964
+ var g = (typeof globalThis === "object" && globalThis) ? globalThis : this;
4965
+ if (!g || g.__extjsBridgeRelayInstalled) return;
4966
+
4967
+ var CONTEXT = "__EXTJS_CONTEXT__";
4968
+ var chrome = g.chrome;
4969
+ if (!chrome || !chrome.runtime) return;
4970
+ var canRelay = typeof chrome.runtime.connect === "function";
4971
+
4972
+ g.__extjsBridgeRelayInstalled = true;
4973
+ var consoleRef = g.console || {};
4974
+ var LEVELS = ["log", "info", "warn", "error", "debug", "trace"];
4975
+
4976
+ function sanitize(args) {
4977
+ var out = [];
4978
+ for (var i = 0; i < args.length; i++) {
4979
+ var p = args[i];
4980
+ try {
4981
+ if (typeof p === "string") out.push(p.length > 2048 ? p.slice(0, 2048) + "..." : p);
4982
+ else if (p instanceof Error) out.push(p.name + ": " + p.message);
4983
+ else out.push(JSON.stringify(p));
4984
+ } catch (e) { out.push(String(p)); }
4985
+ if (out.join(" ").length > 8192) break;
4986
+ }
4987
+ return out;
4988
+ }
4989
+
4990
+ function here() { try { return g.location ? g.location.href : undefined; } catch (e) { return undefined; } }
4991
+
4992
+ // Lazy named port to the SW producer: connecting wakes an idle SW, but
4993
+ // port frames never reach the extension's own onMessage listeners.
4994
+ var logPort = null;
4995
+ function getLogPort() {
4996
+ if (!canRelay) return null;
4997
+ if (logPort) return logPort;
4998
+ try {
4999
+ logPort = chrome.runtime.connect({name: "__extjs-bridge-log__"});
5000
+ logPort.onDisconnect.addListener(function () {
5001
+ try { void chrome.runtime.lastError; } catch (e) {
5002
+ // Ignore
5003
+ }
5004
+ logPort = null;
5005
+ });
5006
+ } catch (e) { logPort = null; }
5007
+ return logPort;
5008
+ }
5009
+
5010
+ // Relay one log payload to the SW producer over the named port, redialing
5011
+ // once if the port went stale (SW restarted).
5012
+ function postLog(payload) {
5013
+ var p = getLogPort();
5014
+ if (!p) return;
5015
+ try {
5016
+ p.postMessage({__extjsBridgeLog: payload});
5017
+ } catch (e) {
5018
+ logPort = null;
5019
+ var p2 = getLogPort();
5020
+ if (p2) { try { p2.postMessage({__extjsBridgeLog: payload}); } catch (e2) {
5021
+ // Ignore
5022
+ } }
5023
+ }
5024
+ }
5025
+
5026
+ // Uncaught throws / unhandled rejections never route through the patched
5027
+ // console; forward them, deduped against console.error Error signatures.
5028
+ var recentErrorSigs = [];
5029
+ function errorSig(message, stack) {
5030
+ return String(message == null ? "" : message) + "::" + String(stack == null ? "" : stack).slice(0, 200);
5031
+ }
5032
+ function noteErrorSig(sig) {
5033
+ recentErrorSigs.push({sig: sig, t: Date.now()});
5034
+ if (recentErrorSigs.length > 50) recentErrorSigs.shift();
5035
+ }
5036
+ function errorSigSeenRecently(sig) {
5037
+ var now = Date.now();
5038
+ for (var i = recentErrorSigs.length - 1; i >= 0; i--) {
5039
+ if (now - recentErrorSigs[i].t > 3000) { recentErrorSigs.splice(i, 1); continue; }
5040
+ if (recentErrorSigs[i].sig === sig) return true;
5041
+ }
5042
+ return false;
5043
+ }
5044
+
5045
+ LEVELS.forEach(function (level) {
5046
+ var orig = typeof consoleRef[level] === "function" ? consoleRef[level].bind(consoleRef) : function () {};
5047
+ consoleRef[level] = function () {
5048
+ try {
5049
+ if (level === "error") {
5050
+ for (var ai = 0; ai < arguments.length; ai++) {
5051
+ if (arguments[ai] instanceof Error) noteErrorSig(errorSig(arguments[ai].message, arguments[ai].stack));
5052
+ }
5053
+ }
5054
+ postLog({level: level, context: CONTEXT, messageParts: sanitize([].slice.call(arguments)), url: here()});
5055
+ } catch (e) {
5056
+ // Ignore
5057
+ }
5058
+ return orig.apply(consoleRef, arguments);
5059
+ };
5060
+ });
5061
+
5062
+ function shipUncaughtError(message, stack, url) {
5063
+ try {
5064
+ var sig = errorSig(message, stack);
5065
+ if (errorSigSeenRecently(sig)) return;
5066
+ noteErrorSig(sig);
5067
+ postLog({level: "error", context: CONTEXT, messageParts: sanitize([String(message) + (stack ? "\\n" + stack : "")]), url: url || here()});
5068
+ } catch (e) {
5069
+ // Ignore
5070
+ }
5071
+ }
5072
+ try {
5073
+ if (typeof g.addEventListener === "function") {
5074
+ g.addEventListener("error", function (ev) {
5075
+ var err = ev && ev.error;
5076
+ var message = (err && err.message) || (ev && ev.message) || "Uncaught error";
5077
+ shipUncaughtError(message, err && err.stack, (ev && ev.filename) || undefined);
5078
+ });
5079
+ g.addEventListener("unhandledrejection", function (ev) {
5080
+ var reason = ev && ev.reason;
5081
+ var message = (reason && reason.message) || (reason != null ? String(reason) : "Unhandled rejection");
5082
+ shipUncaughtError("Unhandled promise rejection: " + message, reason && reason.stack, undefined);
5083
+ });
5084
+ }
5085
+ } catch (e) {
5086
+ // Ignore
5087
+ }
5088
+
5089
+ // The SW executor can't reach another extension page's DOM or eval there,
5090
+ // so THIS surface answers inspect/eval requests for its own context.
5091
+ try {
5092
+ if (chrome.runtime && chrome.runtime.onMessage) {
5093
+ chrome.runtime.onMessage.addListener(function (msg, _sender, sendResponse) {
5094
+ if (!msg || !msg.__extjsEvalRequest) return;
5095
+ if (!msg.target || msg.target.context !== CONTEXT) return; // not for me
5096
+ try {
5097
+ var value = (0, eval)(String((msg.args && msg.args.expression) || ""));
5098
+ try { sendResponse({ok: true, value: value}); }
5099
+ catch (eSend) { sendResponse({ok: true, value: String(value)}); }
5100
+ } catch (e) {
5101
+ var emsg = (e && e.message) || String(e);
5102
+ if (/Content Security Policy|unsafe-eval|call to eval/i.test(emsg)) {
5103
+ sendResponse({ok: false, error: {name: "Unsupported", message: "eval of a string is blocked in the " + CONTEXT + " page by the MV3 extension CSP. Use extension inspect " + CONTEXT + " to read its DOM, or --context background on an MV2/Firefox build. Original: " + emsg}});
5104
+ } else {
5105
+ sendResponse({ok: false, error: {name: (e && e.name) || "EvalError", message: emsg}});
5106
+ }
5107
+ }
5108
+ return true; // responded
5109
+ });
5110
+ }
5111
+ } catch (e) {
5112
+ // Ignore
5113
+ }
5114
+
5115
+ try {
5116
+ if (chrome.runtime && chrome.runtime.onMessage) {
5117
+ chrome.runtime.onMessage.addListener(function (msg, _sender, sendResponse) {
5118
+ if (!msg || !msg.__extjsInspectRequest) return;
5119
+ if (!msg.target || msg.target.context !== CONTEXT) return; // not for me
5120
+ try {
5121
+ var args = msg.args || {};
5122
+ var wantHtml = !args.include || args.include.indexOf("html") !== -1;
5123
+ var cap = args.maxBytes || 262144;
5124
+ function countShadow(root) {
5125
+ var n = 0, els = root.querySelectorAll("*");
5126
+ for (var i = 0; i < els.length; i++) { if (els[i].shadowRoot) { n++; n += countShadow(els[i].shadowRoot); } }
5127
+ return n;
5128
+ }
5129
+ var doc = g.document;
5130
+ var roots = doc.querySelectorAll('#extension-root,[data-extension-root]');
5131
+ var snap = {
5132
+ context: CONTEXT,
5133
+ url: here(),
5134
+ title: doc.title,
5135
+ summary: {
5136
+ htmlLength: doc.documentElement.outerHTML.length,
5137
+ scriptCount: doc.querySelectorAll("script").length,
5138
+ styleCount: doc.querySelectorAll("style,link[rel=stylesheet]").length,
5139
+ extensionRootCount: roots.length,
5140
+ openShadowRoots: countShadow(doc),
5141
+ bodyChildCount: doc.body ? doc.body.children.length : 0
5142
+ }
5143
+ };
5144
+ if (wantHtml) {
5145
+ var html = doc.documentElement.outerHTML;
5146
+ if (cap > 0 && html.length > cap) { snap.html = html.slice(0, cap); snap.htmlTruncated = true; }
5147
+ else { snap.html = html; }
5148
+ }
5149
+ sendResponse({ok: true, value: snap});
5150
+ } catch (e) {
5151
+ sendResponse({ok: false, error: {name: "InspectError", message: String(e)}});
5152
+ }
5153
+ return true; // responded
5154
+ });
5155
+ }
5156
+ } catch (e) {
5157
+ // Ignore
5158
+ }
5159
+ } catch (e) {
5160
+ // Ignore
5161
+ }
5162
+ })();
5163
+ `;
5164
+ function buildBridgeRelaySource(opts) {
5165
+ return BRIDGE_RELAY_SOURCE.replace(/__EXTJS_CONTEXT__/g, String(opts.context || 'content'));
5166
+ }
5167
+ function buildBridgeProducerSource(opts) {
5168
+ if (!opts.controlPort || opts.controlPort < 1) return '';
5169
+ return BRIDGE_PRODUCER_SOURCE.replace(/__EXTJS_CONTROL_PORT__/g, String(opts.controlPort)).replace(/__EXTJS_INSTANCE_ID__/g, String(opts.instanceId)).replace(/__EXTJS_CONTEXT__/g, String(opts.context || 'background')).replace(/__EXTJS_CONTROL_HOST__/g, String(opts.host || '127.0.0.1'));
5170
+ }
5171
+ const BACKGROUND_ASSET = /(^|\/)background\/(?:service_worker|scripts?)\.js$/i;
5172
+ class InjectBridgeProducer {
5173
+ apply(compiler) {
5174
+ const controlPort = parseInt(String(process.env.EXTENSION_CONTROL_PORT || ''), 10);
5175
+ const instanceId = String(process.env.EXTENSION_INSTANCE_ID || '');
5176
+ const host = String(process.env.EXTENSION_DEV_SERVER_CONNECTABLE_HOST || '').trim() || '127.0.0.1';
5177
+ const source = buildBridgeProducerSource({
5178
+ controlPort: Number.isFinite(controlPort) ? controlPort : null,
5179
+ instanceId,
5180
+ context: 'background',
5181
+ host
5182
+ });
5183
+ if (!source) return;
5184
+ compiler.hooks.thisCompilation.tap(InjectBridgeProducer.name, (compilation)=>{
5185
+ compilation.hooks.processAssets.tap({
5186
+ name: InjectBridgeProducer.name,
5187
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT + 101
5188
+ }, ()=>{
5189
+ for (const asset of compilation.getAssets()){
5190
+ if (!BACKGROUND_ASSET.test(asset.name)) continue;
5191
+ const original = asset.source.source().toString();
5192
+ if (-1 !== original.indexOf('__extjsBridgeProducerInstalled')) continue;
5193
+ const next = `${source}\n${original}`;
5194
+ compilation.updateAsset(asset.name, new core_sources.RawSource(next));
5195
+ }
5196
+ });
5197
+ });
5198
+ }
5199
+ }
5200
+ const RELAY_TARGETS = [
5201
+ {
5202
+ re: /(^|\/)content_scripts\/.+\.js$/i,
5203
+ context: 'content'
5204
+ },
5205
+ {
5206
+ re: /(^|\/)action\/index\.js$/i,
5207
+ context: 'popup'
5208
+ },
5209
+ {
5210
+ re: /(^|\/)options\/index\.js$/i,
5211
+ context: 'options'
5212
+ },
5213
+ {
5214
+ re: /(^|\/)sidebar\/index\.js$/i,
5215
+ context: 'sidebar'
5216
+ },
5217
+ {
5218
+ re: /(^|\/)devtools\/index\.js$/i,
5219
+ context: 'devtools'
5220
+ },
5221
+ {
5222
+ re: /(^|\/)chrome_url_overrides\/newtab\.js$/i,
5223
+ context: 'newtab'
5224
+ },
5225
+ {
5226
+ re: /(^|\/)chrome_url_overrides\/history\.js$/i,
5227
+ context: 'history'
5228
+ },
5229
+ {
5230
+ re: /(^|\/)chrome_url_overrides\/bookmarks\.js$/i,
5231
+ context: 'bookmarks'
5232
+ }
5233
+ ];
5234
+ class InjectBridgeRelay {
5235
+ apply(compiler) {
5236
+ const controlPort = parseInt(String(process.env.EXTENSION_CONTROL_PORT || ''), 10);
5237
+ if (!Number.isFinite(controlPort) || controlPort < 1) return;
5238
+ const sourceFor = new Map();
5239
+ for (const t of RELAY_TARGETS)if (!sourceFor.has(t.context)) sourceFor.set(t.context, buildBridgeRelaySource({
5240
+ context: t.context
5241
+ }));
5242
+ compiler.hooks.thisCompilation.tap(InjectBridgeRelay.name, (compilation)=>{
5243
+ compilation.hooks.processAssets.tap({
5244
+ name: InjectBridgeRelay.name,
5245
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT + 101
5246
+ }, ()=>{
5247
+ for (const asset of compilation.getAssets()){
5248
+ const target = RELAY_TARGETS.find((t)=>t.re.test(asset.name));
5249
+ if (!target) continue;
5250
+ const original = asset.source.source().toString();
5251
+ if (-1 !== original.indexOf('__extjsBridgeRelayInstalled')) continue;
5252
+ const next = `${sourceFor.get(target.context)}\n${original}`;
5253
+ compilation.updateAsset(asset.name, new core_sources.RawSource(next));
5254
+ }
5255
+ });
5256
+ });
5257
+ }
5258
+ }
5259
+ const SCRIPTS_REPLAY_SHIM_SOURCE = `;(function () {
5260
+ try {
5261
+ if (typeof globalThis !== "object" || !globalThis) return;
5262
+ var chromeRef =
5263
+ (globalThis.chrome && globalThis.chrome.scripting && typeof globalThis.chrome.scripting.executeScript === "function")
5264
+ ? globalThis.chrome
5265
+ : (globalThis.browser && globalThis.browser.scripting && typeof globalThis.browser.scripting.executeScript === "function")
5266
+ ? globalThis.browser
5267
+ : null;
5268
+ if (!chromeRef) return;
5269
+ if (globalThis.__extjsScriptsReplayInstalled) return;
5270
+ globalThis.__extjsScriptsReplayInstalled = true;
5271
+
5272
+ var registry = new Map();
5273
+ var originalExecuteScript = chromeRef.scripting.executeScript.bind(chromeRef.scripting);
5274
+
5275
+ var serialize = function (entry) {
5276
+ try {
5277
+ return JSON.stringify({
5278
+ files: entry && Array.isArray(entry.files) ? entry.files : [],
5279
+ world: entry && entry.world ? String(entry.world) : ""
5280
+ });
5281
+ } catch (error) {
5282
+ return "";
5283
+ }
5284
+ };
5285
+
5286
+ var track = function (injection) {
5287
+ try {
5288
+ var tabId = injection && injection.target && injection.target.tabId;
5289
+ var files =
5290
+ injection && Array.isArray(injection.files) ? injection.files.slice() : null;
5291
+ if (typeof tabId !== "number") return;
5292
+ if (!files || !files.length) return;
5293
+ var world = injection.world ? String(injection.world) : undefined;
5294
+ var entry = { files: files, world: world, sig: serialize({ files: files, world: world }) };
5295
+ var existing = registry.get(tabId) || [];
5296
+ // Dedupe by signature, repeated identical injections (e.g. user
5297
+ // clicks the action twice) shouldn't stack in the replay list.
5298
+ if (existing.some(function (e) { return e.sig === entry.sig; })) return;
5299
+ existing.push(entry);
5300
+ // Cap to the most recent 50 distinct injections per tab so a misbehaving
5301
+ // user loop can't grow the registry unbounded.
5302
+ registry.set(tabId, existing.slice(-50));
5303
+ } catch (error) {
5304
+ // Tracking is best-effort; never break the user's executeScript call.
5305
+ }
5306
+ };
5307
+
5308
+ chromeRef.scripting.executeScript = function (injection, callback) {
5309
+ track(injection);
5310
+ return originalExecuteScript(injection, callback);
5311
+ };
5312
+
5313
+ var normalizeFile = function (value) {
5314
+ return String(value || "").replace(/^[/\\\\]+/, "");
5315
+ };
5316
+
5317
+ var fileMatches = function (entryFile, changedNormalized) {
5318
+ var fn = normalizeFile(entryFile);
5319
+ for (var i = 0; i < changedNormalized.length; i++) {
5320
+ var c = changedNormalized[i];
5321
+ if (!c) continue;
5322
+ if (fn === c) return true;
5323
+ if (fn.length > c.length && fn.slice(fn.length - c.length - 1) === "/" + c) return true;
5324
+ if (c.length > fn.length && c.slice(c.length - fn.length - 1) === "/" + fn) return true;
5325
+ }
5326
+ return false;
5327
+ };
5328
+
5329
+ globalThis.__extjsScriptsReplay = function (changedFiles) {
5330
+ var changedNormalized = (Array.isArray(changedFiles) ? changedFiles : []).map(normalizeFile);
5331
+ var promises = [];
5332
+ registry.forEach(function (entries, tabId) {
5333
+ entries.forEach(function (entry) {
5334
+ var matches = false;
5335
+ for (var i = 0; i < entry.files.length; i++) {
5336
+ if (fileMatches(entry.files[i], changedNormalized)) {
5337
+ matches = true;
5338
+ break;
5339
+ }
5340
+ }
5341
+ if (!matches) return;
5342
+ try {
5343
+ promises.push(
5344
+ originalExecuteScript({
5345
+ target: { tabId: tabId },
5346
+ files: entry.files,
5347
+ world: entry.world
5348
+ }).then(
5349
+ function () { return { ok: true, tabId: tabId, files: entry.files }; },
5350
+ function (error) {
5351
+ return {
5352
+ ok: false,
5353
+ tabId: tabId,
5354
+ files: entry.files,
5355
+ error: String((error && error.message) || error)
5356
+ };
5357
+ }
5358
+ )
5359
+ );
5360
+ } catch (error) {
5361
+ // Tab may have closed; skip silently.
5362
+ }
5363
+ });
5364
+ });
5365
+ return Promise.all(promises);
5366
+ };
5367
+ } catch (error) {
5368
+ // Best-effort shim; do not break the SW bootstrap on any failure.
5369
+ }
5370
+ })();
5371
+ `;
5372
+ const inject_scripts_replay_shim_BACKGROUND_ASSET = /(^|\/)background\/(?:service_worker|script)\.js$/i;
5373
+ class InjectScriptsReplayShim {
5374
+ apply(compiler) {
5375
+ compiler.hooks.thisCompilation.tap(InjectScriptsReplayShim.name, (compilation)=>{
5376
+ compilation.hooks.processAssets.tap({
5377
+ name: InjectScriptsReplayShim.name,
5378
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT + 100
5379
+ }, ()=>{
5380
+ for (const asset of compilation.getAssets()){
5381
+ if (!inject_scripts_replay_shim_BACKGROUND_ASSET.test(asset.name)) continue;
5382
+ const original = asset.source.source().toString();
5383
+ if (-1 !== original.indexOf('__extjsScriptsReplayInstalled')) continue;
5384
+ const next = `${SCRIPTS_REPLAY_SHIM_SOURCE}\n${original}`;
5385
+ compilation.updateAsset(asset.name, new core_sources.RawSource(next));
5386
+ }
5387
+ });
5388
+ });
5389
+ }
5390
+ }
5391
+ const HOT_DIR = 'hot';
5392
+ class PruneStaleHotUpdates {
5393
+ static name = 'plugin-reload:prune-stale-hot-updates';
5394
+ previousGeneration = new Set();
5395
+ apply(compiler) {
5396
+ if (!compiler?.hooks?.done?.tap) return;
5397
+ compiler.hooks.done.tap(PruneStaleHotUpdates.name, (stats)=>{
5398
+ try {
5399
+ const outputPath = compiler.options.output?.path;
5400
+ if (!outputPath) return;
5401
+ const hotDir = __rspack_external_node_path_c5b9b54f.join(outputPath, HOT_DIR);
5402
+ if (!__rspack_external_node_fs_5ea92f0c.existsSync(hotDir)) return;
5403
+ const emitted = new Set();
5404
+ for (const asset of stats.compilation.getAssets()){
5405
+ const name = String(asset.name || '').replace(/\\/g, '/');
5406
+ if (name.startsWith(`${HOT_DIR}/`)) emitted.add(name.slice(HOT_DIR.length + 1));
5407
+ }
5408
+ const keep = new Set([
5409
+ ...emitted,
5410
+ ...this.previousGeneration
5411
+ ]);
5412
+ const pruneDir = (dir, relPrefix)=>{
5413
+ for (const entry of __rspack_external_node_fs_5ea92f0c.readdirSync(dir, {
5414
+ withFileTypes: true
5415
+ })){
5416
+ const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
5417
+ const absolute = __rspack_external_node_path_c5b9b54f.join(dir, entry.name);
5418
+ if (entry.isDirectory()) {
5419
+ pruneDir(absolute, rel);
5420
+ try {
5421
+ if (0 === __rspack_external_node_fs_5ea92f0c.readdirSync(absolute).length) __rspack_external_node_fs_5ea92f0c.rmdirSync(absolute);
5422
+ } catch {}
5423
+ continue;
5424
+ }
5425
+ if (!keep.has(rel)) try {
5426
+ __rspack_external_node_fs_5ea92f0c.rmSync(absolute, {
5427
+ force: true
5428
+ });
5429
+ } catch {}
5430
+ }
5431
+ };
5432
+ pruneDir(hotDir, '');
5433
+ this.previousGeneration = emitted;
5434
+ } catch {}
5435
+ });
5436
+ }
5437
+ }
5438
+ function createIssue(compiler, message, type = 'error') {
5439
+ const ErrorCtor = compiler?.rspack?.WebpackError || Error;
5440
+ const issue = new ErrorCtor(message);
5441
+ issue.name = 'warning' === type ? 'ExtensionWarning' : 'ExtensionError';
5442
+ return issue;
5443
+ }
5444
+ function reportToCompilation(compilation, compiler, message, type = 'error', file) {
5445
+ const issue = createIssue(compiler, message, type);
5446
+ if (file) issue.file = file;
5447
+ const bucket = 'warning' === type ? 'warnings' : 'errors';
5448
+ compilation[bucket] ||= [];
5449
+ const existing = compilation[bucket];
5450
+ const already = existing.some((e)=>(e?.file || '') === (issue.file || '') && String(e?.message) === String(issue.message));
5451
+ if (already) return;
5452
+ compilation[bucket].push(issue);
5453
+ }
5454
+ function backgroundIsRequiredMessageOnly(backgroundChunkName) {
5455
+ return `Check the ${pintor.yellow(backgroundChunkName.replace('/', '.'))} field in your ${pintor.yellow('manifest.json')} file.`;
5456
+ }
5457
+ class SetupBackgroundEntry {
5458
+ manifestPath;
5459
+ browser;
5460
+ constructor(options){
5461
+ this.manifestPath = options.manifestPath;
5462
+ this.browser = options.browser || 'chrome';
5463
+ }
5464
+ getMissingBackgroundError(bgFile) {
5465
+ if (!__rspack_external_node_fs_5ea92f0c.existsSync(bgFile) && this.manifestPath) {
5466
+ const manifest = JSON.parse(stripBom(__rspack_external_node_fs_5ea92f0c.readFileSync(this.manifestPath, 'utf8')));
5467
+ const patched = filterKeysForThisBrowser(manifest, this.browser);
5468
+ const fieldKey = 3 === patched.manifest_version ? 'background/service_worker' : "background/scripts";
5469
+ return backgroundIsRequiredMessageOnly(fieldKey);
5470
+ }
5471
+ }
5472
+ addDefaultEntry(compiler, name, defaultScript) {
5473
+ compiler.options.entry = {
5474
+ ...compiler.options.entry,
5475
+ [name]: {
5476
+ import: [
5477
+ defaultScript
5478
+ ]
5479
+ }
5480
+ };
5481
+ }
5482
+ apply(compiler) {
5483
+ const manifest = JSON.parse(stripBom(__rspack_external_node_fs_5ea92f0c.readFileSync(this.manifestPath, 'utf-8')));
5484
+ const browser = this.browser;
5485
+ const isGecko = isGeckoBasedBrowser(String(browser));
5486
+ const minimumBgScript = resolveDevelopDistFile(isGecko ? 'minimum-firefox-file' : 'minimum-chromium-file');
5487
+ const dirname = __rspack_external_node_path_c5b9b54f.dirname(this.manifestPath);
5488
+ const filteredManifest = filterKeysForThisBrowser(manifest, browser) || manifest;
5489
+ const manifestBg = filteredManifest?.background ?? manifest.background;
5490
+ const manifestVersion = filteredManifest?.manifest_version ?? manifest.manifest_version;
5491
+ if (isStaticTheme(filteredManifest)) return;
5492
+ function hookError(maybeError) {
5493
+ compiler.hooks.thisCompilation.tap('run-chromium:setup-background-entry', (compilation)=>{
5494
+ reportToCompilation(compilation, compiler, maybeError, 'error');
5495
+ });
5496
+ }
5497
+ if (isGecko) {
5498
+ const bgScripts = manifestBg?.scripts;
5499
+ if (bgScripts && bgScripts.length > 0) {
5500
+ const bgScriptPath = __rspack_external_node_path_c5b9b54f.join(dirname, bgScripts[0]);
5501
+ const maybeError = this.getMissingBackgroundError(bgScriptPath);
5502
+ if (maybeError) hookError(maybeError);
5503
+ } else this.addDefaultEntry(compiler, "background/script", minimumBgScript);
5504
+ return;
5505
+ }
5506
+ if (2 === manifestVersion) {
5507
+ const bgScripts = manifestBg?.scripts;
5508
+ if (bgScripts && bgScripts.length > 0) {
5509
+ const bgScriptPath = __rspack_external_node_path_c5b9b54f.join(dirname, bgScripts[0]);
5510
+ const maybeError = this.getMissingBackgroundError(bgScriptPath);
5511
+ if (maybeError) hookError(maybeError);
5512
+ } else this.addDefaultEntry(compiler, "background/script", minimumBgScript);
5513
+ } else {
5514
+ const serviceWorker = manifestBg?.service_worker;
5515
+ if (serviceWorker) {
5516
+ const swPath = __rspack_external_node_path_c5b9b54f.join(dirname, serviceWorker);
5517
+ const maybeError = this.getMissingBackgroundError(swPath);
5518
+ if (maybeError) hookError(maybeError);
5519
+ const existingEntry = compiler.options.entry && 'background/service_worker' in compiler.options.entry ? compiler.options.entry['background/service_worker'] : void 0;
5520
+ if (!existingEntry && __rspack_external_node_fs_5ea92f0c.existsSync(swPath)) this.addDefaultEntry(compiler, 'background/service_worker', swPath);
5521
+ } else this.addDefaultEntry(compiler, 'background/service_worker', minimumBgScript);
5522
+ }
5523
+ }
5524
+ }
5525
+ function TemplateFn(compilation, Template) {
5526
+ return {
5527
+ f: (args, body)=>{
5528
+ if (!compilation) throw new TypeError('No compilation is found.');
5529
+ if (compilation.runtimeTemplate) return compilation.runtimeTemplate.basicFunction(args, body);
5530
+ return compilation.outputOptions.environment.arrowFunction ? `(${args}) => {\n${Template.indent(body)}\n}` : `function(${args}) {\n${Template.indent(body)}\n}`;
5531
+ },
5532
+ retF: (returnValue, args = '')=>{
5533
+ if (!compilation) throw new TypeError('No compilation is found.');
5534
+ if (compilation.runtimeTemplate) return compilation.runtimeTemplate.returningFunction(returnValue, args);
5535
+ return compilation.outputOptions.environment.arrowFunction ? `(${args}) => (${returnValue})` : `function(${args}) { return ${returnValue}; }`;
5536
+ }
5537
+ };
5538
+ }
5539
+ const RuntimeGlobal = '__webpack_require__.webExtRt';
5540
+ const RuntimeGlobalIsBrowser = '__webpack_require__.webExtRtModern';
5541
+ function BrowserRuntimeModule(webpack, acceptWeak) {
5542
+ const { RuntimeModule, Template } = webpack;
5543
+ class BrowserRuntime extends RuntimeModule {
5544
+ constructor(){
5545
+ super('WebExtensionBrowserRuntime', RuntimeModule.STAGE_NORMAL);
5546
+ }
5547
+ generate() {
5548
+ const { compilation } = this;
5549
+ if (!compilation) return Template.asString('/* [webpack-target-webextension] BrowserRuntimeModule skipped because no compilation is found. */');
5550
+ const optionalChaining = compilation.outputOptions.environment.optionalChaining;
5551
+ const _let = compilation.outputOptions.environment.const ? 'let' : 'var';
5552
+ return Template.asString([
5553
+ `${_let} isChrome, runtime;`,
5554
+ 'try {',
5555
+ Template.indent([
5556
+ `if (typeof globalThis === "object" && globalThis && typeof globalThis.browser !== "undefined" && ${optionalChaining ? 'typeof globalThis.browser.runtime?.getURL === "function"' : 'typeof globalThis.browser.runtime === "object" && typeof globalThis.browser.runtime.getURL === "function"'}) {`,
5557
+ Template.indent([
5558
+ 'runtime = globalThis.browser;'
5559
+ ]),
5560
+ '}'
5561
+ ]),
5562
+ '} catch (_) {}',
5563
+ 'if (!runtime) {',
5564
+ Template.indent([
5565
+ 'try {',
5566
+ Template.indent([
5567
+ `if (typeof globalThis === "object" && globalThis && typeof globalThis.chrome !== "undefined" && ${optionalChaining ? 'typeof globalThis.chrome.runtime?.getURL === "function"' : 'typeof globalThis.chrome.runtime === "object" && typeof globalThis.chrome.runtime.getURL === "function"'}) {`,
5568
+ Template.indent([
5569
+ 'isChrome = true;',
5570
+ 'runtime = globalThis.chrome;'
5571
+ ]),
5572
+ '}'
5573
+ ]),
5574
+ '} catch (_) {}'
5575
+ ]),
5576
+ '}',
5577
+ `${RuntimeGlobalIsBrowser} = !isChrome;`,
5578
+ `${RuntimeGlobal} = runtime || { runtime: null };`,
5579
+ acceptWeak ? 'if (!runtime && (typeof self !== "object" || !self.addEventListener)) {' : '',
5580
+ acceptWeak ? Template.indent([
5581
+ `${RuntimeGlobal} = { runtime: { getURL: String } };`
5582
+ ]) : '',
5583
+ acceptWeak ? '}' : ''
5584
+ ].filter(Boolean));
5585
+ }
5586
+ }
5587
+ return new BrowserRuntime();
5588
+ }
5589
+ const DYNAMIC_IMPORT_LOADER = 'dynamicImportLoader';
5590
+ const DOM_LOADER = "scriptLoader";
5591
+ const WORKER_LOADER = 'workerLoader';
5592
+ const CLASSIC_LOADER = 'classicLoader';
5593
+ const MAIN_CLASSIC_LOADER = 'classicLoaderMainWorld';
5594
+ const FALLBACK_LOADER = 'fallbackLoader';
5595
+ function LoadScriptRuntimeModule(webpack, supportDynamicImport, classicLoaderEnabled, contentScriptsMeta) {
5596
+ const { Template, RuntimeGlobals, RuntimeModule } = webpack;
5597
+ class LoadScriptRuntime extends RuntimeModule {
5598
+ supportDynamicImport;
5599
+ classicLoaderEnabled;
5600
+ contentScriptsMeta;
5601
+ constructor(){
5602
+ super("load script", RuntimeModule.STAGE_BASIC);
5603
+ this.supportDynamicImport = Boolean(supportDynamicImport);
5604
+ this.classicLoaderEnabled = Boolean(classicLoaderEnabled);
5605
+ this.contentScriptsMeta = contentScriptsMeta || {};
5606
+ }
5607
+ generate() {
5608
+ const { compilation } = this;
5609
+ if (!compilation) return Template.asString('/* [webpack-target-webextension] ChunkLoaderFallbackRuntimeModule skipped because no compilation is found. */');
5610
+ const { f } = TemplateFn(compilation, Template);
5611
+ const _const = compilation.outputOptions.environment.const ? 'const' : 'var';
5612
+ const _let = compilation.outputOptions.environment.const ? 'let' : 'var';
5613
+ const chunkName = (this.chunk?.name ? this.chunk.name : '') || (compilation && compilation.chunk && compilation.chunk.name ? compilation.chunk.name : '');
5614
+ const bundleId = chunkName ? `${chunkName}.js` : '';
5615
+ const world = bundleId && this.contentScriptsMeta[bundleId] ? this.contentScriptsMeta[bundleId].world : void 0;
5616
+ const isMainWorld = 'main' === world;
5617
+ const HasExtensionRuntime = `${_const} hasExtensionRuntime = (function(){ try {return ((typeof globalThis === "object" && globalThis && globalThis.browser && globalThis.browser.runtime && typeof globalThis.browser.runtime.sendMessage === "function") || (typeof globalThis === "object" && globalThis && globalThis.chrome && globalThis.chrome.runtime && typeof globalThis.chrome.runtime.sendMessage === "function"));} catch (e) { return false; } })();`;
5618
+ const DynamicImportLoader = `${_const} ${DYNAMIC_IMPORT_LOADER} = ` + f('url, done, key, chunkId', `import(url).then(${f('', [
5619
+ 'if (isNotIframe) return done();',
5620
+ 'try {',
5621
+ Template.indent([
5622
+ "// It's a Chrome bug, if the import() is called in a sandboxed iframe, it _fails_ the script loading but _resolve_ the Promise.",
5623
+ `// we call ${RuntimeGlobals.ensureChunkHandlers}.j(chunkId) to check if it is loaded.`,
5624
+ '// if it is, this is a no-op. if it is not, it will throw a TypeError because this function requires 2 parameters.',
5625
+ '// This call will not trigger the chunk loading because it is already loading.',
5626
+ '// see https://github.com/awesome-webextension/webpack-target-webextension/issues/41',
5627
+ `chunkId !== undefined && ${RuntimeGlobals.ensureChunkHandlers}.j(chunkId);`,
5628
+ 'done();'
5629
+ ]),
5630
+ '}',
5631
+ 'catch (_) {',
5632
+ Template.indent([
5633
+ 'if (!bug816121warned) {',
5634
+ Template.indent([
5635
+ 'console.warn("Chrome bug https://crbug.com/816121 hit.");',
5636
+ 'bug816121warned = true;'
5637
+ ]),
5638
+ '}',
5639
+ `return ${FALLBACK_LOADER}(url, done, key, chunkId);`
5640
+ ]),
5641
+ '}'
5642
+ ])}, ${f('e', [
5643
+ 'console.warn("Dynamic import loader failed. Using fallback loader (see https://github.com/awesome-webextension/webpack-target-webextension#content-script).", e);',
5644
+ `${FALLBACK_LOADER}(url, done, key, chunkId);`
5645
+ ])});`);
5646
+ const DOMLoader = `${_const} ${DOM_LOADER} = ` + f('url, done', [
5647
+ `${_const} script = document.createElement('script');`,
5648
+ "script.src = url;",
5649
+ "script.onload = done;",
5650
+ "script.onerror = done;",
5651
+ "document.body.appendChild(script);"
5652
+ ]);
5653
+ const WorkerLoader = `${_const} ${WORKER_LOADER} = ` + f('url, done', [
5654
+ 'try {',
5655
+ Template.indent([
5656
+ 'importScripts(url);',
5657
+ 'done();'
5658
+ ]),
5659
+ '} catch (e) {',
5660
+ Template.indent([
5661
+ 'done(e);'
5662
+ ]),
5663
+ '}'
5664
+ ]);
5665
+ const ClassicLoader = `${_const} ${CLASSIC_LOADER} = ` + f('url, done', Template.asString([
5666
+ `${_const} msg = { type: "WTW_INJECT", file: url };`,
5667
+ `${_const} onError = ${f('e', 'done(Object.assign(e, { type: "missing" }))')};`,
5668
+ 'try {',
5669
+ `if (${RuntimeGlobalIsBrowser}) {`,
5670
+ Template.indent([
5671
+ `${RuntimeGlobal}.runtime.sendMessage(msg).then(done, onError);`
5672
+ ]),
5673
+ '} else {',
5674
+ Template.indent([
5675
+ `${RuntimeGlobal}.runtime.sendMessage(msg, ${f('', [
5676
+ `${_const} error = ${RuntimeGlobal}.runtime.lastError;`,
5677
+ 'if (error) onError(error);',
5678
+ 'else done();'
5679
+ ])});`
5680
+ ]),
5681
+ '}',
5682
+ '} catch (e) { onError(e); }'
5683
+ ])) + ';';
5684
+ const ClassicLoaderDisabled = `${_const} ${CLASSIC_LOADER} = ` + f('', [
5685
+ "throw new Error(\"[webpack-target-webextension] Failed to load async chunk in the content script. No script loader is found. You can either\\n - Set output.environment.dynamicImport to true if your environment supports native ES Module\\n - Specify the background entry to enable the fallback loader\\n - Set module.parser.javascript.dynamicImportMode to 'eager' to inline all async chunks.\");"
5686
+ ]);
5687
+ return Template.asString([
5688
+ this.supportDynamicImport ? `${_let} bug816121warned, isNotIframe;` : '',
5689
+ this.supportDynamicImport ? Template.asString([
5690
+ 'try {',
5691
+ Template.indent([
5692
+ 'isNotIframe = typeof window === "object" ? window.top === window : true;'
5693
+ ]),
5694
+ '} catch(e) {',
5695
+ Template.indent([
5696
+ 'isNotIframe = false /* CORS error */;'
5697
+ ]),
5698
+ '}'
5699
+ ]) : '',
5700
+ HasExtensionRuntime,
5701
+ this.classicLoaderEnabled ? ClassicLoader : ClassicLoaderDisabled,
5702
+ this.supportDynamicImport ? DynamicImportLoader : '',
5703
+ DOMLoader,
5704
+ WorkerLoader,
5705
+ `${_const} isWorker = typeof importScripts === 'function'`,
5706
+ "if (typeof location === 'object' && location.protocol.includes('-extension:')) {",
5707
+ Template.indent([
5708
+ `${RuntimeGlobals.loadScript} = isWorker ? ${WORKER_LOADER} : ${DOM_LOADER};`
5709
+ ]),
5710
+ "} else {",
5711
+ Template.indent(isMainWorld ? Template.asString([
5712
+ `${_const} __extjsMark = "__extjs__";`,
5713
+ `${_const} __extjsReqType = "EXTJS_WTW_LOAD";`,
5714
+ `${_const} __extjsResType = "EXTJS_WTW_LOADED";`,
5715
+ `${_const} ${MAIN_CLASSIC_LOADER} = ` + f('url, done', [
5716
+ `${_const} requestId = String(Date.now()) + "-" + Math.random().toString(16).slice(2);`,
5717
+ `${_const} onMessage = ${f('event', [
5718
+ 'try {',
5719
+ Template.indent([
5720
+ 'if (!event || event.source !== window) return;',
5721
+ `${_const} data = event.data || null;`,
5722
+ "if (!data || data[__extjsMark] !== true) return;",
5723
+ "if (data.type !== __extjsResType || data.requestId !== requestId) return;",
5724
+ 'window.removeEventListener("message", onMessage);',
5725
+ 'if (data.ok) done();',
5726
+ 'else done(Object.assign(new Error(data.error || "Bridge failed"), { type: "missing" }));'
5727
+ ]),
5728
+ '} catch (e) {',
5729
+ Template.indent([
5730
+ 'window.removeEventListener("message", onMessage);',
5731
+ 'done(Object.assign(e, { type: "missing" }));'
5732
+ ]),
5733
+ '}'
5734
+ ])};`,
5735
+ 'window.addEventListener("message", onMessage);',
5736
+ 'try {',
5737
+ Template.indent([
5738
+ 'window.postMessage({ [__extjsMark]: true, type: __extjsReqType, requestId: requestId, url: url }, "*");'
5739
+ ]),
5740
+ '} catch (e) {',
5741
+ Template.indent([
5742
+ 'window.removeEventListener("message", onMessage);',
5743
+ 'done(Object.assign(e, { type: "missing" }));'
5744
+ ]),
5745
+ '}',
5746
+ 'setTimeout(function(){ try { window.removeEventListener("message", onMessage); } catch(_){} done(Object.assign(new Error("Bridge timeout"), { type: "missing" })); }, 5000);'
5747
+ ]) + ';',
5748
+ `if (!isWorker) ${RuntimeGlobals.loadScript} = ${MAIN_CLASSIC_LOADER};`
5749
+ ]) : Template.asString([
5750
+ `if (!isWorker && hasExtensionRuntime) ${RuntimeGlobals.loadScript} = ${CLASSIC_LOADER};`,
5751
+ `else if (!isWorker) ${RuntimeGlobals.loadScript} = ${DOM_LOADER};`
5752
+ ])),
5753
+ "}",
5754
+ this.supportDynamicImport ? `${_const} ${FALLBACK_LOADER} = ${RuntimeGlobals.loadScript};` : '',
5755
+ this.supportDynamicImport ? `${RuntimeGlobals.loadScript} = ${DYNAMIC_IMPORT_LOADER};` : ''
5756
+ ].filter(Boolean));
5757
+ }
5758
+ }
5759
+ return new LoadScriptRuntime();
5760
+ }
5761
+ function PublicPath_PublicPathRuntimeModule(webpack) {
5762
+ const { RuntimeGlobals, RuntimeModule, Template } = webpack;
5763
+ class PublicPathRuntime extends RuntimeModule {
5764
+ constructor(){
5765
+ super('publicPath', RuntimeModule.STAGE_BASIC);
5766
+ }
5767
+ generate() {
5768
+ const { compilation } = this;
5769
+ if (!compilation) return Template.asString('/* [webpack-target-webextension] PublicPathRuntimeModule skipped because no compilation is found. */');
5770
+ const { publicPath } = compilation.outputOptions;
5771
+ const path = JSON.stringify(compilation.getPath(publicPath || '', {
5772
+ hash: compilation.hash || 'XXXX'
5773
+ }));
5774
+ return Template.asString([
5775
+ 'var __extjsBase = (typeof globalThis === "object" && globalThis && globalThis.__EXTJS_EXTENSION_BASE__) ? String(globalThis.__EXTJS_EXTENSION_BASE__) : "";',
5776
+ 'if (!__extjsBase && typeof document === "object" && document && document.documentElement) {',
5777
+ Template.indent([
5778
+ 'try { __extjsBase = document.documentElement.getAttribute("data-extjs-extension-base") || ""; } catch(_) { __extjsBase = ""; }'
5779
+ ]),
5780
+ "}",
5781
+ 'var __extjsRuntimePath = "";',
5782
+ `if (${RuntimeGlobal} && ${RuntimeGlobal}.runtime && typeof ${RuntimeGlobal}.runtime.getURL === "function") {`,
5783
+ Template.indent([
5784
+ `try { __extjsRuntimePath = ${RuntimeGlobal}.runtime.getURL(${path}); } catch (_) { __extjsRuntimePath = ""; }`
5785
+ ]),
5786
+ "}",
5787
+ "if (__extjsRuntimePath) {",
5788
+ Template.indent([
5789
+ `${RuntimeGlobals.publicPath} = __extjsRuntimePath;`
5790
+ ]),
5791
+ "} else if (__extjsBase) {",
5792
+ Template.indent([
5793
+ `${RuntimeGlobals.publicPath} = __extjsBase.replace(/\\/+$/, "/") + String(${path}).replace(/^\\/+/, "");`
5794
+ ]),
5795
+ "} else {",
5796
+ Template.indent([
5797
+ `${RuntimeGlobals.publicPath} = "";`
5798
+ ]),
5799
+ "}",
5800
+ 'if (!__extjsBase && typeof document === "object" && document && document.documentElement) {',
5801
+ Template.indent([
5802
+ "try {",
5803
+ Template.indent([
5804
+ "var __extjsRetries = 0;",
5805
+ "var __extjsUpdateBase = function(){",
5806
+ Template.indent([
5807
+ 'var base = "";',
5808
+ 'try { base = document.documentElement.getAttribute("data-extjs-extension-base") || ""; } catch(_) { base = ""; }',
5809
+ "if (base) {",
5810
+ Template.indent([
5811
+ `${RuntimeGlobals.publicPath} = base.replace(/\\/+$/, "/") + String(${path}).replace(/^\\/+/, "");`
5812
+ ]),
5813
+ "} else if (__extjsRetries++ < 50) {",
5814
+ Template.indent([
5815
+ "setTimeout(__extjsUpdateBase, 100);"
5816
+ ]),
5817
+ "}"
5818
+ ]),
5819
+ "};",
5820
+ "__extjsUpdateBase();"
5821
+ ]),
5822
+ "} catch (_) {}"
5823
+ ]),
5824
+ "}"
5825
+ ]);
5826
+ }
5827
+ }
5828
+ return new PublicPathRuntime();
5829
+ }
5830
+ function AutoPublicPathRuntimeModule(webpack) {
5831
+ const { RuntimeModule, RuntimeGlobals, Template, javascript: { JavascriptModulesPlugin }, HotUpdateChunk } = webpack;
5832
+ const getChunkFilenameTemplate = JavascriptModulesPlugin.getChunkFilenameTemplate || function(chunk, outputOptions) {
5833
+ if (chunk.filenameTemplate) return chunk.filenameTemplate;
5834
+ if ('function' == typeof HotUpdateChunk && chunk instanceof HotUpdateChunk) return outputOptions.hotUpdateChunkFilename;
5835
+ if (chunk.canBeInitial()) return outputOptions.filename;
5836
+ return outputOptions.chunkFilename;
5837
+ };
5838
+ class AutoPublicPathRuntime extends RuntimeModule {
5839
+ constructor(){
5840
+ super('publicPath', RuntimeModule.STAGE_BASIC);
5841
+ }
5842
+ generate() {
5843
+ const { compilation } = this;
5844
+ if (!compilation || !this.chunk) return Template.asString('/* [webpack-target-webextension] AutoPublicPathRuntimeModule skipped because no compilation/chunk is found. */');
5845
+ const { scriptType, importMetaName } = compilation.outputOptions;
5846
+ const chunkName = compilation.getPath(getChunkFilenameTemplate(this.chunk, compilation.outputOptions), {
5847
+ chunk: this.chunk,
5848
+ contentHashType: "javascript"
5849
+ });
5850
+ const outputPath = compilation.outputOptions.path;
5851
+ if (!outputPath) return Template.asString('/* [webpack-target-webextension] AutoPublicPathRuntimeModule skipped because no output path is found. */');
5852
+ const undoPath = getUndoPath(chunkName, outputPath, false);
5853
+ const optionalChaining = compilation.outputOptions.environment.optionalChaining;
5854
+ const _const = compilation.outputOptions.environment.const ? 'const' : 'var';
5855
+ const _let = compilation.outputOptions.environment.const ? 'let' : 'var';
5856
+ return Template.asString([
5857
+ `${_let} scriptUrl;`,
5858
+ 'module' === scriptType ? `if (typeof ${importMetaName}.url === "string") scriptUrl = ${importMetaName}.url;` : Template.asString([
5859
+ `if (${RuntimeGlobals.global}.importScripts) scriptUrl = ${RuntimeGlobals.global}.location + "";`,
5860
+ `${_const} document = ${RuntimeGlobals.global}.document;`,
5861
+ `if (!scriptUrl && ${optionalChaining ? 'document?.currentScript' : 'document && document.currentScript'}) {`,
5862
+ Template.indent("scriptUrl = document.currentScript.src;"),
5863
+ '}'
5864
+ ]),
5865
+ '// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration',
5866
+ '// or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.',
5867
+ 'var __extjsBase = (typeof globalThis === "object" && globalThis && globalThis.__EXTJS_EXTENSION_BASE__) ? String(globalThis.__EXTJS_EXTENSION_BASE__) : "";',
5868
+ 'if (!__extjsBase && typeof document === "object" && document && document.documentElement) {',
5869
+ Template.indent([
5870
+ 'try { __extjsBase = document.documentElement.getAttribute("data-extjs-extension-base") || ""; } catch(_) { __extjsBase = ""; }'
5871
+ ]),
5872
+ "}",
5873
+ "if (__extjsBase && scriptUrl && !/^((chrome|moz)-extension):///.test(scriptUrl)) {",
5874
+ Template.indent([
5875
+ "scriptUrl = __extjsBase;"
5876
+ ]),
5877
+ "}",
5878
+ "if (!scriptUrl) {",
5879
+ Template.indent([
5880
+ 'var __extjsRuntimeRoot = "";',
5881
+ `if (${RuntimeGlobal} && ${RuntimeGlobal}.runtime && typeof ${RuntimeGlobal}.runtime.getURL === "function") {`,
5882
+ Template.indent(`try { __extjsRuntimeRoot = ${RuntimeGlobal}.runtime.getURL("/"); } catch (_) { __extjsRuntimeRoot = ""; }`),
5883
+ Template.indent("scriptUrl = __extjsRuntimeRoot || scriptUrl;"),
5884
+ "} else {",
5885
+ Template.indent('scriptUrl = __extjsBase || "";'),
5886
+ "}"
5887
+ ]),
5888
+ '}',
5889
+ 'scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\\?.*$/, "").replace(/\\/[^\\/]+$/, "/");',
5890
+ undoPath ? `${RuntimeGlobals.publicPath} = scriptUrl + ${JSON.stringify(undoPath)};` : `${RuntimeGlobals.publicPath} = scriptUrl;`,
5891
+ 'if (!scriptUrl && typeof document === "object" && document && document.documentElement) {',
5892
+ Template.indent([
5893
+ "try {",
5894
+ Template.indent([
5895
+ "var __extjsRetries = 0;",
5896
+ "var __extjsUpdateBase = function(){",
5897
+ Template.indent([
5898
+ 'var base = "";',
5899
+ 'try { base = document.documentElement.getAttribute("data-extjs-extension-base") || ""; } catch(_) { base = ""; }',
5900
+ "if (base) {",
5901
+ Template.indent([
5902
+ 'var normalized = base.replace(/\\/+$/, "/");',
5903
+ undoPath ? `${RuntimeGlobals.publicPath} = normalized + ${JSON.stringify(undoPath)};` : `${RuntimeGlobals.publicPath} = normalized;`
5904
+ ]),
5905
+ "} else if (__extjsRetries++ < 50) {",
5906
+ Template.indent([
5907
+ "setTimeout(__extjsUpdateBase, 100);"
5908
+ ]),
5909
+ "}"
5910
+ ]),
5911
+ "};",
5912
+ "__extjsUpdateBase();"
5913
+ ]),
5914
+ "} catch (_) {}"
5915
+ ]),
5916
+ "}"
5917
+ ]);
5918
+ }
5919
+ }
5920
+ return new AutoPublicPathRuntime();
5921
+ }
5922
+ function getUndoPath(filename, outputPath, enforceRelative) {
5923
+ let depth = -1;
5924
+ let append = '';
5925
+ outputPath = outputPath.replace(/[\\/]$/, '');
5926
+ for (const part of filename.split(/[/\\]+/))if ('..' === part) if (depth > -1) depth--;
5927
+ else {
5928
+ const i = outputPath.lastIndexOf('/');
5929
+ const j = outputPath.lastIndexOf('\\');
5930
+ const pos = i < 0 ? j : j < 0 ? i : Math.max(i, j);
5931
+ if (pos < 0) return `${outputPath}/`;
5932
+ append = `${outputPath.slice(pos + 1)}/${append}`;
5933
+ outputPath = outputPath.slice(0, pos);
5934
+ }
5935
+ else if ('.' !== part) depth++;
5936
+ return depth > 0 ? `${'../'.repeat(depth)}${append}` : enforceRelative ? `./${append}` : append;
5937
+ }
5938
+ function BaseUriRuntimeModule(webpack) {
5939
+ const { RuntimeGlobals, RuntimeModule, Template } = webpack;
5940
+ class BaseUriRuntime extends RuntimeModule {
5941
+ constructor(){
5942
+ super('baseURI', RuntimeModule.STAGE_BASIC);
5943
+ }
5944
+ generate() {
5945
+ return Template.asString([
5946
+ 'var __extjsBase = "";',
5947
+ "try {",
5948
+ Template.indent([
5949
+ `if (${RuntimeGlobal} && ${RuntimeGlobal}.runtime && typeof ${RuntimeGlobal}.runtime.getURL === "function") {`,
5950
+ Template.indent([
5951
+ `__extjsBase = String(${RuntimeGlobal}.runtime.getURL("/"));`
5952
+ ]),
5953
+ "}"
5954
+ ]),
5955
+ "} catch (_) {}",
5956
+ 'if (!__extjsBase && typeof globalThis === "object" && globalThis && globalThis.__EXTJS_EXTENSION_BASE__) {',
5957
+ Template.indent([
5958
+ '__extjsBase = String(globalThis.__EXTJS_EXTENSION_BASE__ || "");'
5959
+ ]),
5960
+ "}",
5961
+ 'if (!__extjsBase && typeof document === "object" && document && document.documentElement) {',
5962
+ Template.indent([
5963
+ 'try { __extjsBase = String(document.documentElement.getAttribute("data-extjs-extension-base") || ""); } catch (_) { __extjsBase = ""; }'
5964
+ ]),
5965
+ "}",
5966
+ "if (__extjsBase) {",
5967
+ Template.indent([
5968
+ `${RuntimeGlobals.baseURI} = __extjsBase;`
5969
+ ]),
5970
+ '} else if (typeof document !== "undefined" && document.baseURI) {',
5971
+ Template.indent([
5972
+ `${RuntimeGlobals.baseURI} = document.baseURI;`
5973
+ ]),
5974
+ "} else {",
5975
+ Template.indent([
5976
+ `${RuntimeGlobals.baseURI} = self.location.href;`
5977
+ ]),
5978
+ "}"
5979
+ ]);
5980
+ }
5981
+ }
5982
+ return new BaseUriRuntime();
5983
+ }
5984
+ function ChunkLoaderFallbackRuntimeModule(webpack) {
5985
+ const { RuntimeModule, Template } = webpack;
5986
+ class ChunkLoaderFallbackRuntime extends RuntimeModule {
5987
+ constructor(){
5988
+ super('chunk loader fallback', RuntimeModule.STAGE_TRIGGER);
5989
+ }
5990
+ generate() {
5991
+ const { compilation } = this;
5992
+ if (!compilation) return Template.asString('/* [webpack-target-webextension] ChunkLoaderFallbackRuntimeModule skipped because no compilation is found. */');
5993
+ const { f } = TemplateFn(compilation, Template);
5994
+ const optionalChain = compilation.outputOptions.environment.optionalChaining;
5995
+ const _const = compilation.outputOptions.environment.const ? 'const' : 'var';
5996
+ const _let = compilation.outputOptions.environment.const ? 'let' : 'var';
5997
+ return `if (${RuntimeGlobal} && ${RuntimeGlobal}.runtime && ${RuntimeGlobal}.runtime.onMessage) {${RuntimeGlobal}.runtime.onMessage.addListener(` + f('message, sender, sendResponse', [
5998
+ optionalChain ? 'if (message?.type != "WTW_INJECT" || typeof sender?.tab?.id != "number") return;' : 'if (!message || message.type != "WTW_INJECT" || !sender || !sender.tab || sender.tab.id == null) return;',
5999
+ `${_let} file = message.file;`,
6000
+ 'try {',
6001
+ Template.indent([
6002
+ 'file = new URL(file).pathname;'
6003
+ ]),
6004
+ '} catch (_) {}',
6005
+ 'if (!file) return;',
6006
+ `if (${RuntimeGlobal}.scripting) {`,
6007
+ Template.indent([
6008
+ `${RuntimeGlobal}.scripting.executeScript({`,
6009
+ Template.indent([
6010
+ 'target: { tabId: sender.tab.id, frameIds: [sender.frameId] },',
6011
+ 'files: [file],'
6012
+ ]),
6013
+ '}).then(sendResponse);'
6014
+ ]),
6015
+ "} else {",
6016
+ Template.indent([
6017
+ `${_const} details = { frameId: sender.frameId, file, matchAboutBlank: true };`,
6018
+ `if (${RuntimeGlobalIsBrowser}) {`,
6019
+ `${RuntimeGlobal}.tabs.executeScript(sender.tab.id, details).then(sendResponse);`,
6020
+ '} else {',
6021
+ `${RuntimeGlobal}.tabs.executeScript(sender.tab.id, details, sendResponse);`,
6022
+ '}'
6023
+ ]),
6024
+ '}',
6025
+ 'return true;'
6026
+ ]) + ");}";
6027
+ }
6028
+ }
6029
+ return new ChunkLoaderFallbackRuntime();
6030
+ }
6031
+ class WebExtensionChuckLoaderRuntimePlugin {
6032
+ pluginOptions;
6033
+ options;
6034
+ contentScriptsMeta;
6035
+ rspackAutoPublicPath = false;
6036
+ constructor(options){
6037
+ this.pluginOptions = options || {};
6038
+ this.options = this.pluginOptions.background || this.pluginOptions || {};
6039
+ this.contentScriptsMeta = this.pluginOptions.contentScriptsMeta || {};
6040
+ }
6041
+ apply(compiler) {
6042
+ if ('rspack' in compiler) {
6043
+ const { output } = compiler.options;
6044
+ if (void 0 === output.publicPath || 'auto' === output.publicPath) {
6045
+ output.publicPath = '';
6046
+ this.rspackAutoPublicPath = true;
6047
+ }
6048
+ }
6049
+ compiler.hooks.compilation.tap(WebExtensionChuckLoaderRuntimePlugin.name, (compilation)=>{
6050
+ this.tap(compiler, compilation);
6051
+ });
6052
+ }
6053
+ tap(compiler, compilation) {
6054
+ const { RuntimeGlobals } = compiler.webpack;
6055
+ const { options } = this;
6056
+ compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.loadScript).tap(WebExtensionChuckLoaderRuntimePlugin.name, (_chunk, set)=>{
6057
+ set.add(RuntimeGlobal);
6058
+ compilation.addRuntimeModule(_chunk, LoadScriptRuntimeModule(compiler.webpack, false !== compilation.outputOptions.environment.dynamicImport, false !== options.classicLoader, this.contentScriptsMeta));
6059
+ return true;
6060
+ });
6061
+ compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.baseURI).tap(WebExtensionChuckLoaderRuntimePlugin.name, (chunk, set)=>{
6062
+ set.add(RuntimeGlobal);
6063
+ compilation.addRuntimeModule(chunk, BaseUriRuntimeModule(compiler.webpack));
6064
+ return true;
6065
+ });
6066
+ compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.publicPath).tap(WebExtensionChuckLoaderRuntimePlugin.name, (chunk, set)=>{
6067
+ const { outputOptions } = compilation;
6068
+ const { publicPath, scriptType } = outputOptions;
6069
+ if ('auto' === publicPath || '' === publicPath && this.rspackAutoPublicPath) {
6070
+ const module = AutoPublicPathRuntimeModule(compiler.webpack);
6071
+ set.add(RuntimeGlobal);
6072
+ if ('module' !== scriptType) set.add(RuntimeGlobals.global);
6073
+ compilation.addRuntimeModule(chunk, module);
6074
+ } else {
6075
+ set.add(RuntimeGlobal);
6076
+ const module = PublicPath_PublicPathRuntimeModule(compiler.webpack);
6077
+ if ('string' != typeof publicPath || /\[(full)?hash\]/.test(publicPath)) module.fullHash = true;
6078
+ compilation.addRuntimeModule(chunk, module);
6079
+ }
6080
+ return true;
6081
+ });
6082
+ if (false !== options.classicLoader) compilation.hooks.afterOptimizeModules.tap(WebExtensionChuckLoaderRuntimePlugin.name, ()=>{
6083
+ const { pageEntry, serviceWorkerEntry } = options;
6084
+ const entryPoint1 = pageEntry && compilation.entrypoints.get(pageEntry);
6085
+ const entryPoint2 = serviceWorkerEntry && compilation.entrypoints.get(serviceWorkerEntry);
6086
+ if (entryPoint1) compilation.addRuntimeModule(entryPoint1.chunks[0], ChunkLoaderFallbackRuntimeModule(compiler.webpack));
6087
+ if (entryPoint2) compilation.addRuntimeModule(entryPoint2.chunks[0], ChunkLoaderFallbackRuntimeModule(compiler.webpack));
6088
+ });
6089
+ }
6090
+ }
6091
+ class NoDangerNamePlugin {
6092
+ apply(compiler) {
6093
+ const { Compilation, WebpackError } = compiler.webpack;
6094
+ compiler.hooks.thisCompilation.tap(NoDangerNamePlugin.name, (compilation)=>{
6095
+ compilation.hooks.processAssets.tap({
6096
+ name: NoDangerNamePlugin.name,
6097
+ stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
6098
+ }, (assets)=>{
6099
+ for (const name of Object.keys(assets || {})){
6100
+ if (!name.startsWith('_')) continue;
6101
+ if (name.startsWith('_locales/') || name.startsWith('_metadata/')) continue;
6102
+ const e = new WebpackError(`[webpack-extension-target] Output file name ${JSON.stringify(name)} starts with "_" which is prohibited.`);
6103
+ e.stack = '';
6104
+ compilation.errors.push(e);
6105
+ }
6106
+ });
6107
+ });
6108
+ }
6109
+ }
6110
+ function createEagerlyLoadChunksRuntimeModule(webpack) {
6111
+ const { RuntimeGlobals, RuntimeModule, Template } = webpack;
6112
+ return class extends RuntimeModule {
6113
+ chunkIds;
6114
+ constructor(chunkIds){
6115
+ super('eagerly load chunks', RuntimeModule.STAGE_NORMAL);
6116
+ this.chunkIds = chunkIds;
6117
+ }
6118
+ generate() {
6119
+ const { compilation } = this;
6120
+ if (!compilation) return Template.asString('/* [webpack-target-webextension] EagerlyLoadChunksRuntimeModule skipped because no compilation is found. */');
6121
+ const _const = compilation.outputOptions.environment.const ? 'const' : 'var';
6122
+ return Template.asString([
6123
+ `${_const} __extjsChunkIds = ${JSON.stringify(this.chunkIds)};`,
6124
+ `${_const} __extjsLoad = ${RuntimeGlobals.ensureChunkHandlers};`,
6125
+ `for (${_const} __extjsId of __extjsChunkIds) __extjsLoad(__extjsId);`
6126
+ ]);
6127
+ }
6128
+ };
6129
+ }
6130
+ class WebExtensionServiceWorkerEntryPlugin {
6131
+ options;
6132
+ backgroundOutputEmitted;
6133
+ constructor(options, backgroundOutputEmitted){
6134
+ this.options = options;
6135
+ this.backgroundOutputEmitted = backgroundOutputEmitted;
6136
+ }
6137
+ apply(compiler) {
6138
+ const { javascript, sources } = compiler.webpack;
6139
+ const entry = this.options.serviceWorkerEntry;
6140
+ if (void 0 === entry) return;
6141
+ if (!(compiler.options.output.module || compiler.options.experiments.outputModule)) {
6142
+ const hook = compiler.hooks.entryOption;
6143
+ hook.tap(WebExtensionServiceWorkerEntryPlugin.name, (_context, entries)=>{
6144
+ if ('function' == typeof entries) {
6145
+ if (this.options.noDynamicEntryWarning) return;
6146
+ console.warn(`[webpack-extension-target] Dynamic entry points not supported yet.
6147
+ You must manually set the chuck loading of entry point ${entry} to "import-scripts".
6148
+
6149
+ See https://webpack.js.org/configuration/entry-context/#entry-descriptor
6150
+
6151
+ Set background.noDynamicEntryWarning to true to disable this warning.
6152
+ `);
6153
+ }
6154
+ const selectedEntry = entries?.[entry];
6155
+ if (!selectedEntry) throw new Error(`[webpack-extension-target] There is no entry called ${entry}.`);
6156
+ selectedEntry.chunkLoading = "import-scripts";
6157
+ });
6158
+ }
6159
+ if (false !== this.options.eagerChunkLoading) compiler.hooks.compilation.tap(WebExtensionServiceWorkerEntryPlugin.name, (compilation)=>{
6160
+ compilation.hooks.afterOptimizeChunkIds?.tap(WebExtensionServiceWorkerEntryPlugin.name, ()=>{
6161
+ const entryPoint = compilation.entrypoints.get(entry);
6162
+ if (!entryPoint) return;
6163
+ const entryChunk = entryPoint.getEntrypointChunk();
6164
+ const reachableChunks = getReachableChunks(entryPoint, new Set(entryPoint.chunks));
6165
+ const reachableChunkIds = new Set([
6166
+ ...reachableChunks
6167
+ ].map((x)=>x.id));
6168
+ for (const id of getInitialChunkIds(entryChunk, compilation.chunkGraph, chunkHasJs))reachableChunkIds.delete(id);
6169
+ if (reachableChunkIds.size) {
6170
+ const EagerlyLoadChunksRuntimeModule = createEagerlyLoadChunksRuntimeModule(compiler.webpack);
6171
+ compilation.hooks.additionalTreeRuntimeRequirements.tap(EagerlyLoadChunksRuntimeModule.name, (chunk, set)=>{
6172
+ if (chunk.id !== entryChunk.id) return;
6173
+ set.add(compiler.webpack.RuntimeGlobals.ensureChunkHandlers);
6174
+ compilation.addRuntimeModule(entryChunk, new EagerlyLoadChunksRuntimeModule([
6175
+ ...reachableChunkIds
6176
+ ]));
6177
+ });
6178
+ }
6179
+ });
6180
+ });
6181
+ if (false !== this.options.tryCatchWrapper && 'module' !== compiler.options.output.chunkFormat && !this.backgroundOutputEmitted) compiler.hooks.compilation.tap(WebExtensionServiceWorkerEntryPlugin.name, (compilation)=>{
6182
+ const hooks = javascript.JavascriptModulesPlugin.getCompilationHooks(compilation);
6183
+ if (hooks.renderContent) hooks.renderContent.tap(WebExtensionServiceWorkerEntryPlugin.name, (source, context)=>{
6184
+ const entryPoint = compilation.entrypoints.get(entry);
6185
+ const entryChunk = entryPoint?.getEntrypointChunk();
6186
+ if (entryChunk !== context.chunk) return source;
6187
+ return new sources.ConcatSource('/******/ try { // If the initial code of the serviceWorkerEntry throws, the console cannot be opened.\n', source, '\n/******/ } catch (e) {\n/******/ \tPromise.reject(e);\n/******/ }');
6188
+ });
6189
+ });
6190
+ }
6191
+ }
6192
+ function getInitialChunkIds(chunk, chunkGraph, filterFn) {
6193
+ const initialChunkIds = new Set(chunk.ids);
6194
+ for (const c of chunk.getAllInitialChunks())if (!(c === chunk || filterFn(c, chunkGraph)) && c.ids) for (const id of c.ids)initialChunkIds.add(id);
6195
+ return initialChunkIds;
6196
+ }
6197
+ function chunkHasJs(chunk, chunkGraph) {
6198
+ if (chunkGraph.getNumberOfEntryModules(chunk) > 0) return true;
6199
+ return !!chunkGraph.getChunkModulesIterableBySourceType(chunk, "javascript");
6200
+ }
6201
+ function getReachableChunks(chunkGroup, reachableChunks = new Set(), visitedChunkGroups = new Set()) {
6202
+ for (const x of chunkGroup.getChildren())if (!visitedChunkGroups.has(x)) {
6203
+ visitedChunkGroups.add(x);
6204
+ for (const c of x.chunks)reachableChunks.add(c);
6205
+ getReachableChunks(x, reachableChunks, visitedChunkGroups);
6206
+ }
6207
+ return reachableChunks;
6208
+ }
6209
+ class WebExtensionContentScriptEntryPlugin {
6210
+ options;
6211
+ constructor(options){
6212
+ this.options = options;
6213
+ }
6214
+ apply(compiler) {
6215
+ const { WebpackError, sources, Template } = compiler.webpack;
6216
+ const { experimental_output } = this.options;
6217
+ if (!experimental_output) return;
6218
+ {
6219
+ const sw = this.options.background?.serviceWorkerEntry;
6220
+ if (sw && sw in experimental_output && 'function' == typeof experimental_output[sw]) throw new Error(`[webpack-extension-target] options.experimental_output[${JSON.stringify(sw)}] cannot be a function because it is a service worker entry. Use { file, touch(manifest, file) { manifest.background.service_worker = file; } } instead.`);
6221
+ }
6222
+ compiler.hooks.thisCompilation.tap(WebExtensionContentScriptEntryPlugin.name, (compilation)=>{
6223
+ compilation.hooks.processAssets.tap({
6224
+ name: WebExtensionContentScriptEntryPlugin.name,
6225
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED
6226
+ }, (assets)=>{
6227
+ let manifest;
6228
+ const serviceWorkerEntry = this.options.background?.serviceWorkerEntry;
6229
+ if (serviceWorkerEntry && !(serviceWorkerEntry in experimental_output) && (getInitialFiles(compilation, serviceWorkerEntry)?.length || 0) > 1) {
6230
+ const e = new WebpackError(`[webpack-extension-target] Entry ${JSON.stringify(serviceWorkerEntry)} is not specified in options.experimental_output.`);
6231
+ e.stack = '';
6232
+ compilation.warnings.push(e);
6233
+ }
6234
+ for(const entry in experimental_output){
6235
+ const isBackgroundEntry = entry === serviceWorkerEntry;
6236
+ const entryOption = experimental_output[entry];
6237
+ const initialFiles = getInitialFiles(compilation, entry);
6238
+ if (!initialFiles || 0 === initialFiles.length) {
6239
+ const name = JSON.stringify(entry);
6240
+ const e = new WebpackError(initialFiles ? `[webpack-extension-target] Entry ${name} does not emit any initial file (specified in options.experimental_output).` : `[webpack-extension-target] Entry ${name} does not exist (specified in options.experimental_output).`);
6241
+ e.stack = '';
6242
+ compilation.errors.push(e);
6243
+ continue;
6244
+ }
6245
+ if (false === entryOption) {
6246
+ if (initialFiles.length > 1) {
6247
+ const name = JSON.stringify(entry);
6248
+ const e = new WebpackError(`[webpack-extension-target] Entry ${name} emits more than one initial file which is prohibited (specified in options.experimental_output).`);
6249
+ e.stack = '';
6250
+ compilation.errors.push(e);
6251
+ }
6252
+ continue;
6253
+ }
6254
+ const emitFile = (entryOptionFile, files)=>{
6255
+ if (entryOptionFile in assets) {
6256
+ const e = new WebpackError(`[webpack-extension-target] Cannot override an existing file ${JSON.stringify(entryOptionFile)} (specified by options.experimental_output[${JSON.stringify(entry)}]).`);
6257
+ e.stack = '';
6258
+ compilation.errors.push(e);
6259
+ return;
6260
+ }
6261
+ let code;
6262
+ if (isBackgroundEntry) {
6263
+ const asyncAndSyncFiles = getInitialAndAsyncFiles(compilation, entry);
6264
+ code = 'module' === compilation.outputOptions.chunkFormat ? asyncAndSyncFiles.map((file)=>`import ${JSON.stringify(`./${file}`)};`) : [
6265
+ 'try {',
6266
+ Template.indent('importScripts(' + asyncAndSyncFiles.map((file)=>JSON.stringify(file)).join(', ') + ');'),
6267
+ '} catch (e) {',
6268
+ Template.indent('Promise.reject(e);'),
6269
+ '}'
6270
+ ];
6271
+ } else code = [
6272
+ ';(() => {',
6273
+ Template.indent([
6274
+ 'const getURL = typeof globalThis === "object" && globalThis && typeof globalThis.browser === "object" ? globalThis.browser.runtime.getURL : globalThis.chrome.runtime.getURL;',
6275
+ `${JSON.stringify(files)}.forEach(file => import(getURL(file)));`
6276
+ ]),
6277
+ '})();',
6278
+ 'null;'
6279
+ ];
6280
+ const source = new compiler.webpack.sources.RawSource(Template.asString(code));
6281
+ compilation.emitAsset(entryOptionFile, source);
6282
+ };
6283
+ if ('string' == typeof entryOption) {
6284
+ emitFile(entryOption, initialFiles);
6285
+ continue;
6286
+ }
6287
+ if (!manifest) {
6288
+ const manifestAsset = assets['manifest.json'];
6289
+ const name = JSON.stringify(entry);
6290
+ if (!manifestAsset) {
6291
+ const e = new WebpackError(`[webpack-extension-target] A manifest.json is required (required by options.experimental_output[${name}]). You can emit this file by using CopyPlugin or any other plugins.`);
6292
+ e.stack = '';
6293
+ compilation.errors.push(e);
6294
+ continue;
6295
+ }
6296
+ try {
6297
+ const source = manifestAsset.source();
6298
+ manifest = 'string' == typeof source ? JSON.parse(source) : JSON.parse(source.toString('utf-8'));
6299
+ } catch {
6300
+ const e = new WebpackError(`[webpack-extension-target] Failed to parse manifest.json (required by options.experimental_output[${name}]).`);
6301
+ e.stack = '';
6302
+ e.file = 'manifest.json';
6303
+ compilation.errors.push(e);
6304
+ continue;
6305
+ }
6306
+ }
6307
+ if ('function' == typeof entryOption) entryOption(manifest, initialFiles);
6308
+ else if ('object' == typeof entryOption) {
6309
+ emitFile(entryOption.file, initialFiles);
6310
+ entryOption.touch(manifest, entryOption.file);
6311
+ }
6312
+ }
6313
+ if (manifest) compilation.updateAsset('manifest.json', new sources.RawSource(JSON.stringify(manifest, void 0, 4)));
6314
+ });
6315
+ });
6316
+ }
6317
+ }
6318
+ function getInitialFiles(compilation, entry) {
6319
+ const entryPoint = compilation.entrypoints.get(entry);
6320
+ if (!entryPoint) return;
6321
+ const files = new Set();
6322
+ const runtimeChunk = entryPoint.getRuntimeChunk();
6323
+ if (runtimeChunk) runtimeChunk.files.forEach((file)=>{
6324
+ if (!isJSFile(file)) return;
6325
+ const asset = compilation.getAsset(file);
6326
+ if (!asset) return;
6327
+ if (!asset.info.hotModuleReplacement) files.add(file);
6328
+ });
6329
+ entryPoint.getFiles().forEach((file)=>{
6330
+ if (!isJSFile(file)) return;
6331
+ const asset = compilation.getAsset(file);
6332
+ if (!asset) return;
6333
+ if (!asset.info.hotModuleReplacement) files.add(file);
6334
+ });
6335
+ return [
6336
+ ...files
6337
+ ];
3121
6338
  }
3122
- const BUDGET_BYTES = {
3123
- "content-script": 524288,
3124
- 'service-worker': 524288,
3125
- page: 1048576,
3126
- ignored: 1 / 0
3127
- };
3128
- function fmtKiB(bytes) {
3129
- const kib = bytes / 1024;
3130
- if (kib >= 1024) return `${(kib / 1024).toFixed(2)} MiB`;
3131
- return `${kib.toFixed(1)} KiB`;
6339
+ function getInitialAndAsyncFiles(compilation, entry) {
6340
+ const entryPoint = compilation.entrypoints.get(entry);
6341
+ if (!entryPoint) return [];
6342
+ const files = new Set();
6343
+ const visitedChunk = new Set();
6344
+ function visit(chunk) {
6345
+ const chunkId = 'rspack' in compilation.compiler ? chunk.id && chunk.hash ? chunk.id + chunk.hash : Array.from(chunk.files).join('') : chunk;
6346
+ if (visitedChunk.has(chunkId)) return;
6347
+ visitedChunk.add(chunkId);
6348
+ chunk.files.forEach((file)=>{
6349
+ if (!isJSFile(file)) return;
6350
+ const asset = compilation.getAsset(file);
6351
+ if (!asset) return;
6352
+ if (!asset.info.hotModuleReplacement) files.add(file);
6353
+ });
6354
+ for (const child of chunk.getAllAsyncChunks())visit(child);
6355
+ }
6356
+ entryPoint.chunks.forEach(visit);
6357
+ const allFiles = [
6358
+ ...files
6359
+ ].filter((file)=>isJSFile(file) && compilation.getAsset(file));
6360
+ return allFiles;
3132
6361
  }
3133
- function perfBudgetWarning(assets) {
3134
- const header = `${1 === assets.length ? 'asset exceeds' : 'assets exceed'} the extension performance budget. Browser extensions inject content scripts on every navigation and wake service workers from cold, so we apply tighter budgets than the rspack default.`;
3135
- const lines = assets.map((a)=>{
3136
- const over = (a.size / a.budget * 100 - 100).toFixed(0);
3137
- return ` ${a.name}\n size: ${fmtKiB(a.size)}\n budget: ${fmtKiB(a.budget)} (over by ${over}%)\n role: ${categoryRole(a.category)}`;
3138
- });
3139
- const remediation = "Lazy-load with dynamic import(), code-split per route, or replace large SDKs with thin fetch wrappers.\nSee https://rspack.rs/guide/optimization/code-splitting";
3140
- return `${header}\n\n${lines.join('\n')}\n\n${remediation}`;
6362
+ function isJSFile(file) {
6363
+ return file.endsWith('.js') || file.endsWith('.mjs');
3141
6364
  }
3142
- function categoryRole(c) {
3143
- switch(c){
3144
- case "content-script":
3145
- return "content script, injected on every page navigation";
3146
- case 'service-worker':
3147
- return 'service worker / background, wakes from cold each session';
3148
- case 'page':
3149
- return 'UI page, opened on demand';
3150
- default:
3151
- return 'asset';
6365
+ class DevServerConfigPlugin {
6366
+ apply(compiler) {
6367
+ if (!compiler.options.devServer) compiler.options.devServer = {};
6368
+ const devServer = compiler.options.devServer;
6369
+ setDefault(devServer, 'devMiddleware', {});
6370
+ setDefault(devServer.devMiddleware, 'writeToDisk', true);
6371
+ if (void 0 === devServer.hot) devServer.hot = 'only';
6372
+ if (!devServer.hot) return;
6373
+ setDefault(devServer, 'host', '127.0.0.1');
6374
+ setDefault(devServer, 'client', {
6375
+ overlay: false,
6376
+ progress: false,
6377
+ webSocketURL: {
6378
+ protocol: 'ws'
6379
+ }
6380
+ });
6381
+ setDefault(devServer.client, 'overlay', false);
6382
+ setDefault(devServer.client, 'progress', false);
6383
+ setDefault(devServer.client, 'webSocketURL', {
6384
+ protocol: 'ws'
6385
+ });
6386
+ setDefault(devServer.client.webSocketURL, 'protocol', 'ws');
6387
+ const connectableHost = resolveClientHost(process.env.EXTENSION_DEV_SERVER_CONNECTABLE_HOST, devServer.host);
6388
+ if (connectableHost) setDefault(devServer.client.webSocketURL, 'hostname', connectableHost);
6389
+ if (devServer.port) setDefault(devServer.client.webSocketURL, 'port', devServer.port);
6390
+ setDefault(devServer, 'allowedHosts', 'all');
6391
+ setDefault(devServer, 'headers', {
6392
+ 'Access-Control-Allow-Origin': '*'
6393
+ });
6394
+ setDefault(devServer, 'static', {
6395
+ watch: {
6396
+ ignored: /\bnode_modules\b/
6397
+ }
6398
+ });
6399
+ setDefault(devServer.static, 'watch', {
6400
+ ignored: /\bnode_modules\b/
6401
+ });
6402
+ isObject(devServer.static) && 'watch' in devServer.static && isObject(devServer.static.watch) && setDefault(devServer.static.watch, 'ignored', /\bnode_modules\b/);
3152
6403
  }
3153
6404
  }
3154
- class PerfBudgetsPlugin {
3155
- static name = 'plugin-perf-budgets';
6405
+ function setDefault(obj, key, val) {
6406
+ if (isObject(obj) && void 0 === obj[key]) obj[key] = val;
6407
+ }
6408
+ const WILDCARD_HOSTS = new Set([
6409
+ '0.0.0.0',
6410
+ '::',
6411
+ '[::]',
6412
+ '::0',
6413
+ '*',
6414
+ ''
6415
+ ]);
6416
+ function resolveClientHost(connectable, bindHost) {
6417
+ const resolved = String(connectable ?? '').trim();
6418
+ if (resolved) return resolved;
6419
+ const bind = 'string' == typeof bindHost ? bindHost.trim() : '';
6420
+ if (!bind) return;
6421
+ return WILDCARD_HOSTS.has(bind) ? '127.0.0.1' : bind;
6422
+ }
6423
+ function isObject(x) {
6424
+ return 'object' == typeof x && null !== x;
6425
+ }
6426
+ class WebExtensionPlugin {
3156
6427
  options;
3157
6428
  constructor(options = {}){
6429
+ const { background } = options;
6430
+ if (background) {
6431
+ const { serviceWorkerEntry, pageEntry } = background;
6432
+ if ('entry' in background || 'manifest' in background) throw new Error('[webpack-extension-target] background.entry and background.manifest has been removed. Use background.pageEntry and/or background.serviceWorkerEntry instead.');
6433
+ if (serviceWorkerEntry && serviceWorkerEntry === pageEntry) throw new Error(`[webpack-extension-target] background.serviceWorkerEntry must not be the same as background.pageEntry. Service Worker entry only supports importScript, but importScript does not exist in background page (mv2) or limited event page (mv3).
6434
+ A possible fix is to create two new files to be the service worker entry and the page entry, then those two files imports the background entry.`);
6435
+ }
3158
6436
  this.options = options;
3159
6437
  }
3160
6438
  apply(compiler) {
3161
- const enabled = 'boolean' == typeof this.options.enabled ? this.options.enabled : 'production' === compiler.options.mode;
3162
- if (!enabled) return;
3163
- const budgets = {
3164
- ...BUDGET_BYTES,
3165
- ...this.options.budgets || {}
6439
+ const { background, experimental_output } = this.options;
6440
+ if (background?.serviceWorkerEntry) {
6441
+ const serviceWorkerEntry = this.options.background?.serviceWorkerEntry;
6442
+ const output = serviceWorkerEntry ? this.options.experimental_output?.[serviceWorkerEntry] : false;
6443
+ new WebExtensionServiceWorkerEntryPlugin(background, !!output).apply(compiler);
6444
+ }
6445
+ if (experimental_output) new WebExtensionContentScriptEntryPlugin(this.options).apply(compiler);
6446
+ if (false !== this.options.hmrConfig) new DevServerConfigPlugin().apply(compiler);
6447
+ new WebExtensionChuckLoaderRuntimePlugin(this.options).apply(compiler);
6448
+ new NoDangerNamePlugin().apply(compiler);
6449
+ compiler.hooks.environment.tap(WebExtensionPlugin.name, ()=>{
6450
+ const { output } = compiler.options;
6451
+ if (false !== output.environment.dynamicImport) output.environment.dynamicImport = true;
6452
+ if (void 0 !== output.hotUpdateChunkFilename) output.hotUpdateChunkFilename = 'hot/[id].[fullhash].js';
6453
+ if (void 0 !== output.hotUpdateMainFilename) output.hotUpdateMainFilename = 'hot/[runtime].[fullhash].json';
6454
+ });
6455
+ compiler.hooks.compilation.tap(WebExtensionPlugin.name, (compilation)=>{
6456
+ compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobal).tap(WebExtensionPlugin.name, (chunk)=>{
6457
+ compilation.addRuntimeModule(chunk, BrowserRuntimeModule(compiler.webpack, !!this.options.weakRuntimeCheck));
6458
+ return true;
6459
+ });
6460
+ });
6461
+ }
6462
+ }
6463
+ const webpack_target_webextension_fork = WebExtensionPlugin;
6464
+ class SetupReloadStrategy {
6465
+ manifestPath;
6466
+ browser;
6467
+ constructor(options){
6468
+ this.manifestPath = options.manifestPath;
6469
+ this.browser = options.browser || 'chrome';
6470
+ }
6471
+ getEntryName(manifest) {
6472
+ if (manifest.background) {
6473
+ if (isGeckoBasedBrowser(String(this.browser))) return {
6474
+ pageEntry: "background/script",
6475
+ tryCatchWrapper: true,
6476
+ eagerChunkLoading: false
6477
+ };
6478
+ if (3 === manifest.manifest_version) return {
6479
+ serviceWorkerEntry: 'background/service_worker',
6480
+ tryCatchWrapper: true,
6481
+ eagerChunkLoading: false
6482
+ };
6483
+ if (2 === manifest.manifest_version) return {
6484
+ pageEntry: "background/script",
6485
+ tryCatchWrapper: true,
6486
+ eagerChunkLoading: false
6487
+ };
6488
+ }
6489
+ return {
6490
+ pageEntry: 'background',
6491
+ tryCatchWrapper: true,
6492
+ eagerChunkLoading: false
3166
6493
  };
3167
- const report = (compilation)=>{
3168
- if (compilation.errors?.length) return;
3169
- const oversized = [];
3170
- const assets = compilation.assets || {};
3171
- for (const [name, source] of Object.entries(assets)){
3172
- const size = source?.size?.() ?? 0;
3173
- if (!size) continue;
3174
- const category = categorizeAsset(name);
3175
- if ('ignored' === category) continue;
3176
- const budget = budgets[category];
3177
- if (Number.isFinite(budget)) {
3178
- if (!(size <= budget)) oversized.push({
3179
- name,
3180
- size,
3181
- budget,
3182
- category
3183
- });
3184
- }
6494
+ }
6495
+ apply(compiler) {
6496
+ const manifest = JSON.parse(stripBom(__rspack_external_node_fs_5ea92f0c.readFileSync(this.manifestPath, 'utf-8')));
6497
+ const patchedManifest = filterKeysForThisBrowser(manifest, this.browser);
6498
+ const contentScriptsMeta = {};
6499
+ try {
6500
+ const csList = Array.isArray(patchedManifest.content_scripts) ? patchedManifest.content_scripts : [];
6501
+ const originalCount = csList.length;
6502
+ let bridgeOrdinal = 0;
6503
+ for(let i = 0; i < csList.length; i++){
6504
+ const cs = csList[i];
6505
+ const bundleId = getCanonicalContentScriptJsAssetName(i);
6506
+ const isMain = cs?.world === 'MAIN';
6507
+ if (isMain) {
6508
+ const bridgeIndex = originalCount + bridgeOrdinal++;
6509
+ const bridgeBundleId = getCanonicalContentScriptJsAssetName(bridgeIndex);
6510
+ contentScriptsMeta[bundleId] = {
6511
+ index: i,
6512
+ bundleId,
6513
+ world: 'main',
6514
+ bridgeBundleId
6515
+ };
6516
+ contentScriptsMeta[bridgeBundleId] = {
6517
+ index: bridgeIndex,
6518
+ bundleId: bridgeBundleId,
6519
+ world: 'extension',
6520
+ role: 'main_world_bridge',
6521
+ mainBundleId: bundleId
6522
+ };
6523
+ } else contentScriptsMeta[bundleId] = {
6524
+ index: i,
6525
+ bundleId,
6526
+ world: 'extension'
6527
+ };
3185
6528
  }
3186
- if (0 === oversized.length) return;
3187
- oversized.sort((a, b)=>b.size - a.size);
3188
- const ErrorConstructor = compiler?.rspack?.WebpackError || Error;
3189
- const warning = new ErrorConstructor(perfBudgetWarning(oversized));
3190
- warning.name = 'PerfBudgetWarning';
3191
- if (!compilation.warnings) compilation.warnings = [];
3192
- compilation.warnings.push(warning);
3193
- };
3194
- const REPORT_STAGE = compiler?.rspack?.Compilation?.PROCESS_ASSETS_STAGE_REPORT ?? 5000;
3195
- compiler.hooks.thisCompilation.tap(PerfBudgetsPlugin.name, (compilation)=>{
6529
+ } catch {}
6530
+ new SetupBackgroundEntry({
6531
+ manifestPath: this.manifestPath,
6532
+ browser: this.browser
6533
+ }).apply(compiler);
6534
+ new webpack_target_webextension_fork({
6535
+ background: this.getEntryName(patchedManifest),
6536
+ hmrConfig: false,
6537
+ weakRuntimeCheck: true,
6538
+ contentScriptsMeta
6539
+ }).apply(compiler);
6540
+ }
6541
+ }
6542
+ const DEV_SERVER_STRONG_MARKERS = [
6543
+ '@rspack/dev-server/client/index.js?',
6544
+ '@rspack/dev-server/client/utils/ansiHTML.js',
6545
+ '[HMR] Waiting for update signal from WDS...'
6546
+ ];
6547
+ const DEV_SERVER_WEAK_MARKERS = [
6548
+ 'webpack-dev-server',
6549
+ 'WebSocketClient',
6550
+ '.hot.check()',
6551
+ '[HMR] Cannot find update. Need to do a full reload!'
6552
+ ];
6553
+ function contentScriptRetainsDevServerRuntime(source) {
6554
+ return source.includes('@rspack/dev-server/client');
6555
+ }
6556
+ function stripDevServerStartupFromContentScript(source) {
6557
+ let nextSource = source;
6558
+ const startupModuleIds = getStartupModuleIds(source);
6559
+ for (const moduleId of startupModuleIds){
6560
+ const moduleBody = getModuleBody(source, moduleId);
6561
+ if (!moduleBody) continue;
6562
+ const strongHit = DEV_SERVER_STRONG_MARKERS.some((marker)=>moduleBody.includes(marker));
6563
+ const weakHits = DEV_SERVER_WEAK_MARKERS.filter((marker)=>moduleBody.includes(marker)).length;
6564
+ if (strongHit || weakHits >= 2) nextSource = stripStartupRequire(nextSource, moduleId);
6565
+ }
6566
+ return nextSource;
6567
+ }
6568
+ function getStartupModuleIds(source) {
6569
+ const startupIndex = source.indexOf('// startup');
6570
+ if (-1 === startupIndex) return [];
6571
+ const startupSection = source.slice(startupIndex);
6572
+ const requirePattern = /__webpack_require__\((\d+)\);/g;
6573
+ const ids = [];
6574
+ let match = null;
6575
+ while(match = requirePattern.exec(startupSection))ids.push(match[1]);
6576
+ return ids;
6577
+ }
6578
+ function getModuleBody(source, moduleId) {
6579
+ const moduleHeaderPattern = new RegExp(`(?:^|\\n)${moduleId}\\([^)]*\\)\\s*\\{`, 'm');
6580
+ const headerMatch = moduleHeaderPattern.exec(source);
6581
+ if (!headerMatch) return null;
6582
+ const moduleStart = headerMatch.index;
6583
+ const nextHeaderPattern = /(?:^|\n)\d+\([^)]*\)\s*\{/g;
6584
+ nextHeaderPattern.lastIndex = moduleStart + headerMatch[0].length;
6585
+ const nextHeaderMatch = nextHeaderPattern.exec(source);
6586
+ return source.slice(moduleStart, nextHeaderMatch ? nextHeaderMatch.index : source.length);
6587
+ }
6588
+ function stripStartupRequire(source, moduleId) {
6589
+ const startupRequirePattern = new RegExp(`^\\s*__webpack_require__\\(${moduleId}\\);\\n?`, 'm');
6590
+ return source.replace(startupRequirePattern, '');
6591
+ }
6592
+ const CONTENT_SCRIPT_ASSET = /(^|\/)content_scripts\/content-\d+(?:\.[a-f0-9]+)?\.js$/i;
6593
+ class StripContentScriptDevServerRuntime {
6594
+ apply(compiler) {
6595
+ compiler.hooks.thisCompilation.tap(StripContentScriptDevServerRuntime.name, (compilation)=>{
3196
6596
  compilation.hooks.processAssets.tap({
3197
- name: PerfBudgetsPlugin.name,
3198
- stage: REPORT_STAGE
3199
- }, ()=>report(compilation));
6597
+ name: StripContentScriptDevServerRuntime.name,
6598
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT
6599
+ }, ()=>{
6600
+ for (const asset of compilation.getAssets()){
6601
+ if (!CONTENT_SCRIPT_ASSET.test(asset.name)) continue;
6602
+ const originalSource = asset.source.source().toString();
6603
+ const strippedSource = stripDevServerStartupFromContentScript(originalSource);
6604
+ if (strippedSource !== originalSource) compilation.updateAsset(asset.name, new core_sources.RawSource(strippedSource));
6605
+ if (contentScriptRetainsDevServerRuntime(strippedSource)) {
6606
+ const warning = new core_WebpackError(`Could not strip the dev-server runtime from ${asset.name}. This usually means the bundler's output format changed and Extension.js needs an update. The content script may try to open a dev-server connection on the host page. Please report this at https://github.com/extension-js/extension.js/issues.`);
6607
+ warning.name = 'ContentScriptDevServerRuntimeWarning';
6608
+ warning.file = asset.name;
6609
+ compilation.warnings ||= [];
6610
+ compilation.warnings.push(warning);
6611
+ }
6612
+ }
6613
+ });
3200
6614
  });
3201
6615
  }
3202
6616
  }
6617
+ class ReloadPlugin {
6618
+ static name = 'plugin-reload';
6619
+ manifestPath;
6620
+ browser;
6621
+ constructor(options){
6622
+ this.manifestPath = options.manifestPath;
6623
+ this.browser = options.browser || 'chrome';
6624
+ }
6625
+ apply(compiler) {
6626
+ if ('production' === compiler.options.mode || 'true' === process.env.EXTENSION_NO_RELOAD) return;
6627
+ const hasValidManifest = !!this.manifestPath && __rspack_external_node_fs_5ea92f0c.existsSync(this.manifestPath) && __rspack_external_node_fs_5ea92f0c.lstatSync(this.manifestPath).isFile();
6628
+ if (!hasValidManifest) return;
6629
+ new StripContentScriptDevServerRuntime().apply(compiler);
6630
+ new SetupReloadStrategy({
6631
+ manifestPath: this.manifestPath,
6632
+ browser: this.browser
6633
+ }).apply(compiler);
6634
+ new InjectScriptsReplayShim().apply(compiler);
6635
+ new InjectBridgeProducer().apply(compiler);
6636
+ new InjectBridgeRelay().apply(compiler);
6637
+ new PruneStaleHotUpdates().apply(compiler);
6638
+ }
6639
+ }
3203
6640
  function checkManifestInPublic(compilation, publicDir) {
3204
6641
  try {
3205
6642
  const manifestInPublic = __rspack_external_node_path_c5b9b54f.join(publicDir, 'manifest.json');
3206
6643
  if (__rspack_external_node_fs_5ea92f0c.existsSync(manifestInPublic)) {
3207
- const err = new WebpackError(`manifest.json must not be placed under public/: ${manifestInPublic}`);
6644
+ const err = new core_WebpackError(`manifest.json must not be placed under public/: ${manifestInPublic}`);
3208
6645
  err.file = 'manifest.json';
3209
6646
  compilation.errors.push(err);
3210
6647
  }
@@ -3228,7 +6665,7 @@ class TraceRuntimeLoadedFiles {
3228
6665
  compiler.hooks.thisCompilation.tap(TraceRuntimeLoadedFiles.name, (compilation)=>{
3229
6666
  compilation.hooks.processAssets.tap({
3230
6667
  name: TraceRuntimeLoadedFiles.name,
3231
- stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE
6668
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE
3232
6669
  }, ()=>{
3233
6670
  this.traceWorkerImportScripts(compilation);
3234
6671
  this.traceInjectedFilePayloads(compilation);
@@ -3252,7 +6689,7 @@ class TraceRuntimeLoadedFiles {
3252
6689
  const workerAsset = compilation.getAsset(EMITTED_WORKER_PATH);
3253
6690
  if (!workerAsset) return;
3254
6691
  const manifestDir = __rspack_external_node_path_c5b9b54f.dirname(this.manifestPath);
3255
- const sourceWorkerPath = unixify(String(workerRef)).replace(/^\/+/, '');
6692
+ const sourceWorkerPath = trace_runtime_loaded_files_unixify(String(workerRef)).replace(/^\/+/, '');
3256
6693
  let pending = [
3257
6694
  workerAsset.source.source().toString()
3258
6695
  ];
@@ -3310,7 +6747,7 @@ class TraceRuntimeLoadedFiles {
3310
6747
  continue;
3311
6748
  }
3312
6749
  if (/\.[a-zA-Z0-9]{1,8}$/.test(distRel)) {
3313
- const warn = new WebpackError(fetchedFileDependencyMissing(asset.name, literal, distRel));
6750
+ const warn = new core_WebpackError(fetchedFileDependencyMissing(asset.name, literal, distRel));
3314
6751
  warn.name = 'RuntimeFetchedFileMissing';
3315
6752
  warn.file = asset.name;
3316
6753
  compilation.warnings ||= [];
@@ -3378,7 +6815,7 @@ class TraceRuntimeLoadedFiles {
3378
6815
  continue;
3379
6816
  }
3380
6817
  if ('js' === item.kind && /\.[a-zA-Z0-9]{1,8}$/.test(distRel)) {
3381
- const warn = new WebpackError(isStaticImport ? staticImportDependencyMissing(item.assetName, literal, distRel) : isRuntimeSurface ? runtimeSetSurfaceDependencyMissing(item.assetName, literal, distRel) : getURLDependencyMissing(item.assetName, literal, distRel));
6818
+ const warn = new core_WebpackError(isStaticImport ? staticImportDependencyMissing(item.assetName, literal, distRel) : isRuntimeSurface ? runtimeSetSurfaceDependencyMissing(item.assetName, literal, distRel) : getURLDependencyMissing(item.assetName, literal, distRel));
3382
6819
  warn.name = isStaticImport ? 'RuntimeStaticImportFileMissing' : isRuntimeSurface ? 'RuntimeSetSurfaceFileMissing' : 'RuntimeGetURLFileMissing';
3383
6820
  warn.file = item.assetName;
3384
6821
  compilation.warnings ||= [];
@@ -3396,7 +6833,7 @@ class TraceRuntimeLoadedFiles {
3396
6833
  for (const asset of jsAssets){
3397
6834
  const content = asset.source.source().toString();
3398
6835
  if (!hasWebpackChunkLoadingRuntime(content)) continue;
3399
- const assetDirRel = __rspack_external_node_path_c5b9b54f.posix.dirname(unixify(asset.name));
6836
+ const assetDirRel = __rspack_external_node_path_c5b9b54f.posix.dirname(trace_runtime_loaded_files_unixify(asset.name));
3400
6837
  const sourceDirs = new Set();
3401
6838
  const entryDir = entrySourceDirs.get(asset.name.replace(/\.js$/i, ''));
3402
6839
  if (entryDir) sourceDirs.add(entryDir);
@@ -3477,7 +6914,7 @@ function copyThroughOrWarn(compilation, opts) {
3477
6914
  return buffer.toString();
3478
6915
  }
3479
6916
  const sourceSibling = findSourceSibling(abs);
3480
- const warn = new WebpackError(opts.warning(opts.sourceRel, sourceSibling ? unixify(__rspack_external_node_path_c5b9b54f.relative(opts.manifestDir, sourceSibling)) : void 0));
6917
+ const warn = new core_WebpackError(opts.warning(opts.sourceRel, sourceSibling ? trace_runtime_loaded_files_unixify(__rspack_external_node_path_c5b9b54f.relative(opts.manifestDir, sourceSibling)) : void 0));
3481
6918
  warn.name = opts.warningName;
3482
6919
  warn.file = opts.warningFile;
3483
6920
  compilation.warnings ||= [];
@@ -3489,7 +6926,7 @@ function findSourceSibling(abs) {
3489
6926
  const base = abs.slice(0, -3);
3490
6927
  return SOURCE_SIBLING_EXTENSIONS.map((ext)=>base + ext).find((candidate)=>__rspack_external_node_fs_5ea92f0c.existsSync(candidate));
3491
6928
  }
3492
- function unixify(filePath) {
6929
+ function trace_runtime_loaded_files_unixify(filePath) {
3493
6930
  return filePath.replace(/\\/g, '/');
3494
6931
  }
3495
6932
  function resolveExtensionPath(literal, basePath) {
@@ -3498,7 +6935,7 @@ function resolveExtensionPath(literal, basePath) {
3498
6935
  if (/^[a-zA-Z][\w+.-]*:/.test(trimmed)) return null;
3499
6936
  if (trimmed.startsWith('//')) return null;
3500
6937
  try {
3501
- const base = new URL(`chrome-extension://extension-js/${unixify(basePath)}`);
6938
+ const base = new URL(`chrome-extension://extension-js/${trace_runtime_loaded_files_unixify(basePath)}`);
3502
6939
  const resolved = new URL(trimmed, base);
3503
6940
  if ('extension-js' !== resolved.hostname) return null;
3504
6941
  const pathname = decodeURIComponent(resolved.pathname).replace(/^\/+/, '');
@@ -3511,7 +6948,7 @@ function manifestDeclaredSourcePaths(manifest) {
3511
6948
  const declared = new Set();
3512
6949
  if (!manifest) return declared;
3513
6950
  const add = (value)=>{
3514
- if ('string' == typeof value && value.trim()) declared.add(unixify(value.trim()).replace(/^\/+/, '').split(/[?#]/)[0]);
6951
+ if ('string' == typeof value && value.trim()) declared.add(trace_runtime_loaded_files_unixify(value.trim()).replace(/^\/+/, '').split(/[?#]/)[0]);
3515
6952
  };
3516
6953
  add(manifest.background?.service_worker);
3517
6954
  add(manifest.background?.page);
@@ -3682,7 +7119,7 @@ function fetchLiteralToFsPath(literal) {
3682
7119
  if (/^[a-zA-Z][\w+.-]*:/.test(trimmed)) return null;
3683
7120
  if (trimmed.startsWith('//')) return null;
3684
7121
  const noQuery = trimmed.split(/[?#]/)[0];
3685
- return noQuery ? unixify(noQuery) : null;
7122
+ return noQuery ? trace_runtime_loaded_files_unixify(noQuery) : null;
3686
7123
  }
3687
7124
  function blankComments(source) {
3688
7125
  let out = '';
@@ -3909,14 +7346,14 @@ class WarnUponFolderChanges {
3909
7346
  const typeOfAsset = 'pages' === folder ? 'HTML pages' : "script files";
3910
7347
  const errorMessage = serverRestartRequiredFromSpecialFolderMessageOnly(addingOrRemoving, folder, typeOfAsset);
3911
7348
  if (isAddition) {
3912
- const warn = new WebpackError(errorMessage);
7349
+ const warn = new core_WebpackError(errorMessage);
3913
7350
  warn.name = 'SpecialFoldersChange';
3914
7351
  warn.file = filePath;
3915
7352
  warn.details = `Detected change in ${folder}/ affecting ${typeOfAsset}. Restart may be required for full effect.`;
3916
7353
  compilation.warnings?.push(warn);
3917
7354
  return;
3918
7355
  }
3919
- const err = new WebpackError(errorMessage);
7356
+ const err = new core_WebpackError(errorMessage);
3920
7357
  err.name = 'SpecialFoldersRemoval';
3921
7358
  err.file = filePath;
3922
7359
  err.details = `Removing from ${folder}/ breaks current build. Restart the dev server to recompile.`;
@@ -4317,6 +7754,34 @@ function manifestFieldMessageOnly(manifestField) {
4317
7754
  const fieldLabel = isContentScripts ? `content_scripts (index ${contentIndex})` : manifestFieldName;
4318
7755
  return `Check the ${pintor.yellow(fieldLabel)} field in your ${pintor.yellow('manifest.json')} file.`;
4319
7756
  }
7757
+ function injectCssLink(headNode, feature, firstLinkAttrs, hrefOverride) {
7758
+ const linkTag = __rspack_external_parse5_utilities_78b19c6a.createNode('link');
7759
+ linkTag.attrs = [
7760
+ {
7761
+ name: 'rel',
7762
+ value: 'stylesheet'
7763
+ },
7764
+ {
7765
+ name: 'href',
7766
+ value: hrefOverride || `/${feature}.css`
7767
+ }
7768
+ ];
7769
+ const propagateLinkAttrs = new Set([
7770
+ 'media',
7771
+ 'crossorigin',
7772
+ 'integrity',
7773
+ 'referrerpolicy',
7774
+ 'type',
7775
+ 'disabled'
7776
+ ]);
7777
+ if (firstLinkAttrs) {
7778
+ for (const attr of firstLinkAttrs)if (propagateLinkAttrs.has(attr.name) && !linkTag.attrs.find((a)=>a.name === attr.name)) linkTag.attrs.push({
7779
+ name: attr.name,
7780
+ value: attr.value
7781
+ });
7782
+ }
7783
+ __rspack_external_parse5_utilities_78b19c6a.append(headNode, linkTag);
7784
+ }
4320
7785
  function resolveCssAsset(compilation, feature) {
4321
7786
  if (compilation.getAsset(`${feature}.css`)) return {
4322
7787
  found: true,
@@ -4383,7 +7848,7 @@ function warnIfPublicRootAssetMissing(compilation, htmlEntry, cleanPath) {
4383
7848
  const projectDir = __rspack_external_node_path_c5b9b54f.dirname(__rspack_external_node_path_c5b9b54f.dirname(htmlEntry));
4384
7849
  const publicCandidate = __rspack_external_node_path_c5b9b54f.join(projectDir, 'public', cleanPath.slice(1));
4385
7850
  if (__rspack_external_node_fs_5ea92f0c.existsSync(publicCandidate)) return;
4386
- const warn = new WebpackError(fileNotFound(htmlEntry, cleanPath));
7851
+ const warn = new core_WebpackError(fileNotFound(htmlEntry, cleanPath));
4387
7852
  warn.name = 'HtmlPublicAssetMissing';
4388
7853
  warn.file = htmlEntry;
4389
7854
  const filtered = String(warn.message).split('\n').filter((line)=>!line.includes(String(cleanPath)));
@@ -4711,13 +8176,13 @@ class AddAssetsToCompilation {
4711
8176
  };
4712
8177
  if (processAssetsHook && 'function' == typeof processAssetsHook.tap) processAssetsHook.tap({
4713
8178
  name: 'html:add-assets-to-compilation',
4714
- stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
8179
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
4715
8180
  }, ()=>runner());
4716
8181
  else runner();
4717
8182
  });
4718
8183
  }
4719
8184
  }
4720
- const WILDCARD_HOSTS = new Set([
8185
+ const connectable_host_WILDCARD_HOSTS = new Set([
4721
8186
  '0.0.0.0',
4722
8187
  '::',
4723
8188
  '[::]',
@@ -4727,7 +8192,7 @@ const WILDCARD_HOSTS = new Set([
4727
8192
  ''
4728
8193
  ]);
4729
8194
  function isWildcardHost(host) {
4730
- return WILDCARD_HOSTS.has(String(host ?? '').trim());
8195
+ return connectable_host_WILDCARD_HOSTS.has(String(host ?? '').trim());
4731
8196
  }
4732
8197
  function resolveConnectableHost(bindHost, publicHost) {
4733
8198
  const override = String(publicHost ?? '').trim();
@@ -4900,7 +8365,7 @@ class AddToFileDependencies {
4900
8365
  compiler.hooks.thisCompilation.tap('html:add-to-file-dependencies', (compilation)=>{
4901
8366
  compilation.hooks.processAssets.tap({
4902
8367
  name: 'html:add-to-file-dependencies',
4903
- stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
8368
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
4904
8369
  }, ()=>{
4905
8370
  if (compilation.errors?.length) return;
4906
8371
  const allEntries = this.includeList || {};
@@ -4964,7 +8429,7 @@ class EmitHtmlFile {
4964
8429
  };
4965
8430
  if (processAssetsHook && 'function' == typeof processAssetsHook.tap) processAssetsHook.tap({
4966
8431
  name: 'AddAssetsToCompilationPlugin',
4967
- stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
8432
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
4968
8433
  }, ()=>runner());
4969
8434
  else runner();
4970
8435
  });
@@ -5045,7 +8510,7 @@ function handleCantResolveError(includesList, error, compilation, manifestDir) {
5045
8510
  const errorMsg = fileNotFound(resource, overrideNotFoundPath || wrongFilename, {
5046
8511
  publicRootHint: isPublicRoot
5047
8512
  });
5048
- const warn = new WebpackError(errorMsg);
8513
+ const warn = new core_WebpackError(errorMsg);
5049
8514
  warn.name = 'HtmlEntrypointMissing';
5050
8515
  warn.file = __rspack_external_node_path_c5b9b54f.relative(manifestDir, resource);
5051
8516
  return warn;
@@ -5068,7 +8533,7 @@ class HandleCommonErrors {
5068
8533
  const hasProcessAssets = Boolean(compilation?.hooks?.processAssets?.tap);
5069
8534
  if (hasProcessAssets) return void compilation.hooks.processAssets.tap({
5070
8535
  name: 'html:handle-common-errors',
5071
- stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE
8536
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE
5072
8537
  }, ()=>{
5073
8538
  const errs = [
5074
8539
  ...compilation.errors
@@ -5142,7 +8607,7 @@ class ThrowIfRecompileIsNeeded {
5142
8607
  if (this.hasEntriesChanged(updatedCssEntries, css) || this.hasEntriesChanged(updatedJsEntries, js)) {
5143
8608
  const projectRoot = __rspack_external_node_path_c5b9b54f.dirname(this.manifestPath);
5144
8609
  const relToManifest = __rspack_external_node_path_c5b9b54f.relative(projectRoot, changedFile);
5145
- const err = new WebpackError(serverRestartRequiredFromHtml(relToManifest, changedFile));
8610
+ const err = new core_WebpackError(serverRestartRequiredFromHtml(relToManifest, changedFile));
5146
8611
  err.name = 'HtmlEntrypointChanged';
5147
8612
  err.file = relToManifest;
5148
8613
  compilation.errors.push(err);
@@ -5182,7 +8647,7 @@ class UpdateHtmlFile {
5182
8647
  const hasProcessAssets = Boolean(compilation?.hooks?.processAssets?.tap);
5183
8648
  if (hasProcessAssets) compilation.hooks.processAssets.tap({
5184
8649
  name: 'html:update-html-file',
5185
- stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE
8650
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE
5186
8651
  }, ()=>run());
5187
8652
  else run();
5188
8653
  });
@@ -5282,6 +8747,16 @@ function iconsMissingFile(manifestField, filePath, opts) {
5282
8747
  lines.push(`${pintor.red('NOT FOUND')} ${pintor.underline(filePath)}`);
5283
8748
  return lines.join('\n');
5284
8749
  }
8750
+ function themeImageMissingFile(manifestField, filePath, opts) {
8751
+ const lines = [];
8752
+ lines.push(`Check the ${pintor.yellow(manifestField)} field in your ${pintor.yellow('manifest.json')} file.`);
8753
+ lines.push("The theme image path must point to an existing file that will be packaged with the extension.");
8754
+ lines.push(`Browsers reject the whole extension when this file is missing.\nThe build stops here to protect you.`);
8755
+ if (opts?.publicRootHint) lines.push(`Paths starting with '/' are resolved from the extension output root (served from ${pintor.yellow('public/')}), not your source directory.`);
8756
+ lines.push('');
8757
+ lines.push(`${pintor.red('NOT FOUND')} ${pintor.underline(filePath)}`);
8758
+ return lines.join('\n');
8759
+ }
5285
8760
  function themeImageIsEmpty(manifestField, filePath) {
5286
8761
  const lines = [];
5287
8762
  lines.push(`Check the ${pintor.yellow(manifestField)} field in your ${pintor.yellow('manifest.json')} file.`);
@@ -5345,7 +8820,7 @@ class add_to_file_dependencies_AddToFileDependencies {
5345
8820
  compiler.hooks.thisCompilation.tap('icons:add-to-file-dependencies', (compilation)=>{
5346
8821
  compilation.hooks.processAssets.tap({
5347
8822
  name: 'icons:add-to-file-dependencies',
5348
- stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
8823
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
5349
8824
  }, ()=>{
5350
8825
  if (compilation.errors?.length) return;
5351
8826
  const iconFields = this.includeList || {};
@@ -5381,7 +8856,7 @@ class EmitFile {
5381
8856
  compiler.hooks.thisCompilation.tap('icons:emit-file', (compilation)=>{
5382
8857
  compilation.hooks.processAssets.tap({
5383
8858
  name: 'icons:emit-file',
5384
- stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER
8859
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER
5385
8860
  }, ()=>{
5386
8861
  if (compilation.errors.length > 0) return;
5387
8862
  const iconFields = this.includeList || {};
@@ -5444,7 +8919,9 @@ class EmitFile {
5444
8919
  const displayPath = !__rspack_external_node_path_c5b9b54f.isAbsolute(entry) && isPublicRoot ? __rspack_external_node_path_c5b9b54f.join(projectPath, 'public', entry.slice(1)) : __rspack_external_node_path_c5b9b54f.isAbsolute(entry) ? entry : __rspack_external_node_path_c5b9b54f.join(projectPath, entry);
5445
8920
  const isFatal = 'icons' === group || isDefaultIconFamily || isThemeImage;
5446
8921
  const severity = isFatal ? 'error' : 'warning';
5447
- reportToCompilation(compilation, compiler, iconsMissingFile(feature, displayPath, {
8922
+ reportToCompilation(compilation, compiler, isThemeImage ? themeImageMissingFile(feature, displayPath, {
8923
+ publicRootHint: isPublicRoot
8924
+ }) : iconsMissingFile(feature, displayPath, {
5448
8925
  publicRootHint: isPublicRoot,
5449
8926
  fatal: isFatal
5450
8927
  }), severity, 'manifest.json');
@@ -5564,7 +9041,7 @@ function validateJsonAsset(compilation, feature, filePath, buf) {
5564
9041
  try {
5565
9042
  parsed = JSON.parse(stripBom(buf.toString('utf-8')));
5566
9043
  } catch (e) {
5567
- const err = new WebpackError(invalidJsonSyntax(feature, filePath, String(e?.message || e)));
9044
+ const err = new core_WebpackError(invalidJsonSyntax(feature, filePath, String(e?.message || e)));
5568
9045
  err.file = filePath;
5569
9046
  err.name = 'JSONInvalidSyntax';
5570
9047
  compilation.errors.push(err);
@@ -5572,7 +9049,7 @@ function validateJsonAsset(compilation, feature, filePath, buf) {
5572
9049
  }
5573
9050
  if (feature.startsWith('declarative_net_request')) {
5574
9051
  if (!Array.isArray(parsed)) {
5575
- const err = new WebpackError(invalidRulesetStructure(feature, filePath));
9052
+ const err = new core_WebpackError(invalidRulesetStructure(feature, filePath));
5576
9053
  err.file = filePath;
5577
9054
  err.name = 'DNRInvalidRuleset';
5578
9055
  compilation.errors.push(err);
@@ -5580,7 +9057,7 @@ function validateJsonAsset(compilation, feature, filePath, buf) {
5580
9057
  }
5581
9058
  } else if ('storage.managed_schema' === feature) {
5582
9059
  if (null === parsed || Array.isArray(parsed) || 'object' != typeof parsed) {
5583
- const err = new WebpackError(invalidManagedSchemaStructure(feature, filePath));
9060
+ const err = new core_WebpackError(invalidManagedSchemaStructure(feature, filePath));
5584
9061
  err.file = filePath;
5585
9062
  err.name = 'ManagedSchemaInvalid';
5586
9063
  compilation.errors.push(err);
@@ -5623,7 +9100,7 @@ function processJsonAssets(compilation, manifestPath, includeList) {
5623
9100
  const outputRoot = compilation?.options?.output?.path || '';
5624
9101
  const displayPath = isPublicRoot ? __rspack_external_node_path_c5b9b54f.join(outputRoot, rawRef.slice(1)) : abs;
5625
9102
  const isFatal = isCriticalJsonFeature(feature);
5626
- const notFound = new WebpackError(jsonMissingFile(feature, displayPath, {
9103
+ const notFound = new core_WebpackError(jsonMissingFile(feature, displayPath, {
5627
9104
  publicRootHint: isPublicRoot,
5628
9105
  fatal: isFatal
5629
9106
  }));
@@ -5706,7 +9183,7 @@ class JsonPlugin {
5706
9183
  compiler.hooks.thisCompilation.tap('json:module', (compilation)=>{
5707
9184
  compilation.hooks.processAssets.tap({
5708
9185
  name: 'json:module',
5709
- stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
9186
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
5710
9187
  }, ()=>{
5711
9188
  processJsonAssets(compilation, this.manifestPath, this.includeList || {});
5712
9189
  trackJsonDependencies(compilation, this.manifestPath, this.includeList || {});
@@ -5901,11 +9378,11 @@ function validateLocales(compiler, compilation, manifestPath) {
5901
9378
  const dict = defaultLocaleMessages;
5902
9379
  const collectMsgKeys = (value, acc)=>{
5903
9380
  if ('string' == typeof value) {
5904
- const regex = /__MSG_([a-zA-Z0-9_]+)__/g;
9381
+ const regex = /__MSG_([A-Za-z0-9_@]+?)__/g;
5905
9382
  let matches;
5906
9383
  while(null !== (matches = regex.exec(value))){
5907
9384
  const key = matches[1];
5908
- if (key) acc.add(key);
9385
+ if (key && !key.startsWith('@@')) acc.add(key);
5909
9386
  }
5910
9387
  } else if (Array.isArray(value)) for (const item of value)collectMsgKeys(item, acc);
5911
9388
  else if (value && 'object' == typeof value) for (const v of Object.values(value))collectMsgKeys(v, acc);
@@ -5963,7 +9440,7 @@ class LocalesPlugin {
5963
9440
  compiler.hooks.thisCompilation.tap('locales:module', (compilation)=>{
5964
9441
  compilation.hooks.processAssets.tap({
5965
9442
  name: 'locales:module',
5966
- stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
9443
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
5967
9444
  }, ()=>{
5968
9445
  if (!__rspack_external_node_fs_5ea92f0c.existsSync(this.manifestPath)) {
5969
9446
  const ErrorConstructor = compiler?.rspack?.WebpackError || Error;
@@ -6010,6 +9487,15 @@ function fatalManifestShapeFixed(field, detail) {
6010
9487
  lines.push(`${pintor.brightBlue('FIELD')} ${pintor.underline(field)}, ${detail}`);
6011
9488
  return lines.join('\n');
6012
9489
  }
9490
+ function invalidThemeValue(field, detail, value) {
9491
+ const lines = [];
9492
+ lines.push(`Check the ${pintor.yellow(field)} field in your ${pintor.yellow('manifest.json')} file.`);
9493
+ lines.push(detail);
9494
+ lines.push(`Chrome rejects the whole extension when this value is malformed.\nThe build stops here to protect you.`);
9495
+ lines.push('');
9496
+ lines.push(`${pintor.red('INVALID VALUE')} ${value}`);
9497
+ return lines.join('\n');
9498
+ }
6013
9499
  function missingGeckoDataCollectionPermissions() {
6014
9500
  const lines = [];
6015
9501
  lines.push(`${messaging_prefix('warn')} addons.mozilla.org requires ${pintor.yellow('browser_specific_settings.gecko.data_collection_permissions')} for new add-ons.`);
@@ -6340,7 +9826,7 @@ class ApplyDevDefaults {
6340
9826
  if (!compilation?.hooks?.processAssets) return;
6341
9827
  compilation.hooks.processAssets.tap({
6342
9828
  name: 'manifest:apply-dev-defaults',
6343
- stage: Compilation.PROCESS_ASSETS_STAGE_REPORT + 100
9829
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT + 100
6344
9830
  }, ()=>{
6345
9831
  if (compilation.errors.length > 0) return;
6346
9832
  if (!this.manifestPath) {
@@ -6423,7 +9909,7 @@ class EmitManifest {
6423
9909
  compiler.hooks.thisCompilation.tap('manifest:emit-manifest', (compilation)=>{
6424
9910
  compilation.hooks.processAssets.tap({
6425
9911
  name: 'manifest:emit-manifest',
6426
- stage: Compilation.PROCESS_ASSETS_STAGE_PRE_PROCESS
9912
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_PRE_PROCESS
6427
9913
  }, ()=>{
6428
9914
  let jsonContent;
6429
9915
  try {
@@ -6461,7 +9947,7 @@ class ManifestLegacyWarnings {
6461
9947
  compiler.hooks.thisCompilation.tap(ManifestLegacyWarnings.name, (compilation)=>{
6462
9948
  compilation.hooks.processAssets.tap({
6463
9949
  name: ManifestLegacyWarnings.name,
6464
- stage: Compilation.PROCESS_ASSETS_STAGE_REPORT
9950
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT
6465
9951
  }, ()=>{
6466
9952
  const asset = compilation.getAsset('manifest.json');
6467
9953
  if (!asset) return;
@@ -6469,7 +9955,7 @@ class ManifestLegacyWarnings {
6469
9955
  let count = 0;
6470
9956
  legacy.forEach((needle)=>{
6471
9957
  if (text.includes(needle)) {
6472
- const warn = new WebpackError(legacyManifestPathWarning(needle));
9958
+ const warn = new core_WebpackError(legacyManifestPathWarning(needle));
6473
9959
  warn.name = 'ManifestLegacyWarning';
6474
9960
  warn.file = 'manifest.json';
6475
9961
  compilation.warnings.push(warn);
@@ -6546,11 +10032,11 @@ function warPatchedSummary(v3Groups, v3ResourcesTotal, v2Resources) {
6546
10032
  return `${messaging_prefix('debug')} war patched v3Groups=${String(v3Groups)} v3Resources=${String(v3ResourcesTotal)} v2Resources=${String(v2Resources)}`;
6547
10033
  }
6548
10034
  function isPublicRootLike(possiblePath) {
6549
- const normalizedPath = paths_unixify(possiblePath || '');
10035
+ const normalizedPath = unixify(possiblePath || '');
6550
10036
  return normalizedPath.startsWith('/') || /^(?:\.\/)?public\//i.test(normalizedPath) || /^\/public\//i.test(normalizedPath);
6551
10037
  }
6552
10038
  function toPublicOutput(possiblePath) {
6553
- const normalizedPath = paths_unixify(possiblePath || '');
10039
+ const normalizedPath = unixify(possiblePath || '');
6554
10040
  if (/^\/public\//i.test(normalizedPath)) return normalizedPath.replace(/^\/public\//i, '');
6555
10041
  if (/^(?:\.\/)?public\//i.test(normalizedPath)) return normalizedPath.replace(/^(?:\.\/)?public\//i, '');
6556
10042
  if (/^\//.test(normalizedPath)) return normalizedPath.replace(/^\//, '');
@@ -6573,7 +10059,7 @@ function emitDirectoryAsAssets(compilation, absDir, baseDir) {
6573
10059
  continue;
6574
10060
  }
6575
10061
  if (!entry.isFile()) continue;
6576
- const outName = paths_unixify(__rspack_external_node_path_c5b9b54f.relative(baseDir, full));
10062
+ const outName = unixify(__rspack_external_node_path_c5b9b54f.relative(baseDir, full));
6577
10063
  if (!compilation.getAsset(outName)) compilation.emitAsset(outName, new core_sources.RawSource(__rspack_external_node_fs_5ea92f0c.readFileSync(full)));
6578
10064
  compilation.fileDependencies.add(full);
6579
10065
  }
@@ -6592,7 +10078,7 @@ function emitFileAsAsset(compilation, absPath) {
6592
10078
  }
6593
10079
  if (!compilation.getAsset(outName)) compilation.emitAsset(outName, new core_sources.RawSource(content));
6594
10080
  compilation.fileDependencies.add(absPath);
6595
- return paths_unixify(outName);
10081
+ return unixify(outName);
6596
10082
  }
6597
10083
  function resolve_war_findSourceSibling(absOutputPath) {
6598
10084
  const candidatesByExt = {
@@ -6641,7 +10127,7 @@ function validateMatchesOrReport(compilation, matches, browser) {
6641
10127
  compilation.errors ||= [];
6642
10128
  for (const m of matches)if (!isValidChromeMatchPattern(m)) {
6643
10129
  const msg = warInvalidMatchPattern(m);
6644
- const err = new WebpackError(msg);
10130
+ const err = new core_WebpackError(msg);
6645
10131
  err.file = 'manifest.json';
6646
10132
  err.name = 'WARInvalidMatchPattern';
6647
10133
  compilation.errors.push(err);
@@ -6709,7 +10195,7 @@ function resolveUserDeclaredWAR(compilation, manifestPath, manifest, browser) {
6709
10195
  publicRootHint: true,
6710
10196
  relativeRef: res
6711
10197
  });
6712
- const err = new WebpackError(msg);
10198
+ const err = new core_WebpackError(msg);
6713
10199
  err.file = 'manifest.json';
6714
10200
  compilation.warnings.push(err);
6715
10201
  }
@@ -6733,7 +10219,7 @@ function resolveUserDeclaredWAR(compilation, manifestPath, manifest, browser) {
6733
10219
  relativeRef: res,
6734
10220
  sourceSibling: sourceSibling ? __rspack_external_node_path_c5b9b54f.relative(manifestDir, sourceSibling) : void 0
6735
10221
  });
6736
- const warn = new WebpackError(msg);
10222
+ const warn = new core_WebpackError(msg);
6737
10223
  warn.file = 'manifest.json';
6738
10224
  warn.name = 'WARRelativeAssetMissing';
6739
10225
  compilation.warnings.push(warn);
@@ -6741,11 +10227,11 @@ function resolveUserDeclaredWAR(compilation, manifestPath, manifest, browser) {
6741
10227
  }
6742
10228
  if (__rspack_external_node_fs_5ea92f0c.statSync(abs).isDirectory()) {
6743
10229
  emitDirectoryAsAssets(compilation, abs, manifestDir);
6744
- const dirResource = `${paths_unixify(res).replace(/\/+$/, '')}/*`;
10230
+ const dirResource = `${unixify(res).replace(/\/+$/, '')}/*`;
6745
10231
  pushResource(matches, dirResource, extra);
6746
10232
  return;
6747
10233
  }
6748
- const relOut = paths_unixify(__rspack_external_node_path_c5b9b54f.relative(manifestDir, abs));
10234
+ const relOut = unixify(__rspack_external_node_path_c5b9b54f.relative(manifestDir, abs));
6749
10235
  if (!relOut.startsWith('..')) {
6750
10236
  if (!compilation.getAsset(relOut)) compilation.emitAsset(relOut, new core_sources.RawSource(__rspack_external_node_fs_5ea92f0c.readFileSync(abs)));
6751
10237
  compilation.fileDependencies.add(abs);
@@ -6759,7 +10245,7 @@ function resolveUserDeclaredWAR(compilation, manifestPath, manifest, browser) {
6759
10245
  if ('string' == typeof entry) {
6760
10246
  if (isMv2) return void handleOne(void 0, entry);
6761
10247
  const msg = warStringEntryInMv3(entry);
6762
- const err = new WebpackError(msg);
10248
+ const err = new core_WebpackError(msg);
6763
10249
  err.file = 'manifest.json';
6764
10250
  err.name = 'WARStringEntryInMv3';
6765
10251
  compilation.errors = compilation.errors || [];
@@ -6998,7 +10484,7 @@ class PatchWAR {
6998
10484
  compiler.hooks.thisCompilation.tap('manifest:patch-war', (compilation)=>{
6999
10485
  compilation.hooks.processAssets.tap({
7000
10486
  name: 'manifest:patch-war',
7001
- stage: Compilation.PROCESS_ASSETS_STAGE_REPORT
10487
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT
7002
10488
  }, ()=>{
7003
10489
  if (compilation.errors.length > 0) return;
7004
10490
  if (isStaticThemeSource(this.manifestPath, this.browser)) return;
@@ -7075,7 +10561,7 @@ class PersistManifestToDisk {
7075
10561
  compiler.hooks.thisCompilation.tap('manifest:persist-manifest:capture', (compilation)=>{
7076
10562
  compilation.hooks.processAssets.tap({
7077
10563
  name: 'manifest:persist-manifest:capture',
7078
- stage: Compilation.PROCESS_ASSETS_STAGE_REPORT + 1000
10564
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT + 1000
7079
10565
  }, ()=>{
7080
10566
  pendingHadErrors = compilation.errors.length > 0;
7081
10567
  pendingOutputPath = compilation.outputOptions.path || compiler.options.output?.path;
@@ -7426,7 +10912,7 @@ class UpdateManifest {
7426
10912
  compiler.hooks.thisCompilation.tap('manifest:update-manifest', (compilation)=>{
7427
10913
  compilation.hooks.processAssets.tap({
7428
10914
  name: 'manifest:update-manifest',
7429
- stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE + 1
10915
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE + 1
7430
10916
  }, ()=>{
7431
10917
  if (compilation.errors.length > 0) return;
7432
10918
  const manifest = getManifestContent(compilation, this.manifestPath);
@@ -7452,13 +10938,13 @@ class UpdateManifest {
7452
10938
  const sanitized = sanitizeFatalManifestShapes(patchedManifest, __rspack_external_node_path_c5b9b54f.dirname(this.manifestPath));
7453
10939
  patchedManifest = sanitized.manifest;
7454
10940
  for (const fix of sanitized.fixes){
7455
- const warn = new WebpackError(fatalManifestShapeFixed(fix.field, fix.detail));
10941
+ const warn = new core_WebpackError(fatalManifestShapeFixed(fix.field, fix.detail));
7456
10942
  warn.name = 'ManifestFatalShapeWarning';
7457
10943
  warn.file = 'manifest.json';
7458
10944
  compilation.warnings.push(warn);
7459
10945
  }
7460
10946
  if ('production' === compiler.options.mode && isGeckoBasedBrowser(String(this.browser)) && gecko_data_collection_missingGeckoDataCollectionPermissions(patchedManifest)) {
7461
- const warn = new WebpackError(missingGeckoDataCollectionPermissions());
10947
+ const warn = new core_WebpackError(missingGeckoDataCollectionPermissions());
7462
10948
  warn.name = 'AmoDataCollectionWarning';
7463
10949
  warn.file = 'manifest.json';
7464
10950
  compilation.warnings.push(warn);
@@ -7472,6 +10958,68 @@ class UpdateManifest {
7472
10958
  });
7473
10959
  }
7474
10960
  }
10961
+ const isChannel = (entry)=>'number' == typeof entry && Number.isInteger(entry) && entry >= 0 && entry <= 255;
10962
+ const isFiniteNumber = (entry)=>'number' == typeof entry && Number.isFinite(entry);
10963
+ function colorValueDetail(value) {
10964
+ if (!Array.isArray(value) || 3 !== value.length && 4 !== value.length) return 'Chrome only accepts an [R, G, B] or [R, G, B, A] array here.';
10965
+ for (const entry of value.slice(0, 3))if (!isChannel(entry)) return 'Chrome only accepts integer color channels from 0 to 255 here.';
10966
+ if (4 === value.length && !isFiniteNumber(value[3])) return 'Chrome only accepts a numeric alpha channel here.';
10967
+ }
10968
+ function tintValueDetail(value) {
10969
+ if (!Array.isArray(value) || 3 !== value.length || !value.every(isFiniteNumber)) return 'Chrome only accepts a [hue, saturation, lightness] array of 3 numbers here.';
10970
+ }
10971
+ function collectThemeValueIssues(manifest) {
10972
+ const issues = [];
10973
+ const theme = manifest?.theme;
10974
+ if (!theme || 'object' != typeof theme) return issues;
10975
+ const groups = [
10976
+ [
10977
+ 'colors',
10978
+ theme.colors,
10979
+ colorValueDetail
10980
+ ],
10981
+ [
10982
+ 'tints',
10983
+ theme.tints,
10984
+ tintValueDetail
10985
+ ]
10986
+ ];
10987
+ for (const [group, container, detailFor] of groups)if (!(!container || 'object' != typeof container || Array.isArray(container))) for (const [key, value] of Object.entries(container)){
10988
+ const detail = detailFor(value);
10989
+ if (detail) issues.push({
10990
+ field: `theme.${group}.${key}`,
10991
+ detail,
10992
+ value: JSON.stringify(value)
10993
+ });
10994
+ }
10995
+ return issues;
10996
+ }
10997
+ class ValidateThemeValues {
10998
+ static name = 'manifest:validate-theme-values';
10999
+ manifestPath;
11000
+ browser;
11001
+ constructor(options){
11002
+ this.manifestPath = options.manifestPath;
11003
+ this.browser = options.browser || 'chrome';
11004
+ }
11005
+ apply(compiler) {
11006
+ if (!isChromiumBasedBrowser(String(this.browser))) return;
11007
+ compiler.hooks.thisCompilation.tap(ValidateThemeValues.name, (compilation)=>{
11008
+ compilation.hooks.processAssets.tap({
11009
+ name: ValidateThemeValues.name,
11010
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
11011
+ }, ()=>{
11012
+ let manifest;
11013
+ try {
11014
+ manifest = filterKeysForThisBrowser(parseJsonSafe(__rspack_external_node_fs_5ea92f0c.readFileSync(this.manifestPath, 'utf8')), this.browser);
11015
+ } catch {
11016
+ return;
11017
+ }
11018
+ for (const issue of collectThemeValueIssues(manifest))reportToCompilation(compilation, compiler, invalidThemeValue(issue.field, issue.detail, issue.value), 'error', 'manifest.json');
11019
+ });
11020
+ });
11021
+ }
11022
+ }
7475
11023
  class ManifestPlugin {
7476
11024
  manifestPath;
7477
11025
  browser;
@@ -7487,6 +11035,10 @@ class ManifestPlugin {
7487
11035
  manifestPath: this.manifestPath,
7488
11036
  browser: this.browser
7489
11037
  }).apply(compiler);
11038
+ new ValidateThemeValues({
11039
+ manifestPath: this.manifestPath,
11040
+ browser: this.browser
11041
+ }).apply(compiler);
7490
11042
  new UpdateManifest({
7491
11043
  manifestPath: this.manifestPath,
7492
11044
  browser: this.browser
@@ -7540,7 +11092,7 @@ function getMainWorldBridgeScripts(manifestPath, browser = 'chrome') {
7540
11092
  const bridgeScripts = {};
7541
11093
  try {
7542
11094
  const raw = JSON.parse(stripBom(node_fs.readFileSync(manifestPath, 'utf-8')));
7543
- const resolved = manifest_utils_filterKeysForThisBrowser(raw, browser);
11095
+ const resolved = filterKeysForThisBrowser(raw, browser);
7544
11096
  const contentScripts = Array.isArray(resolved?.content_scripts) ? resolved.content_scripts : [];
7545
11097
  const originalCount = contentScripts.length;
7546
11098
  const bridgeSource = resolveMainWorldBridgeSourcePath();
@@ -7857,7 +11409,7 @@ class ValidateContentScriptSyntax {
7857
11409
  compiler.hooks.thisCompilation.tap("scripts:validate-content-script-syntax", (compilation)=>{
7858
11410
  compilation.hooks.processAssets.tap({
7859
11411
  name: "scripts:validate-content-script-syntax",
7860
- stage: Compilation.PROCESS_ASSETS_STAGE_REPORT
11412
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT
7861
11413
  }, ()=>{
7862
11414
  const assets = 'function' == typeof compilation.getAssets ? compilation.getAssets() : [];
7863
11415
  for (const asset of assets){
@@ -7875,7 +11427,7 @@ class ValidateContentScriptSyntax {
7875
11427
  } catch (caught) {
7876
11428
  const error = caught;
7877
11429
  if (error?.name !== 'SyntaxError') continue;
7878
- const err = new WebpackError([
11430
+ const err = new core_WebpackError([
7879
11431
  `${name} is not valid JavaScript: ${error.message}.`,
7880
11432
  "The browser will silently skip an unparsable content script:",
7881
11433
  'it never injects and reports no error anywhere. Fix the syntax',
@@ -8009,7 +11561,7 @@ function collectContentScriptEntryImports(compilation, includeList) {
8009
11561
  const name = entryImportsEntries[i][0];
8010
11562
  const files = entryImportsEntries[i][1];
8011
11563
  const normalizedFiles = [];
8012
- for(let j = 0; j < files.length; j++)normalizedFiles.push(paths_unixify(files[j]));
11564
+ for(let j = 0; j < files.length; j++)normalizedFiles.push(unixify(files[j]));
8013
11565
  entryImports[name] = normalizedFiles;
8014
11566
  }
8015
11567
  return entryImports;
@@ -8023,7 +11575,7 @@ class CollectContentEntryImports {
8023
11575
  compiler.hooks.thisCompilation.tap('plugin-extension:feature-web-resources:collect-entry-imports', (compilation)=>{
8024
11576
  compilation.hooks.processAssets.tap({
8025
11577
  name: 'plugin-extension:feature-web-resources:collect-entry-imports',
8026
- stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE
11578
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE
8027
11579
  }, ()=>{
8028
11580
  const entryImports = collectContentScriptEntryImports(compilation, this.includeList);
8029
11581
  const shared = getSharedFor(compilation);
@@ -8160,7 +11712,7 @@ class ManifestFieldsChangeDetector {
8160
11712
  compiler.hooks.thisCompilation.tap('manifest:fields-change-detector', (compilation)=>{
8161
11713
  compilation.hooks.processAssets.tap({
8162
11714
  name: 'manifest:fields-change-detector',
8163
- stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_COMPATIBILITY
11715
+ stage: core_Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_COMPATIBILITY
8164
11716
  }, ()=>{
8165
11717
  this.emitErrors(compilation);
8166
11718
  this.pending = {};
@@ -8171,21 +11723,21 @@ class ManifestFieldsChangeDetector {
8171
11723
  if (this.pending.scripts) {
8172
11724
  const p = this.pending.scripts;
8173
11725
  const msg = serverRestartRequiredFromManifestError(p.pathAfter || '', p.pathBefore || '');
8174
- const err = new WebpackError(msg);
11726
+ const err = new core_WebpackError(msg);
8175
11727
  err.file = 'manifest.json';
8176
11728
  compilation.errors.push(err);
8177
11729
  }
8178
11730
  if (this.pending.html) {
8179
11731
  const p = this.pending.html;
8180
11732
  const msg = manifestHtmlEntrypointChange(p.manifestField, p.pathAfter, p.pathBefore);
8181
- const err = new WebpackError(msg);
11733
+ const err = new core_WebpackError(msg);
8182
11734
  err.file = 'manifest.json';
8183
11735
  compilation.errors.push(err);
8184
11736
  }
8185
11737
  if (this.pending.icons) {
8186
11738
  const p = this.pending.icons;
8187
11739
  const msg = manifestIconsEntrypointChange(p.manifestField, p.pathAfter, p.pathBefore);
8188
- const err = new WebpackError(msg);
11740
+ const err = new core_WebpackError(msg);
8189
11741
  err.file = 'manifest.json';
8190
11742
  compilation.errors.push(err);
8191
11743
  }
@@ -8197,13 +11749,13 @@ class ManifestFieldsChangeDetector {
8197
11749
  lines.push('');
8198
11750
  if (p.pathBefore) lines.push(`PATH BEFORE ${p.pathBefore}`);
8199
11751
  if (p.pathAfter) lines.push(`PATH AFTER ${p.pathAfter}`);
8200
- const err = new WebpackError(lines.join('\n'));
11752
+ const err = new core_WebpackError(lines.join('\n'));
8201
11753
  err.file = 'manifest.json';
8202
11754
  compilation.errors.push(err);
8203
11755
  }
8204
11756
  }
8205
11757
  }
8206
- class WebExtensionPlugin {
11758
+ class plugin_web_extension_WebExtensionPlugin {
8207
11759
  static name = 'plugin-extension';
8208
11760
  manifestPath;
8209
11761
  browser;
@@ -8282,7 +11834,7 @@ function webpackConfig(projectStructure, devOptions) {
8282
11834
  } catch (error) {
8283
11835
  throw new Error(manifestInvalidJson(manifestPath, error));
8284
11836
  }
8285
- const manifest = manifest_utils_filterKeysForThisBrowser(rawManifest, devOptions.browser);
11837
+ const manifest = filterKeysForThisBrowser(rawManifest, devOptions.browser);
8286
11838
  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));
8287
11839
  const companionUnpackedExtensionDirs = resolveCompanionExtensionDirs({
8288
11840
  projectRoot: packageJsonDir,
@@ -8337,7 +11889,7 @@ function webpackConfig(projectStructure, devOptions) {
8337
11889
  browser: devOptions.browser,
8338
11890
  polyfill: devOptions.polyfill
8339
11891
  }),
8340
- new WebExtensionPlugin({
11892
+ new plugin_web_extension_WebExtensionPlugin({
8341
11893
  manifestPath,
8342
11894
  browser: devOptions.browser
8343
11895
  }),
@@ -8383,7 +11935,8 @@ function webpackConfig(projectStructure, devOptions) {
8383
11935
  apply (compiler) {
8384
11936
  const target = String(devOptions.browser);
8385
11937
  const isChromiumTarget = isChromiumBasedBrowser(target);
8386
- if (manifest?.manifest_version !== 2 || !isChromiumTarget) return;
11938
+ const isThemeManifest = Boolean(manifest?.theme && 'object' == typeof manifest.theme);
11939
+ if (manifest?.manifest_version !== 2 || !isChromiumTarget || isThemeManifest) return;
8387
11940
  compiler.hooks.thisCompilation.tap('warn-mv2-on-chromium', (compilation)=>{
8388
11941
  const ErrorCtor = compiler.rspack?.WebpackError || Error;
8389
11942
  const usesBlockingWebRequest = Array.isArray(manifest.permissions) && manifest.permissions.includes('webRequestBlocking');
@@ -8415,7 +11968,8 @@ function webpackConfig(projectStructure, devOptions) {
8415
11968
  instanceId: devOptions.instanceId,
8416
11969
  controlPort: devOptions.controlPort,
8417
11970
  controlPath: devOptions.controlPath,
8418
- logsPath: devOptions.logsPath
11971
+ logsPath: devOptions.logsPath,
11972
+ managedExtensionDirs: unpackedExtensionDirsToLoad.filter((dir)=>__rspack_external_node_path_c5b9b54f.resolve(dir) !== __rspack_external_node_path_c5b9b54f.resolve(primaryExtensionOutputDir))
8419
11973
  }));
8420
11974
  if (devOptions.browsersPlugin) {
8421
11975
  const browsersPlugin = devOptions.browsersPlugin;