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.
- package/dist/0~dev-server.mjs +8 -3
- package/dist/0~rspack-config.mjs +3759 -205
- package/dist/101.mjs +65 -2
- package/dist/349.mjs +2 -0
- package/dist/494.mjs +7 -1
- package/dist/839.mjs +164 -3619
- package/dist/887.mjs +5 -1
- package/dist/bridge-entry.d.ts +5 -4
- package/dist/bridge.mjs +25 -5
- package/dist/contract/codes.json +19 -2
- package/dist/contract/envelope.schema.json +10 -1
- package/dist/contract/golden.eval.target-not-found.json +14 -0
- package/dist/contract/golden.open.headed-window-required.json +14 -0
- package/dist/contracts-entry.d.ts +2 -0
- package/dist/contracts.mjs +12 -0
- package/dist/dev-server/control-bridge/consumer-client.d.ts +15 -1
- package/dist/dev-server/control-bridge/contracts.d.ts +6 -0
- package/dist/dev-server/control-bridge/producer-runtime.d.ts +1 -1
- package/dist/ensure-hmr-for-scripts.js +1 -1
- package/dist/ensure-hmr-for-scripts.mjs +1 -1
- package/dist/feature-scripts-content-script-wrapper.js +2 -2
- package/dist/feature-scripts-content-script-wrapper.mjs +2 -2
- package/dist/lib/extension-id.d.ts +9 -0
- package/dist/lib/messaging.d.ts +9 -0
- package/dist/lib/session-paths.d.ts +2 -0
- package/dist/plugin-playwright/index.d.ts +7 -0
- package/dist/plugin-web-extension/feature-icons/messages.d.ts +3 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-lib/theme-values.d.ts +7 -0
- package/dist/plugin-web-extension/feature-manifest/messages.d.ts +1 -0
- package/dist/plugin-web-extension/feature-manifest/steps/validate-theme-values.d.ts +9 -0
- package/package.json +10 -1
package/dist/0~rspack-config.mjs
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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 (
|
|
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 && !
|
|
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 (
|
|
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 (
|
|
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 && !
|
|
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 && !
|
|
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 =
|
|
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
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
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
|
|
3134
|
-
|
|
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
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
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
|
-
|
|
3155
|
-
|
|
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
|
|
3162
|
-
if (
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
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
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
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
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
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:
|
|
3198
|
-
stage:
|
|
3199
|
-
}, ()=>
|
|
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
|
|
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:
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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/${
|
|
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(
|
|
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 ?
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
8179
|
+
stage: core_Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
|
|
4715
8180
|
}, ()=>runner());
|
|
4716
8181
|
else runner();
|
|
4717
8182
|
});
|
|
4718
8183
|
}
|
|
4719
8184
|
}
|
|
4720
|
-
const
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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_([
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 = `${
|
|
10230
|
+
const dirResource = `${unixify(res).replace(/\/+$/, '')}/*`;
|
|
6745
10231
|
pushResource(matches, dirResource, extra);
|
|
6746
10232
|
return;
|
|
6747
10233
|
}
|
|
6748
|
-
const relOut =
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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 =
|
|
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:
|
|
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
|
|
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(
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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;
|