extension-develop 4.0.11 → 4.0.12
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 +1 -1
- package/dist/0~rspack-config.mjs +107 -3154
- package/dist/101.mjs +32 -4
- package/dist/266.mjs +1 -4
- package/dist/839.mjs +3156 -59
- package/dist/bridge-entry.d.ts +5 -0
- package/dist/command-build.d.ts +3 -0
- package/dist/command-dev.d.ts +5 -0
- package/dist/command-preview.d.ts +30 -0
- package/dist/dev-server/auto-exit.d.ts +1 -0
- package/dist/dev-server/cleanup.d.ts +11 -0
- package/dist/dev-server/compiler-hooks.d.ts +9 -0
- package/dist/dev-server/connectable-host.d.ts +2 -0
- package/dist/dev-server/control-bridge/actions-file.d.ts +38 -0
- package/dist/dev-server/control-bridge/broker.d.ts +122 -0
- package/dist/dev-server/control-bridge/consumer-client.d.ts +43 -0
- package/dist/dev-server/control-bridge/contracts.d.ts +164 -0
- package/dist/dev-server/control-bridge/control-port-store.d.ts +25 -0
- package/dist/dev-server/control-bridge/controller-client.d.ts +30 -0
- package/dist/dev-server/control-bridge/logs-file.d.ts +30 -0
- package/dist/dev-server/control-bridge/producer-runtime.d.ts +30 -0
- package/dist/dev-server/control-bridge/ring-buffer.d.ts +21 -0
- package/dist/dev-server/control-bridge/session-token.d.ts +18 -0
- package/dist/dev-server/control-bridge/ws-control-server.d.ts +13 -0
- package/dist/dev-server/frameworks.d.ts +2 -0
- package/dist/dev-server/index.d.ts +3 -0
- package/dist/dev-server/messages.d.ts +17 -0
- package/dist/dev-server/port-manager.d.ts +17 -0
- package/dist/extension-js-devtools/extension-js/chrome/events.ndjson +2 -6
- package/dist/extension-js-devtools/extension-js/chrome/ready.json +9 -6
- package/dist/extension-js-devtools/extension-js/chromium/events.ndjson +2 -6
- package/dist/extension-js-devtools/extension-js/chromium/ready.json +9 -6
- package/dist/extension-js-devtools/extension-js/edge/events.ndjson +2 -6
- package/dist/extension-js-devtools/extension-js/edge/ready.json +9 -6
- package/dist/extension-js-devtools/extension-js/firefox/events.ndjson +2 -6
- package/dist/extension-js-devtools/extension-js/firefox/ready.json +9 -6
- package/dist/extension-js-theme/extension-js/chrome/events.ndjson +2 -6
- package/dist/extension-js-theme/extension-js/chrome/ready.json +9 -6
- package/dist/extension-js-theme/extension-js/chromium/events.ndjson +2 -6
- package/dist/extension-js-theme/extension-js/chromium/ready.json +9 -6
- package/dist/extension-js-theme/extension-js/edge/events.ndjson +2 -6
- package/dist/extension-js-theme/extension-js/edge/ready.json +9 -6
- package/dist/extension-js-theme/extension-js/firefox/events.ndjson +2 -8
- package/dist/extension-js-theme/extension-js/firefox/ready.json +9 -6
- package/dist/lib/branding.d.ts +7 -0
- package/dist/lib/build-summary.d.ts +9 -0
- package/dist/lib/config-loader.d.ts +38 -0
- package/dist/lib/constants.d.ts +11 -0
- package/dist/lib/dark-mode.d.ts +9 -0
- package/dist/lib/dev-server-client-import.d.ts +4 -0
- package/dist/lib/develop-context.d.ts +3 -0
- package/dist/lib/ensure-develop-artifacts.d.ts +10 -0
- package/dist/lib/extensions-to-load.d.ts +1 -0
- package/dist/lib/generate-extension-types.d.ts +1 -0
- package/dist/lib/has-dependency.d.ts +6 -0
- package/dist/lib/manifest-utils.d.ts +2 -0
- package/dist/lib/messages.d.ts +55 -0
- package/dist/lib/optional-dependency-types.d.ts +14 -0
- package/dist/lib/optional-deps-contracts.d.ts +13 -0
- package/dist/lib/optional-deps-resolver.d.ts +41 -0
- package/dist/lib/package-json.d.ts +3 -0
- package/dist/lib/package-manager.d.ts +61 -0
- package/dist/lib/parse-json-safe.d.ts +13 -0
- package/dist/lib/paths.d.ts +19 -0
- package/dist/lib/project-manifest.d.ts +60 -0
- package/dist/lib/project.d.ts +7 -0
- package/dist/lib/resource-path.d.ts +4 -0
- package/dist/lib/rust-min-stack.d.ts +2 -0
- package/dist/lib/sanitize.d.ts +1 -0
- package/dist/lib/session-paths.d.ts +39 -0
- package/dist/lib/stats-handler.d.ts +1 -0
- package/dist/lib/transpile-packages.d.ts +7 -0
- package/dist/lib/validate-user-dependencies.d.ts +1 -0
- package/dist/lib/zip.d.ts +2 -0
- package/dist/module.d.ts +8 -0
- package/dist/plugin-browsers/index.d.ts +108 -0
- package/dist/plugin-browsers/safari-dev-plugin.d.ts +19 -0
- package/dist/plugin-compatibility/compatibility-lib/messages.d.ts +5 -0
- package/dist/plugin-compatibility/feature-polyfill.d.ts +11 -0
- package/dist/plugin-compatibility/index.d.ts +12 -0
- package/dist/plugin-compilation/boring.d.ts +11 -0
- package/dist/plugin-compilation/clean-dist.d.ts +8 -0
- package/dist/plugin-compilation/compilation-lib/messages.d.ts +11 -0
- package/dist/plugin-compilation/compilation-lib/shared-state.d.ts +7 -0
- package/dist/plugin-compilation/env.d.ts +17 -0
- package/dist/plugin-compilation/index.d.ts +22 -0
- package/dist/plugin-compilation/zip.d.ts +18 -0
- package/dist/plugin-css/common-style-loaders.d.ts +8 -0
- package/dist/plugin-css/css-in-content-script-loader.d.ts +4 -0
- package/dist/plugin-css/css-in-html-loader.d.ts +4 -0
- package/dist/plugin-css/css-lib/build-css-rules.d.ts +12 -0
- package/dist/plugin-css/css-lib/inject-css-link.d.ts +16 -0
- package/dist/plugin-css/css-lib/is-content-script.d.ts +1 -0
- package/dist/plugin-css/css-lib/messages.d.ts +9 -0
- package/dist/plugin-css/css-lib/resolve-css-asset.d.ts +21 -0
- package/dist/plugin-css/css-parse-guard-loader.d.ts +8 -0
- package/dist/plugin-css/css-tools/less.d.ts +7 -0
- package/dist/plugin-css/css-tools/postcss.d.ts +4 -0
- package/dist/plugin-css/css-tools/sass.d.ts +10 -0
- package/dist/plugin-css/css-tools/tailwind.d.ts +2 -0
- package/dist/plugin-css/index.d.ts +26 -0
- package/dist/plugin-css/preprocessor-passthrough-loader.d.ts +6 -0
- package/dist/plugin-js-frameworks/frameworks-lib/integrations.d.ts +4 -0
- package/dist/plugin-js-frameworks/index.d.ts +18 -0
- package/dist/plugin-js-frameworks/js-frameworks-lib/load-loader-options.d.ts +2 -0
- package/dist/plugin-js-frameworks/js-frameworks-lib/messages.d.ts +10 -0
- package/dist/plugin-js-frameworks/js-tools/preact.d.ts +3 -0
- package/dist/plugin-js-frameworks/js-tools/react.d.ts +8 -0
- package/dist/plugin-js-frameworks/js-tools/svelte.d.ts +4 -0
- package/dist/plugin-js-frameworks/js-tools/typescript.d.ts +37 -0
- package/dist/plugin-js-frameworks/js-tools/vue.d.ts +3 -0
- package/dist/plugin-perf-budgets/categorize.d.ts +3 -0
- package/dist/plugin-perf-budgets/index.d.ts +34 -0
- package/dist/plugin-perf-budgets/messages.d.ts +8 -0
- package/dist/plugin-playwright/index.d.ts +89 -0
- package/dist/plugin-reload/classify-reload.d.ts +86 -0
- package/dist/plugin-reload/index.d.ts +29 -0
- package/dist/plugin-reload/messages.d.ts +1 -0
- package/dist/plugin-reload/reload-dispatch.d.ts +46 -0
- package/dist/plugin-reload/reload-lib/minimum-files/minimum-background-file-chromium.d.ts +0 -0
- package/dist/plugin-reload/reload-lib/minimum-files/minimum-background-file-firefox.d.ts +0 -0
- package/dist/plugin-reload/reload-lib/scripts-replay-shim.d.ts +19 -0
- package/dist/plugin-reload/steps/inject-bridge-producer.d.ts +14 -0
- package/dist/plugin-reload/steps/inject-bridge-relay.d.ts +4 -0
- package/dist/plugin-reload/steps/inject-scripts-replay-shim.d.ts +10 -0
- package/dist/plugin-reload/steps/prune-stale-hot-updates.d.ts +20 -0
- package/dist/plugin-reload/steps/remove-content-script-dev-server-runtime.d.ts +2 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/index.d.ts +9 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/setup-background-entry.d.ts +13 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/index.d.ts +2 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/lib/webpack5/ChunkLoader.d.ts +9 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/lib/webpack5/HMRDevServer.d.ts +3 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/lib/webpack5/InitialChunkFile.d.ts +5 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/lib/webpack5/NoDangerNamePlugin.d.ts +3 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/lib/webpack5/RuntimeModules/AutoPublicPath.d.ts +2 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/lib/webpack5/RuntimeModules/BaseUri.d.ts +2 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/lib/webpack5/RuntimeModules/BrowserRuntime.d.ts +8 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/lib/webpack5/RuntimeModules/ChunkLoaderFallback.d.ts +1 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/lib/webpack5/RuntimeModules/EagerlyLoadChunks.d.ts +1 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/lib/webpack5/RuntimeModules/LoadScript.d.ts +4 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/lib/webpack5/RuntimeModules/PublicPath.d.ts +2 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/lib/webpack5/ServiceWorkerPlugin.d.ts +6 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/lib/webpack5/helper.d.ts +12 -0
- package/dist/plugin-reload/steps/setup-reload-strategy/webpack-target-webextension-fork/lib/webpack5/index.d.ts +5 -0
- package/dist/plugin-reload/steps/strip-content-script-dev-server-runtime.d.ts +4 -0
- package/dist/plugin-special-folders/check-manifest-in-public.d.ts +2 -0
- package/dist/plugin-special-folders/emit-root-absolute-refs.d.ts +2 -0
- package/dist/plugin-special-folders/folder-extensions/resolve-config.d.ts +6 -0
- package/dist/plugin-special-folders/folder-extensions/resolve-dirs.d.ts +5 -0
- package/dist/plugin-special-folders/folder-extensions/types.d.ts +17 -0
- package/dist/plugin-special-folders/folder-extensions/utils.d.ts +9 -0
- package/dist/plugin-special-folders/get-data.d.ts +12 -0
- package/dist/plugin-special-folders/index.d.ts +20 -0
- package/dist/plugin-special-folders/messages.d.ts +3 -0
- package/dist/plugin-special-folders/warn-upon-folder-changes.d.ts +13 -0
- package/dist/plugin-static-assets/index.d.ts +10 -0
- package/dist/plugin-static-assets/static-assets-lib/messages.d.ts +8 -0
- package/dist/plugin-wasm/index.d.ts +15 -0
- package/dist/plugin-web-extension/feature-html/html-lib/assets.d.ts +2 -0
- package/dist/plugin-web-extension/feature-html/html-lib/inject.d.ts +4 -0
- package/dist/plugin-web-extension/feature-html/html-lib/messages.d.ts +24 -0
- package/dist/plugin-web-extension/feature-html/html-lib/parse-html.d.ts +8 -0
- package/dist/plugin-web-extension/feature-html/html-lib/patch-html.d.ts +8 -0
- package/dist/plugin-web-extension/feature-html/html-lib/utils.d.ts +46 -0
- package/dist/plugin-web-extension/feature-html/index.d.ts +36 -0
- package/dist/plugin-web-extension/feature-html/steps/add-assets-to-compilation.d.ts +9 -0
- package/dist/plugin-web-extension/feature-html/steps/add-scripts-and-styles-to-compilation.d.ts +9 -0
- package/dist/plugin-web-extension/feature-html/steps/add-to-file-dependencies.d.ts +9 -0
- package/dist/plugin-web-extension/feature-html/steps/emit-html-file.d.ts +9 -0
- package/dist/plugin-web-extension/feature-html/steps/ensure-hmr-for-scripts.d.ts +2 -0
- package/dist/plugin-web-extension/feature-html/steps/handle-common-errors.d.ts +9 -0
- package/dist/plugin-web-extension/feature-html/steps/minimum-script-file.d.ts +1 -0
- package/dist/plugin-web-extension/feature-html/steps/preact-refresh-shim.d.ts +1 -0
- package/dist/plugin-web-extension/feature-html/steps/throw-if-recompile-is-needed.d.ts +12 -0
- package/dist/plugin-web-extension/feature-html/steps/update-html-file.d.ts +9 -0
- package/dist/plugin-web-extension/feature-icons/index.d.ts +22 -0
- package/dist/plugin-web-extension/feature-icons/messages.d.ts +12 -0
- package/dist/plugin-web-extension/feature-icons/normalize-keys.d.ts +3 -0
- package/dist/plugin-web-extension/feature-icons/steps/add-to-file-dependencies.d.ts +8 -0
- package/dist/plugin-web-extension/feature-icons/steps/emit-file.d.ts +8 -0
- package/dist/plugin-web-extension/feature-json/index.d.ts +18 -0
- package/dist/plugin-web-extension/feature-json/json-validation.d.ts +3 -0
- package/dist/plugin-web-extension/feature-json/messages.d.ts +17 -0
- package/dist/plugin-web-extension/feature-json/process-assets.d.ts +2 -0
- package/dist/plugin-web-extension/feature-json/track-dependencies.d.ts +2 -0
- package/dist/plugin-web-extension/feature-locales/compilation-error.d.ts +2 -0
- package/dist/plugin-web-extension/feature-locales/get-locales.d.ts +2 -0
- package/dist/plugin-web-extension/feature-locales/index.d.ts +12 -0
- package/dist/plugin-web-extension/feature-locales/messages.d.ts +13 -0
- package/dist/plugin-web-extension/feature-locales/process-assets.d.ts +2 -0
- package/dist/plugin-web-extension/feature-locales/track-dependencies.d.ts +2 -0
- package/dist/plugin-web-extension/feature-locales/validation.d.ts +2 -0
- package/dist/plugin-web-extension/feature-manifest/index.d.ts +21 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-lib/manifest.d.ts +10 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-lib/sanitize-fatal-shapes.d.ts +45 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/background.d.ts +6 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/chrome_url_overrides.d.ts +8 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/commands.d.ts +16 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/content_scripts.d.ts +4 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/content_security_policy.d.ts +4 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/devtools_page.d.ts +4 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/icons.d.ts +6 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/index.d.ts +2 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/omnibox.d.ts +9 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/options_page.d.ts +4 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/options_ui.d.ts +8 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/page_action.d.ts +8 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/permissions.d.ts +4 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/sandbox.d.ts +7 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/sidebar_action.d.ts +2 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/storage.d.ts +6 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/theme.d.ts +2 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/user_scripts.d.ts +2 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/common/web_accessible_resources.d.ts +4 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/index.d.ts +2 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/mv2/background.d.ts +6 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/mv2/browser_action.d.ts +12 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/mv2/chrome_settings_overrides.d.ts +24 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/mv2/index.d.ts +2 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/mv2/theme_experiment.d.ts +7 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/mv3/action.d.ts +8 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/mv3/background.d.ts +6 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/mv3/declarative_net_request.d.ts +2 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/mv3/host_permissions.d.ts +4 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/mv3/index.d.ts +2 -0
- package/dist/plugin-web-extension/feature-manifest/manifest-overrides/mv3/side_panel.d.ts +2 -0
- package/dist/plugin-web-extension/feature-manifest/messages.d.ts +9 -0
- package/dist/plugin-web-extension/feature-manifest/normalize-manifest-path.d.ts +3 -0
- package/dist/plugin-web-extension/feature-manifest/steps/add-dependencies.d.ts +6 -0
- package/dist/plugin-web-extension/feature-manifest/steps/apply-dev-defaults-lib/patch-background.d.ts +10 -0
- package/dist/plugin-web-extension/feature-manifest/steps/apply-dev-defaults-lib/patch-csp.d.ts +5 -0
- package/dist/plugin-web-extension/feature-manifest/steps/apply-dev-defaults-lib/patch-externally-connectable.d.ts +10 -0
- package/dist/plugin-web-extension/feature-manifest/steps/apply-dev-defaults-lib/patch-web-resources.d.ts +19 -0
- package/dist/plugin-web-extension/feature-manifest/steps/apply-dev-defaults.d.ts +12 -0
- package/dist/plugin-web-extension/feature-manifest/steps/emit-manifest.d.ts +7 -0
- package/dist/plugin-web-extension/feature-manifest/steps/legacy-warnings.d.ts +5 -0
- package/dist/plugin-web-extension/feature-manifest/steps/patch-chromium-background.d.ts +15 -0
- package/dist/plugin-web-extension/feature-manifest/steps/patch-dev-content-script-manifest-paths.d.ts +11 -0
- package/dist/plugin-web-extension/feature-manifest/steps/patch-gecko-background.d.ts +15 -0
- package/dist/plugin-web-extension/feature-manifest/steps/patch-war.d.ts +15 -0
- package/dist/plugin-web-extension/feature-manifest/steps/persist-manifest.d.ts +4 -0
- package/dist/plugin-web-extension/feature-manifest/steps/update-manifest.d.ts +9 -0
- package/dist/plugin-web-extension/feature-scripts/contracts.d.ts +12 -0
- package/dist/plugin-web-extension/feature-scripts/index.d.ts +19 -0
- package/dist/plugin-web-extension/feature-scripts/messages.d.ts +7 -0
- package/dist/plugin-web-extension/feature-scripts/scripts-lib/utils.d.ts +2 -0
- package/dist/plugin-web-extension/feature-scripts/steps/add-content-script-wrapper/classic-concat-loader.d.ts +8 -0
- package/dist/plugin-web-extension/feature-scripts/steps/add-content-script-wrapper/content-script-wrapper.d.ts +13 -0
- package/dist/plugin-web-extension/feature-scripts/steps/add-content-script-wrapper/get-bridge-scripts.d.ts +6 -0
- package/dist/plugin-web-extension/feature-scripts/steps/add-content-script-wrapper/index.d.ts +11 -0
- package/dist/plugin-web-extension/feature-scripts/steps/add-public-path-runtime-module.d.ts +4 -0
- package/dist/plugin-web-extension/feature-scripts/steps/add-scripts.d.ts +9 -0
- package/dist/plugin-web-extension/feature-scripts/steps/keep-geturl-imports-native.d.ts +15 -0
- package/dist/plugin-web-extension/feature-scripts/steps/native-geturl-import-loader.d.ts +3 -0
- package/dist/plugin-web-extension/feature-scripts/steps/trace-runtime-loaded-files.d.ts +41 -0
- package/dist/plugin-web-extension/feature-scripts/steps/validate-content-script-syntax.d.ts +18 -0
- package/dist/plugin-web-extension/feature-web-resources/collect-entry-imports.d.ts +3 -0
- package/dist/plugin-web-extension/feature-web-resources/index.d.ts +19 -0
- package/dist/plugin-web-extension/feature-web-resources/steps/collect-content-entry-imports.d.ts +10 -0
- package/dist/plugin-web-extension/feature-web-resources/web-resources-lib/clean-matches.d.ts +7 -0
- package/dist/plugin-web-extension/feature-web-resources/web-resources-lib/generate-manifest.d.ts +11 -0
- package/dist/plugin-web-extension/feature-web-resources/web-resources-lib/messages.d.ts +10 -0
- package/dist/plugin-web-extension/feature-web-resources/web-resources-lib/resolve-war.d.ts +9 -0
- package/dist/plugin-web-extension/feature-web-resources/web-resources-lib/shared.d.ts +4 -0
- package/dist/plugin-web-extension/index.d.ts +9 -0
- package/dist/plugin-web-extension/shared/classic-concat.d.ts +18 -0
- package/dist/plugin-web-extension/shared/compilation-issues.d.ts +6 -0
- package/dist/plugin-web-extension/shared/manifest-fields-change-detector.d.ts +12 -0
- package/dist/plugin-web-extension/shared/paths.d.ts +26 -0
- package/dist/preview-entry.d.ts +3 -0
- package/dist/rslib.config.d.ts +3 -0
- package/dist/rspack-config.d.ts +4 -0
- package/dist/types.d.ts +406 -0
- package/package.json +3 -3
package/dist/839.mjs
CHANGED
|
@@ -4,10 +4,11 @@ import { execFileSync, spawn, spawnSync as external_child_process_spawnSync } fr
|
|
|
4
4
|
import { buildExecEnv, detectPackageManagerFromLockfile } from "prefers-yarn";
|
|
5
5
|
import pintor from "pintor";
|
|
6
6
|
import { EventEmitter } from "node:events";
|
|
7
|
-
import {
|
|
7
|
+
import { Compilation as core_Compilation, WebpackError as core_WebpackError, sources as core_sources } from "@rspack/core";
|
|
8
|
+
import { package_namespaceObject, buildWarningsDetails, sanitize, getDirs, projectInstallFallbackToNpm, assertNoManagedDependencyConflicts, debugDirs, buildSuccess, writingTypeDefinitionsError, projectInstallScriptsDisabled, authorInstallNotice, getSpecialFoldersDataForProjectRoot, buildSuccessWithWarnings, loadCommandConfig, needsInstall, buildCommandFailed, writingTypeDefinitions, constants_isGeckoBasedBrowser, buildWebpack, debugBrowser, normalizeBrowser, debugOutputPath, loadCustomConfig, isChromiumBasedBrowser, devCommandFailed, getDistPath, loadBrowserConfig, getProjectStructure, resolveCompanionExtensionsConfig } from "./101.mjs";
|
|
8
9
|
import { stripBom, parseJsonSafe } from "./23.mjs";
|
|
9
10
|
import { hasProjectDependency, findNearestProjectManifestDirSync } from "./80.mjs";
|
|
10
|
-
import { getCanonicalContentScriptEntryName } from "./291.mjs";
|
|
11
|
+
import { getCanonicalContentScriptEntryName, parseCanonicalContentScriptAsset, getCanonicalContentScriptJsAssetName, getCanonicalContentScriptCssAssetName } from "./291.mjs";
|
|
11
12
|
import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
|
|
12
13
|
import { dirname as __rspack_dirname } from "node:path";
|
|
13
14
|
var __rspack_import_meta_dirname__ = __rspack_dirname(__rspack_fileURLToPath(import.meta.url));
|
|
@@ -1227,6 +1228,7 @@ async function extensionBuild(pathOrRemoteUrl, buildOptions) {
|
|
|
1227
1228
|
extensions: resolvedExtensionsConfig,
|
|
1228
1229
|
browser,
|
|
1229
1230
|
mode: resolvedMode,
|
|
1231
|
+
metadataCommand: buildOptions?.metadataCommand || 'build',
|
|
1230
1232
|
output: {
|
|
1231
1233
|
clean: true,
|
|
1232
1234
|
path: distPath
|
|
@@ -1299,48 +1301,3077 @@ async function extensionBuild(pathOrRemoteUrl, buildOptions) {
|
|
|
1299
1301
|
process.exit(1);
|
|
1300
1302
|
}
|
|
1301
1303
|
}
|
|
1302
|
-
function
|
|
1303
|
-
return
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1304
|
+
function TemplateFn(compilation, Template) {
|
|
1305
|
+
return {
|
|
1306
|
+
f: (args, body)=>{
|
|
1307
|
+
if (!compilation) throw new TypeError('No compilation is found.');
|
|
1308
|
+
if (compilation.runtimeTemplate) return compilation.runtimeTemplate.basicFunction(args, body);
|
|
1309
|
+
return compilation.outputOptions.environment.arrowFunction ? `(${args}) => {\n${Template.indent(body)}\n}` : `function(${args}) {\n${Template.indent(body)}\n}`;
|
|
1310
|
+
},
|
|
1311
|
+
retF: (returnValue, args = '')=>{
|
|
1312
|
+
if (!compilation) throw new TypeError('No compilation is found.');
|
|
1313
|
+
if (compilation.runtimeTemplate) return compilation.runtimeTemplate.returningFunction(returnValue, args);
|
|
1314
|
+
return compilation.outputOptions.environment.arrowFunction ? `(${args}) => (${returnValue})` : `function(${args}) { return ${returnValue}; }`;
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
const RuntimeGlobal = '__webpack_require__.webExtRt';
|
|
1319
|
+
const RuntimeGlobalIsBrowser = '__webpack_require__.webExtRtModern';
|
|
1320
|
+
function BrowserRuntimeModule(webpack, acceptWeak) {
|
|
1321
|
+
const { RuntimeModule, Template } = webpack;
|
|
1322
|
+
class BrowserRuntime extends RuntimeModule {
|
|
1323
|
+
constructor(){
|
|
1324
|
+
super('WebExtensionBrowserRuntime', RuntimeModule.STAGE_NORMAL);
|
|
1325
|
+
}
|
|
1326
|
+
generate() {
|
|
1327
|
+
const { compilation } = this;
|
|
1328
|
+
if (!compilation) return Template.asString('/* [webpack-target-webextension] BrowserRuntimeModule skipped because no compilation is found. */');
|
|
1329
|
+
const optionalChaining = compilation.outputOptions.environment.optionalChaining;
|
|
1330
|
+
const _let = compilation.outputOptions.environment.const ? 'let' : 'var';
|
|
1331
|
+
return Template.asString([
|
|
1332
|
+
`${_let} isChrome, runtime;`,
|
|
1333
|
+
'try {',
|
|
1334
|
+
Template.indent([
|
|
1335
|
+
`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"'}) {`,
|
|
1336
|
+
Template.indent([
|
|
1337
|
+
'runtime = globalThis.browser;'
|
|
1338
|
+
]),
|
|
1339
|
+
'}'
|
|
1340
|
+
]),
|
|
1341
|
+
'} catch (_) {}',
|
|
1342
|
+
'if (!runtime) {',
|
|
1343
|
+
Template.indent([
|
|
1344
|
+
'try {',
|
|
1345
|
+
Template.indent([
|
|
1346
|
+
`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"'}) {`,
|
|
1347
|
+
Template.indent([
|
|
1348
|
+
'isChrome = true;',
|
|
1349
|
+
'runtime = globalThis.chrome;'
|
|
1350
|
+
]),
|
|
1351
|
+
'}'
|
|
1352
|
+
]),
|
|
1353
|
+
'} catch (_) {}'
|
|
1354
|
+
]),
|
|
1355
|
+
'}',
|
|
1356
|
+
`${RuntimeGlobalIsBrowser} = !isChrome;`,
|
|
1357
|
+
`${RuntimeGlobal} = runtime || { runtime: null };`,
|
|
1358
|
+
acceptWeak ? 'if (!runtime && (typeof self !== "object" || !self.addEventListener)) {' : '',
|
|
1359
|
+
acceptWeak ? Template.indent([
|
|
1360
|
+
`${RuntimeGlobal} = { runtime: { getURL: String } };`
|
|
1361
|
+
]) : '',
|
|
1362
|
+
acceptWeak ? '}' : ''
|
|
1363
|
+
].filter(Boolean));
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
return new BrowserRuntime();
|
|
1367
|
+
}
|
|
1368
|
+
const DYNAMIC_IMPORT_LOADER = 'dynamicImportLoader';
|
|
1369
|
+
const DOM_LOADER = "scriptLoader";
|
|
1370
|
+
const WORKER_LOADER = 'workerLoader';
|
|
1371
|
+
const CLASSIC_LOADER = 'classicLoader';
|
|
1372
|
+
const MAIN_CLASSIC_LOADER = 'classicLoaderMainWorld';
|
|
1373
|
+
const FALLBACK_LOADER = 'fallbackLoader';
|
|
1374
|
+
function LoadScriptRuntimeModule(webpack, supportDynamicImport, classicLoaderEnabled, contentScriptsMeta) {
|
|
1375
|
+
const { Template, RuntimeGlobals, RuntimeModule } = webpack;
|
|
1376
|
+
class LoadScriptRuntime extends RuntimeModule {
|
|
1377
|
+
supportDynamicImport;
|
|
1378
|
+
classicLoaderEnabled;
|
|
1379
|
+
contentScriptsMeta;
|
|
1380
|
+
constructor(){
|
|
1381
|
+
super("load script", RuntimeModule.STAGE_BASIC);
|
|
1382
|
+
this.supportDynamicImport = Boolean(supportDynamicImport);
|
|
1383
|
+
this.classicLoaderEnabled = Boolean(classicLoaderEnabled);
|
|
1384
|
+
this.contentScriptsMeta = contentScriptsMeta || {};
|
|
1385
|
+
}
|
|
1386
|
+
generate() {
|
|
1387
|
+
const { compilation } = this;
|
|
1388
|
+
if (!compilation) return Template.asString('/* [webpack-target-webextension] ChunkLoaderFallbackRuntimeModule skipped because no compilation is found. */');
|
|
1389
|
+
const { f } = TemplateFn(compilation, Template);
|
|
1390
|
+
const _const = compilation.outputOptions.environment.const ? 'const' : 'var';
|
|
1391
|
+
const _let = compilation.outputOptions.environment.const ? 'let' : 'var';
|
|
1392
|
+
const chunkName = (this.chunk && this.chunk.name ? this.chunk.name : '') || (compilation && compilation.chunk && compilation.chunk.name ? compilation.chunk.name : '');
|
|
1393
|
+
const bundleId = chunkName ? `${chunkName}.js` : '';
|
|
1394
|
+
const world = bundleId && this.contentScriptsMeta[bundleId] ? this.contentScriptsMeta[bundleId].world : void 0;
|
|
1395
|
+
const isMainWorld = 'main' === world;
|
|
1396
|
+
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; } })();`;
|
|
1397
|
+
const DynamicImportLoader = `${_const} ${DYNAMIC_IMPORT_LOADER} = ` + f('url, done, key, chunkId', `import(url).then(${f('', [
|
|
1398
|
+
'if (isNotIframe) return done();',
|
|
1399
|
+
'try {',
|
|
1400
|
+
Template.indent([
|
|
1401
|
+
"// It's a Chrome bug, if the import() is called in a sandboxed iframe, it _fails_ the script loading but _resolve_ the Promise.",
|
|
1402
|
+
`// we call ${RuntimeGlobals.ensureChunkHandlers}.j(chunkId) to check if it is loaded.`,
|
|
1403
|
+
'// if it is, this is a no-op. if it is not, it will throw a TypeError because this function requires 2 parameters.',
|
|
1404
|
+
'// This call will not trigger the chunk loading because it is already loading.',
|
|
1405
|
+
'// see https://github.com/awesome-webextension/webpack-target-webextension/issues/41',
|
|
1406
|
+
`chunkId !== undefined && ${RuntimeGlobals.ensureChunkHandlers}.j(chunkId);`,
|
|
1407
|
+
'done();'
|
|
1408
|
+
]),
|
|
1409
|
+
'}',
|
|
1410
|
+
'catch (_) {',
|
|
1411
|
+
Template.indent([
|
|
1412
|
+
'if (!bug816121warned) {',
|
|
1413
|
+
Template.indent([
|
|
1414
|
+
'console.warn("Chrome bug https://crbug.com/816121 hit.");',
|
|
1415
|
+
'bug816121warned = true;'
|
|
1416
|
+
]),
|
|
1417
|
+
'}',
|
|
1418
|
+
`return ${FALLBACK_LOADER}(url, done, key, chunkId);`
|
|
1419
|
+
]),
|
|
1420
|
+
'}'
|
|
1421
|
+
])}, ${f('e', [
|
|
1422
|
+
'console.warn("Dynamic import loader failed. Using fallback loader (see https://github.com/awesome-webextension/webpack-target-webextension#content-script).", e);',
|
|
1423
|
+
`${FALLBACK_LOADER}(url, done, key, chunkId);`
|
|
1424
|
+
])});`);
|
|
1425
|
+
const DOMLoader = `${_const} ${DOM_LOADER} = ` + f('url, done', [
|
|
1426
|
+
`${_const} script = document.createElement('script');`,
|
|
1427
|
+
"script.src = url;",
|
|
1428
|
+
"script.onload = done;",
|
|
1429
|
+
"script.onerror = done;",
|
|
1430
|
+
"document.body.appendChild(script);"
|
|
1431
|
+
]);
|
|
1432
|
+
const WorkerLoader = `${_const} ${WORKER_LOADER} = ` + f('url, done', [
|
|
1433
|
+
'try {',
|
|
1434
|
+
Template.indent([
|
|
1435
|
+
'importScripts(url);',
|
|
1436
|
+
'done();'
|
|
1437
|
+
]),
|
|
1438
|
+
'} catch (e) {',
|
|
1439
|
+
Template.indent([
|
|
1440
|
+
'done(e);'
|
|
1441
|
+
]),
|
|
1442
|
+
'}'
|
|
1443
|
+
]);
|
|
1444
|
+
const ClassicLoader = `${_const} ${CLASSIC_LOADER} = ` + f('url, done', Template.asString([
|
|
1445
|
+
`${_const} msg = { type: "WTW_INJECT", file: url };`,
|
|
1446
|
+
`${_const} onError = ${f('e', 'done(Object.assign(e, { type: "missing" }))')};`,
|
|
1447
|
+
'try {',
|
|
1448
|
+
`if (${RuntimeGlobalIsBrowser}) {`,
|
|
1449
|
+
Template.indent([
|
|
1450
|
+
`${RuntimeGlobal}.runtime.sendMessage(msg).then(done, onError);`
|
|
1451
|
+
]),
|
|
1452
|
+
'} else {',
|
|
1453
|
+
Template.indent([
|
|
1454
|
+
`${RuntimeGlobal}.runtime.sendMessage(msg, ${f('', [
|
|
1455
|
+
`${_const} error = ${RuntimeGlobal}.runtime.lastError;`,
|
|
1456
|
+
'if (error) onError(error);',
|
|
1457
|
+
'else done();'
|
|
1458
|
+
])});`
|
|
1459
|
+
]),
|
|
1460
|
+
'}',
|
|
1461
|
+
'} catch (e) { onError(e); }'
|
|
1462
|
+
])) + ';';
|
|
1463
|
+
const ClassicLoaderDisabled = `${_const} ${CLASSIC_LOADER} = ` + f('', [
|
|
1464
|
+
"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.\");"
|
|
1465
|
+
]);
|
|
1466
|
+
return Template.asString([
|
|
1467
|
+
this.supportDynamicImport ? `${_let} bug816121warned, isNotIframe;` : '',
|
|
1468
|
+
this.supportDynamicImport ? Template.asString([
|
|
1469
|
+
'try {',
|
|
1470
|
+
Template.indent([
|
|
1471
|
+
'isNotIframe = typeof window === "object" ? window.top === window : true;'
|
|
1472
|
+
]),
|
|
1473
|
+
'} catch(e) {',
|
|
1474
|
+
Template.indent([
|
|
1475
|
+
'isNotIframe = false /* CORS error */;'
|
|
1476
|
+
]),
|
|
1477
|
+
'}'
|
|
1478
|
+
]) : '',
|
|
1479
|
+
HasExtensionRuntime,
|
|
1480
|
+
this.classicLoaderEnabled ? ClassicLoader : ClassicLoaderDisabled,
|
|
1481
|
+
this.supportDynamicImport ? DynamicImportLoader : '',
|
|
1482
|
+
DOMLoader,
|
|
1483
|
+
WorkerLoader,
|
|
1484
|
+
`${_const} isWorker = typeof importScripts === 'function'`,
|
|
1485
|
+
"if (typeof location === 'object' && location.protocol.includes('-extension:')) {",
|
|
1486
|
+
Template.indent([
|
|
1487
|
+
`${RuntimeGlobals.loadScript} = isWorker ? ${WORKER_LOADER} : ${DOM_LOADER};`
|
|
1488
|
+
]),
|
|
1489
|
+
"} else {",
|
|
1490
|
+
Template.indent(isMainWorld ? Template.asString([
|
|
1491
|
+
`${_const} __extjsMark = "__extjs__";`,
|
|
1492
|
+
`${_const} __extjsReqType = "EXTJS_WTW_LOAD";`,
|
|
1493
|
+
`${_const} __extjsResType = "EXTJS_WTW_LOADED";`,
|
|
1494
|
+
`${_const} ${MAIN_CLASSIC_LOADER} = ` + f('url, done', [
|
|
1495
|
+
`${_const} requestId = String(Date.now()) + "-" + Math.random().toString(16).slice(2);`,
|
|
1496
|
+
`${_const} onMessage = ${f('event', [
|
|
1497
|
+
'try {',
|
|
1498
|
+
Template.indent([
|
|
1499
|
+
'if (!event || event.source !== window) return;',
|
|
1500
|
+
`${_const} data = event.data || null;`,
|
|
1501
|
+
"if (!data || data[__extjsMark] !== true) return;",
|
|
1502
|
+
"if (data.type !== __extjsResType || data.requestId !== requestId) return;",
|
|
1503
|
+
'window.removeEventListener("message", onMessage);',
|
|
1504
|
+
'if (data.ok) done();',
|
|
1505
|
+
'else done(Object.assign(new Error(data.error || "Bridge failed"), { type: "missing" }));'
|
|
1506
|
+
]),
|
|
1507
|
+
'} catch (e) {',
|
|
1508
|
+
Template.indent([
|
|
1509
|
+
'window.removeEventListener("message", onMessage);',
|
|
1510
|
+
'done(Object.assign(e, { type: "missing" }));'
|
|
1511
|
+
]),
|
|
1512
|
+
'}'
|
|
1513
|
+
])};`,
|
|
1514
|
+
'window.addEventListener("message", onMessage);',
|
|
1515
|
+
'try {',
|
|
1516
|
+
Template.indent([
|
|
1517
|
+
'window.postMessage({ [__extjsMark]: true, type: __extjsReqType, requestId: requestId, url: url }, "*");'
|
|
1518
|
+
]),
|
|
1519
|
+
'} catch (e) {',
|
|
1520
|
+
Template.indent([
|
|
1521
|
+
'window.removeEventListener("message", onMessage);',
|
|
1522
|
+
'done(Object.assign(e, { type: "missing" }));'
|
|
1523
|
+
]),
|
|
1524
|
+
'}',
|
|
1525
|
+
'setTimeout(function(){ try { window.removeEventListener("message", onMessage); } catch(_){} done(Object.assign(new Error("Bridge timeout"), { type: "missing" })); }, 5000);'
|
|
1526
|
+
]) + ';',
|
|
1527
|
+
`if (!isWorker) ${RuntimeGlobals.loadScript} = ${MAIN_CLASSIC_LOADER};`
|
|
1528
|
+
]) : Template.asString([
|
|
1529
|
+
`if (!isWorker && hasExtensionRuntime) ${RuntimeGlobals.loadScript} = ${CLASSIC_LOADER};`,
|
|
1530
|
+
`else if (!isWorker) ${RuntimeGlobals.loadScript} = ${DOM_LOADER};`
|
|
1531
|
+
])),
|
|
1532
|
+
"}",
|
|
1533
|
+
this.supportDynamicImport ? `${_const} ${FALLBACK_LOADER} = ${RuntimeGlobals.loadScript};` : '',
|
|
1534
|
+
this.supportDynamicImport ? `${RuntimeGlobals.loadScript} = ${DYNAMIC_IMPORT_LOADER};` : ''
|
|
1535
|
+
].filter(Boolean));
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
return new LoadScriptRuntime();
|
|
1539
|
+
}
|
|
1540
|
+
function PublicPathRuntimeModule(webpack) {
|
|
1541
|
+
const { RuntimeGlobals, RuntimeModule, Template } = webpack;
|
|
1542
|
+
class PublicPathRuntime extends RuntimeModule {
|
|
1543
|
+
constructor(){
|
|
1544
|
+
super('publicPath', RuntimeModule.STAGE_BASIC);
|
|
1545
|
+
}
|
|
1546
|
+
generate() {
|
|
1547
|
+
const { compilation } = this;
|
|
1548
|
+
if (!compilation) return Template.asString('/* [webpack-target-webextension] PublicPathRuntimeModule skipped because no compilation is found. */');
|
|
1549
|
+
const { publicPath } = compilation.outputOptions;
|
|
1550
|
+
const path = JSON.stringify(compilation.getPath(publicPath || '', {
|
|
1551
|
+
hash: compilation.hash || 'XXXX'
|
|
1552
|
+
}));
|
|
1553
|
+
return Template.asString([
|
|
1554
|
+
'var __extjsBase = (typeof globalThis === "object" && globalThis && globalThis.__EXTJS_EXTENSION_BASE__) ? String(globalThis.__EXTJS_EXTENSION_BASE__) : "";',
|
|
1555
|
+
'if (!__extjsBase && typeof document === "object" && document && document.documentElement) {',
|
|
1556
|
+
Template.indent([
|
|
1557
|
+
'try { __extjsBase = document.documentElement.getAttribute("data-extjs-extension-base") || ""; } catch(_) { __extjsBase = ""; }'
|
|
1558
|
+
]),
|
|
1559
|
+
"}",
|
|
1560
|
+
'var __extjsRuntimePath = "";',
|
|
1561
|
+
`if (${RuntimeGlobal} && ${RuntimeGlobal}.runtime && typeof ${RuntimeGlobal}.runtime.getURL === "function") {`,
|
|
1562
|
+
Template.indent([
|
|
1563
|
+
`try { __extjsRuntimePath = ${RuntimeGlobal}.runtime.getURL(${path}); } catch (_) { __extjsRuntimePath = ""; }`
|
|
1564
|
+
]),
|
|
1565
|
+
"}",
|
|
1566
|
+
"if (__extjsRuntimePath) {",
|
|
1567
|
+
Template.indent([
|
|
1568
|
+
`${RuntimeGlobals.publicPath} = __extjsRuntimePath;`
|
|
1569
|
+
]),
|
|
1570
|
+
"} else if (__extjsBase) {",
|
|
1571
|
+
Template.indent([
|
|
1572
|
+
`${RuntimeGlobals.publicPath} = __extjsBase.replace(/\\/+$/, "/") + String(${path}).replace(/^\\/+/, "");`
|
|
1573
|
+
]),
|
|
1574
|
+
"} else {",
|
|
1575
|
+
Template.indent([
|
|
1576
|
+
`${RuntimeGlobals.publicPath} = "";`
|
|
1577
|
+
]),
|
|
1578
|
+
"}",
|
|
1579
|
+
'if (!__extjsBase && typeof document === "object" && document && document.documentElement) {',
|
|
1580
|
+
Template.indent([
|
|
1581
|
+
"try {",
|
|
1582
|
+
Template.indent([
|
|
1583
|
+
"var __extjsRetries = 0;",
|
|
1584
|
+
"var __extjsUpdateBase = function(){",
|
|
1585
|
+
Template.indent([
|
|
1586
|
+
'var base = "";',
|
|
1587
|
+
'try { base = document.documentElement.getAttribute("data-extjs-extension-base") || ""; } catch(_) { base = ""; }',
|
|
1588
|
+
"if (base) {",
|
|
1589
|
+
Template.indent([
|
|
1590
|
+
`${RuntimeGlobals.publicPath} = base.replace(/\\/+$/, "/") + String(${path}).replace(/^\\/+/, "");`
|
|
1591
|
+
]),
|
|
1592
|
+
"} else if (__extjsRetries++ < 50) {",
|
|
1593
|
+
Template.indent([
|
|
1594
|
+
"setTimeout(__extjsUpdateBase, 100);"
|
|
1595
|
+
]),
|
|
1596
|
+
"}"
|
|
1597
|
+
]),
|
|
1598
|
+
"};",
|
|
1599
|
+
"__extjsUpdateBase();"
|
|
1600
|
+
]),
|
|
1601
|
+
"} catch (_) {}"
|
|
1602
|
+
]),
|
|
1603
|
+
"}"
|
|
1604
|
+
]);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
return new PublicPathRuntime();
|
|
1608
|
+
}
|
|
1609
|
+
function AutoPublicPathRuntimeModule(webpack) {
|
|
1610
|
+
const { RuntimeModule, RuntimeGlobals, Template, javascript: { JavascriptModulesPlugin }, HotUpdateChunk } = webpack;
|
|
1611
|
+
const getChunkFilenameTemplate = JavascriptModulesPlugin.getChunkFilenameTemplate || function(chunk, outputOptions) {
|
|
1612
|
+
if (chunk.filenameTemplate) return chunk.filenameTemplate;
|
|
1613
|
+
if ('function' == typeof HotUpdateChunk && chunk instanceof HotUpdateChunk) return outputOptions.hotUpdateChunkFilename;
|
|
1614
|
+
if (chunk.canBeInitial()) return outputOptions.filename;
|
|
1615
|
+
return outputOptions.chunkFilename;
|
|
1616
|
+
};
|
|
1617
|
+
class AutoPublicPathRuntime extends RuntimeModule {
|
|
1618
|
+
constructor(){
|
|
1619
|
+
super('publicPath', RuntimeModule.STAGE_BASIC);
|
|
1620
|
+
}
|
|
1621
|
+
generate() {
|
|
1622
|
+
const { compilation } = this;
|
|
1623
|
+
if (!compilation || !this.chunk) return Template.asString('/* [webpack-target-webextension] AutoPublicPathRuntimeModule skipped because no compilation/chunk is found. */');
|
|
1624
|
+
const { scriptType, importMetaName } = compilation.outputOptions;
|
|
1625
|
+
const chunkName = compilation.getPath(getChunkFilenameTemplate(this.chunk, compilation.outputOptions), {
|
|
1626
|
+
chunk: this.chunk,
|
|
1627
|
+
contentHashType: "javascript"
|
|
1628
|
+
});
|
|
1629
|
+
const outputPath = compilation.outputOptions.path;
|
|
1630
|
+
if (!outputPath) return Template.asString('/* [webpack-target-webextension] AutoPublicPathRuntimeModule skipped because no output path is found. */');
|
|
1631
|
+
const undoPath = getUndoPath(chunkName, outputPath, false);
|
|
1632
|
+
const optionalChaining = compilation.outputOptions.environment.optionalChaining;
|
|
1633
|
+
const _const = compilation.outputOptions.environment.const ? 'const' : 'var';
|
|
1634
|
+
const _let = compilation.outputOptions.environment.const ? 'let' : 'var';
|
|
1635
|
+
return Template.asString([
|
|
1636
|
+
`${_let} scriptUrl;`,
|
|
1637
|
+
'module' === scriptType ? `if (typeof ${importMetaName}.url === "string") scriptUrl = ${importMetaName}.url;` : Template.asString([
|
|
1638
|
+
`if (${RuntimeGlobals.global}.importScripts) scriptUrl = ${RuntimeGlobals.global}.location + "";`,
|
|
1639
|
+
`${_const} document = ${RuntimeGlobals.global}.document;`,
|
|
1640
|
+
`if (!scriptUrl && ${optionalChaining ? 'document?.currentScript' : 'document && document.currentScript'}) {`,
|
|
1641
|
+
Template.indent("scriptUrl = document.currentScript.src;"),
|
|
1642
|
+
'}'
|
|
1643
|
+
]),
|
|
1644
|
+
'// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration',
|
|
1645
|
+
'// or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.',
|
|
1646
|
+
'var __extjsBase = (typeof globalThis === "object" && globalThis && globalThis.__EXTJS_EXTENSION_BASE__) ? String(globalThis.__EXTJS_EXTENSION_BASE__) : "";',
|
|
1647
|
+
'if (!__extjsBase && typeof document === "object" && document && document.documentElement) {',
|
|
1648
|
+
Template.indent([
|
|
1649
|
+
'try { __extjsBase = document.documentElement.getAttribute("data-extjs-extension-base") || ""; } catch(_) { __extjsBase = ""; }'
|
|
1650
|
+
]),
|
|
1651
|
+
"}",
|
|
1652
|
+
"if (__extjsBase && scriptUrl && !/^((chrome|moz)-extension):///.test(scriptUrl)) {",
|
|
1653
|
+
Template.indent([
|
|
1654
|
+
"scriptUrl = __extjsBase;"
|
|
1655
|
+
]),
|
|
1656
|
+
"}",
|
|
1657
|
+
"if (!scriptUrl) {",
|
|
1658
|
+
Template.indent([
|
|
1659
|
+
'var __extjsRuntimeRoot = "";',
|
|
1660
|
+
`if (${RuntimeGlobal} && ${RuntimeGlobal}.runtime && typeof ${RuntimeGlobal}.runtime.getURL === "function") {`,
|
|
1661
|
+
Template.indent(`try { __extjsRuntimeRoot = ${RuntimeGlobal}.runtime.getURL("/"); } catch (_) { __extjsRuntimeRoot = ""; }`),
|
|
1662
|
+
Template.indent("scriptUrl = __extjsRuntimeRoot || scriptUrl;"),
|
|
1663
|
+
"} else {",
|
|
1664
|
+
Template.indent('scriptUrl = __extjsBase || "";'),
|
|
1665
|
+
"}"
|
|
1666
|
+
]),
|
|
1667
|
+
'}',
|
|
1668
|
+
'scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\\?.*$/, "").replace(/\\/[^\\/]+$/, "/");',
|
|
1669
|
+
undoPath ? `${RuntimeGlobals.publicPath} = scriptUrl + ${JSON.stringify(undoPath)};` : `${RuntimeGlobals.publicPath} = scriptUrl;`,
|
|
1670
|
+
'if (!scriptUrl && typeof document === "object" && document && document.documentElement) {',
|
|
1671
|
+
Template.indent([
|
|
1672
|
+
"try {",
|
|
1673
|
+
Template.indent([
|
|
1674
|
+
"var __extjsRetries = 0;",
|
|
1675
|
+
"var __extjsUpdateBase = function(){",
|
|
1676
|
+
Template.indent([
|
|
1677
|
+
'var base = "";',
|
|
1678
|
+
'try { base = document.documentElement.getAttribute("data-extjs-extension-base") || ""; } catch(_) { base = ""; }',
|
|
1679
|
+
"if (base) {",
|
|
1680
|
+
Template.indent([
|
|
1681
|
+
'var normalized = base.replace(/\\/+$/, "/");',
|
|
1682
|
+
undoPath ? `${RuntimeGlobals.publicPath} = normalized + ${JSON.stringify(undoPath)};` : `${RuntimeGlobals.publicPath} = normalized;`
|
|
1683
|
+
]),
|
|
1684
|
+
"} else if (__extjsRetries++ < 50) {",
|
|
1685
|
+
Template.indent([
|
|
1686
|
+
"setTimeout(__extjsUpdateBase, 100);"
|
|
1687
|
+
]),
|
|
1688
|
+
"}"
|
|
1689
|
+
]),
|
|
1690
|
+
"};",
|
|
1691
|
+
"__extjsUpdateBase();"
|
|
1692
|
+
]),
|
|
1693
|
+
"} catch (_) {}"
|
|
1694
|
+
]),
|
|
1695
|
+
"}"
|
|
1696
|
+
]);
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
return new AutoPublicPathRuntime();
|
|
1700
|
+
}
|
|
1701
|
+
function getUndoPath(filename, outputPath, enforceRelative) {
|
|
1702
|
+
let depth = -1;
|
|
1703
|
+
let append = '';
|
|
1704
|
+
outputPath = outputPath.replace(/[\\/]$/, '');
|
|
1705
|
+
for (const part of filename.split(/[/\\]+/))if ('..' === part) if (depth > -1) depth--;
|
|
1706
|
+
else {
|
|
1707
|
+
const i = outputPath.lastIndexOf('/');
|
|
1708
|
+
const j = outputPath.lastIndexOf('\\');
|
|
1709
|
+
const pos = i < 0 ? j : j < 0 ? i : Math.max(i, j);
|
|
1710
|
+
if (pos < 0) return outputPath + '/';
|
|
1711
|
+
append = outputPath.slice(pos + 1) + '/' + append;
|
|
1712
|
+
outputPath = outputPath.slice(0, pos);
|
|
1713
|
+
}
|
|
1714
|
+
else if ('.' !== part) depth++;
|
|
1715
|
+
return depth > 0 ? `${'../'.repeat(depth)}${append}` : enforceRelative ? `./${append}` : append;
|
|
1716
|
+
}
|
|
1717
|
+
function BaseUriRuntimeModule(webpack) {
|
|
1718
|
+
const { RuntimeGlobals, RuntimeModule, Template } = webpack;
|
|
1719
|
+
class BaseUriRuntime extends RuntimeModule {
|
|
1720
|
+
constructor(){
|
|
1721
|
+
super('baseURI', RuntimeModule.STAGE_BASIC);
|
|
1722
|
+
}
|
|
1723
|
+
generate() {
|
|
1724
|
+
return Template.asString([
|
|
1725
|
+
'var __extjsBase = "";',
|
|
1726
|
+
"try {",
|
|
1727
|
+
Template.indent([
|
|
1728
|
+
`if (${RuntimeGlobal} && ${RuntimeGlobal}.runtime && typeof ${RuntimeGlobal}.runtime.getURL === "function") {`,
|
|
1729
|
+
Template.indent([
|
|
1730
|
+
`__extjsBase = String(${RuntimeGlobal}.runtime.getURL("/"));`
|
|
1731
|
+
]),
|
|
1732
|
+
"}"
|
|
1733
|
+
]),
|
|
1734
|
+
"} catch (_) {}",
|
|
1735
|
+
'if (!__extjsBase && typeof globalThis === "object" && globalThis && globalThis.__EXTJS_EXTENSION_BASE__) {',
|
|
1736
|
+
Template.indent([
|
|
1737
|
+
'__extjsBase = String(globalThis.__EXTJS_EXTENSION_BASE__ || "");'
|
|
1738
|
+
]),
|
|
1739
|
+
"}",
|
|
1740
|
+
'if (!__extjsBase && typeof document === "object" && document && document.documentElement) {',
|
|
1741
|
+
Template.indent([
|
|
1742
|
+
'try { __extjsBase = String(document.documentElement.getAttribute("data-extjs-extension-base") || ""); } catch (_) { __extjsBase = ""; }'
|
|
1743
|
+
]),
|
|
1744
|
+
"}",
|
|
1745
|
+
"if (__extjsBase) {",
|
|
1746
|
+
Template.indent([
|
|
1747
|
+
`${RuntimeGlobals.baseURI} = __extjsBase;`
|
|
1748
|
+
]),
|
|
1749
|
+
'} else if (typeof document !== "undefined" && document.baseURI) {',
|
|
1750
|
+
Template.indent([
|
|
1751
|
+
`${RuntimeGlobals.baseURI} = document.baseURI;`
|
|
1752
|
+
]),
|
|
1753
|
+
"} else {",
|
|
1754
|
+
Template.indent([
|
|
1755
|
+
`${RuntimeGlobals.baseURI} = self.location.href;`
|
|
1756
|
+
]),
|
|
1757
|
+
"}"
|
|
1758
|
+
]);
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
return new BaseUriRuntime();
|
|
1762
|
+
}
|
|
1763
|
+
function ChunkLoaderFallbackRuntimeModule(webpack) {
|
|
1764
|
+
const { RuntimeModule, Template } = webpack;
|
|
1765
|
+
class ChunkLoaderFallbackRuntime extends RuntimeModule {
|
|
1766
|
+
constructor(){
|
|
1767
|
+
super('chunk loader fallback', RuntimeModule.STAGE_TRIGGER);
|
|
1768
|
+
}
|
|
1769
|
+
generate() {
|
|
1770
|
+
const { compilation } = this;
|
|
1771
|
+
if (!compilation) return Template.asString('/* [webpack-target-webextension] ChunkLoaderFallbackRuntimeModule skipped because no compilation is found. */');
|
|
1772
|
+
const { f } = TemplateFn(compilation, Template);
|
|
1773
|
+
const optionalChain = compilation.outputOptions.environment.optionalChaining;
|
|
1774
|
+
const _const = compilation.outputOptions.environment.const ? 'const' : 'var';
|
|
1775
|
+
const _let = compilation.outputOptions.environment.const ? 'let' : 'var';
|
|
1776
|
+
return `if (${RuntimeGlobal} && ${RuntimeGlobal}.runtime && ${RuntimeGlobal}.runtime.onMessage) {${RuntimeGlobal}.runtime.onMessage.addListener(` + f('message, sender, sendResponse', [
|
|
1777
|
+
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;',
|
|
1778
|
+
`${_let} file = message.file;`,
|
|
1779
|
+
'try {',
|
|
1780
|
+
Template.indent([
|
|
1781
|
+
'file = new URL(file).pathname;'
|
|
1782
|
+
]),
|
|
1783
|
+
'} catch (_) {}',
|
|
1784
|
+
'if (!file) return;',
|
|
1785
|
+
`if (${RuntimeGlobal}.scripting) {`,
|
|
1786
|
+
Template.indent([
|
|
1787
|
+
`${RuntimeGlobal}.scripting.executeScript({`,
|
|
1788
|
+
Template.indent([
|
|
1789
|
+
'target: { tabId: sender.tab.id, frameIds: [sender.frameId] },',
|
|
1790
|
+
'files: [file],'
|
|
1791
|
+
]),
|
|
1792
|
+
'}).then(sendResponse);'
|
|
1793
|
+
]),
|
|
1794
|
+
"} else {",
|
|
1795
|
+
Template.indent([
|
|
1796
|
+
`${_const} details = { frameId: sender.frameId, file, matchAboutBlank: true };`,
|
|
1797
|
+
`if (${RuntimeGlobalIsBrowser}) {`,
|
|
1798
|
+
`${RuntimeGlobal}.tabs.executeScript(sender.tab.id, details).then(sendResponse);`,
|
|
1799
|
+
'} else {',
|
|
1800
|
+
`${RuntimeGlobal}.tabs.executeScript(sender.tab.id, details, sendResponse);`,
|
|
1801
|
+
'}'
|
|
1802
|
+
]),
|
|
1803
|
+
'}',
|
|
1804
|
+
'return true;'
|
|
1805
|
+
]) + ");}";
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
return new ChunkLoaderFallbackRuntime();
|
|
1809
|
+
}
|
|
1810
|
+
class WebExtensionChuckLoaderRuntimePlugin {
|
|
1811
|
+
pluginOptions;
|
|
1812
|
+
options;
|
|
1813
|
+
contentScriptsMeta;
|
|
1814
|
+
rspackAutoPublicPath = false;
|
|
1815
|
+
constructor(options){
|
|
1816
|
+
this.pluginOptions = options || {};
|
|
1817
|
+
this.options = this.pluginOptions.background || this.pluginOptions || {};
|
|
1818
|
+
this.contentScriptsMeta = this.pluginOptions.contentScriptsMeta || {};
|
|
1819
|
+
}
|
|
1820
|
+
apply(compiler) {
|
|
1821
|
+
if ('rspack' in compiler) {
|
|
1822
|
+
const { output } = compiler.options;
|
|
1823
|
+
if (void 0 === output.publicPath || 'auto' === output.publicPath) {
|
|
1824
|
+
output.publicPath = '';
|
|
1825
|
+
this.rspackAutoPublicPath = true;
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
compiler.hooks.compilation.tap(WebExtensionChuckLoaderRuntimePlugin.name, (compilation)=>{
|
|
1829
|
+
this.tap(compiler, compilation);
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
tap(compiler, compilation) {
|
|
1833
|
+
const { RuntimeGlobals } = compiler.webpack;
|
|
1834
|
+
const { options } = this;
|
|
1835
|
+
compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.loadScript).tap(WebExtensionChuckLoaderRuntimePlugin.name, (_chunk, set)=>{
|
|
1836
|
+
set.add(RuntimeGlobal);
|
|
1837
|
+
compilation.addRuntimeModule(_chunk, LoadScriptRuntimeModule(compiler.webpack, false !== compilation.outputOptions.environment.dynamicImport, false !== options.classicLoader, this.contentScriptsMeta));
|
|
1838
|
+
return true;
|
|
1839
|
+
});
|
|
1840
|
+
compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.baseURI).tap(WebExtensionChuckLoaderRuntimePlugin.name, (chunk, set)=>{
|
|
1841
|
+
set.add(RuntimeGlobal);
|
|
1842
|
+
compilation.addRuntimeModule(chunk, BaseUriRuntimeModule(compiler.webpack));
|
|
1843
|
+
return true;
|
|
1844
|
+
});
|
|
1845
|
+
compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.publicPath).tap(WebExtensionChuckLoaderRuntimePlugin.name, (chunk, set)=>{
|
|
1846
|
+
const { outputOptions } = compilation;
|
|
1847
|
+
const { publicPath, scriptType } = outputOptions;
|
|
1848
|
+
if ('auto' === publicPath || '' === publicPath && this.rspackAutoPublicPath) {
|
|
1849
|
+
const module = AutoPublicPathRuntimeModule(compiler.webpack);
|
|
1850
|
+
set.add(RuntimeGlobal);
|
|
1851
|
+
if ('module' !== scriptType) set.add(RuntimeGlobals.global);
|
|
1852
|
+
compilation.addRuntimeModule(chunk, module);
|
|
1853
|
+
} else {
|
|
1854
|
+
set.add(RuntimeGlobal);
|
|
1855
|
+
const module = PublicPathRuntimeModule(compiler.webpack);
|
|
1856
|
+
if ('string' != typeof publicPath || /\[(full)?hash\]/.test(publicPath)) module.fullHash = true;
|
|
1857
|
+
compilation.addRuntimeModule(chunk, module);
|
|
1858
|
+
}
|
|
1859
|
+
return true;
|
|
1860
|
+
});
|
|
1861
|
+
if (false !== options.classicLoader) compilation.hooks.afterOptimizeModules.tap(WebExtensionChuckLoaderRuntimePlugin.name, ()=>{
|
|
1862
|
+
const { pageEntry, serviceWorkerEntry } = options;
|
|
1863
|
+
const entryPoint1 = pageEntry && compilation.entrypoints.get(pageEntry);
|
|
1864
|
+
const entryPoint2 = serviceWorkerEntry && compilation.entrypoints.get(serviceWorkerEntry);
|
|
1865
|
+
if (entryPoint1) compilation.addRuntimeModule(entryPoint1.chunks[0], ChunkLoaderFallbackRuntimeModule(compiler.webpack));
|
|
1866
|
+
if (entryPoint2) compilation.addRuntimeModule(entryPoint2.chunks[0], ChunkLoaderFallbackRuntimeModule(compiler.webpack));
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
class NoDangerNamePlugin {
|
|
1871
|
+
apply(compiler) {
|
|
1872
|
+
const { Compilation, WebpackError } = compiler.webpack;
|
|
1873
|
+
compiler.hooks.thisCompilation.tap(NoDangerNamePlugin.name, (compilation)=>{
|
|
1874
|
+
compilation.hooks.processAssets.tap({
|
|
1875
|
+
name: NoDangerNamePlugin.name,
|
|
1876
|
+
stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
|
|
1877
|
+
}, (assets)=>{
|
|
1878
|
+
for (const name of Object.keys(assets || {})){
|
|
1879
|
+
if (!name.startsWith('_')) continue;
|
|
1880
|
+
const e = new WebpackError(`[webpack-extension-target] Output file name ${JSON.stringify(name)} starts with "_" which is prohibited.`);
|
|
1881
|
+
e.stack = '';
|
|
1882
|
+
compilation.errors.push(e);
|
|
1883
|
+
}
|
|
1884
|
+
});
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
function createEagerlyLoadChunksRuntimeModule(webpack) {
|
|
1889
|
+
const { RuntimeGlobals, RuntimeModule, Template } = webpack;
|
|
1890
|
+
return class extends RuntimeModule {
|
|
1891
|
+
chunkIds;
|
|
1892
|
+
constructor(chunkIds){
|
|
1893
|
+
super('eagerly load chunks', RuntimeModule.STAGE_NORMAL);
|
|
1894
|
+
this.chunkIds = chunkIds;
|
|
1895
|
+
}
|
|
1896
|
+
generate() {
|
|
1897
|
+
const { compilation } = this;
|
|
1898
|
+
if (!compilation) return Template.asString('/* [webpack-target-webextension] EagerlyLoadChunksRuntimeModule skipped because no compilation is found. */');
|
|
1899
|
+
const _const = compilation.outputOptions.environment.const ? 'const' : 'var';
|
|
1900
|
+
return Template.asString([
|
|
1901
|
+
`${_const} __extjsChunkIds = ${JSON.stringify(this.chunkIds)};`,
|
|
1902
|
+
`${_const} __extjsLoad = ${RuntimeGlobals.ensureChunkHandlers};`,
|
|
1903
|
+
`for (${_const} __extjsId of __extjsChunkIds) __extjsLoad(__extjsId);`
|
|
1904
|
+
]);
|
|
1905
|
+
}
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1908
|
+
class WebExtensionServiceWorkerEntryPlugin {
|
|
1909
|
+
options;
|
|
1910
|
+
backgroundOutputEmitted;
|
|
1911
|
+
constructor(options, backgroundOutputEmitted){
|
|
1912
|
+
this.options = options;
|
|
1913
|
+
this.backgroundOutputEmitted = backgroundOutputEmitted;
|
|
1914
|
+
}
|
|
1915
|
+
apply(compiler) {
|
|
1916
|
+
const { javascript, sources } = compiler.webpack;
|
|
1917
|
+
const entry = this.options.serviceWorkerEntry;
|
|
1918
|
+
if (void 0 === entry) return;
|
|
1919
|
+
if (!(compiler.options.output.module || compiler.options.experiments.outputModule)) {
|
|
1920
|
+
const hook = compiler.hooks.entryOption;
|
|
1921
|
+
hook.tap(WebExtensionServiceWorkerEntryPlugin.name, (_context, entries)=>{
|
|
1922
|
+
if ('function' == typeof entries) {
|
|
1923
|
+
if (this.options.noDynamicEntryWarning) return;
|
|
1924
|
+
console.warn(`[webpack-extension-target] Dynamic entry points not supported yet.
|
|
1925
|
+
You must manually set the chuck loading of entry point ${entry} to "import-scripts".
|
|
1926
|
+
|
|
1927
|
+
See https://webpack.js.org/configuration/entry-context/#entry-descriptor
|
|
1928
|
+
|
|
1929
|
+
Set background.noDynamicEntryWarning to true to disable this warning.
|
|
1930
|
+
`);
|
|
1931
|
+
}
|
|
1932
|
+
const selectedEntry = entries?.[entry];
|
|
1933
|
+
if (!selectedEntry) throw new Error(`[webpack-extension-target] There is no entry called ${entry}.`);
|
|
1934
|
+
selectedEntry.chunkLoading = "import-scripts";
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1937
|
+
if (false !== this.options.eagerChunkLoading) compiler.hooks.compilation.tap(WebExtensionServiceWorkerEntryPlugin.name, (compilation)=>{
|
|
1938
|
+
compilation.hooks.afterOptimizeChunkIds?.tap(WebExtensionServiceWorkerEntryPlugin.name, ()=>{
|
|
1939
|
+
const entryPoint = compilation.entrypoints.get(entry);
|
|
1940
|
+
if (!entryPoint) return;
|
|
1941
|
+
const entryChunk = entryPoint.getEntrypointChunk();
|
|
1942
|
+
const reachableChunks = getReachableChunks(entryPoint, new Set(entryPoint.chunks));
|
|
1943
|
+
const reachableChunkIds = new Set([
|
|
1944
|
+
...reachableChunks
|
|
1945
|
+
].map((x)=>x.id));
|
|
1946
|
+
for (const id of getInitialChunkIds(entryChunk, compilation.chunkGraph, chunkHasJs))reachableChunkIds.delete(id);
|
|
1947
|
+
if (reachableChunkIds.size) {
|
|
1948
|
+
const EagerlyLoadChunksRuntimeModule = createEagerlyLoadChunksRuntimeModule(compiler.webpack);
|
|
1949
|
+
compilation.hooks.additionalTreeRuntimeRequirements.tap(EagerlyLoadChunksRuntimeModule.name, (chunk, set)=>{
|
|
1950
|
+
if (chunk.id !== entryChunk.id) return;
|
|
1951
|
+
set.add(compiler.webpack.RuntimeGlobals.ensureChunkHandlers);
|
|
1952
|
+
compilation.addRuntimeModule(entryChunk, new EagerlyLoadChunksRuntimeModule([
|
|
1953
|
+
...reachableChunkIds
|
|
1954
|
+
]));
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
});
|
|
1958
|
+
});
|
|
1959
|
+
if (false !== this.options.tryCatchWrapper && 'module' !== compiler.options.output.chunkFormat && !this.backgroundOutputEmitted) compiler.hooks.compilation.tap(WebExtensionServiceWorkerEntryPlugin.name, (compilation)=>{
|
|
1960
|
+
const hooks = javascript.JavascriptModulesPlugin.getCompilationHooks(compilation);
|
|
1961
|
+
if (hooks.renderContent) hooks.renderContent.tap(WebExtensionServiceWorkerEntryPlugin.name, (source, context)=>{
|
|
1962
|
+
const entryPoint = compilation.entrypoints.get(entry);
|
|
1963
|
+
const entryChunk = entryPoint?.getEntrypointChunk();
|
|
1964
|
+
if (entryChunk !== context.chunk) return source;
|
|
1965
|
+
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/******/ }');
|
|
1966
|
+
});
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
function getInitialChunkIds(chunk, chunkGraph, filterFn) {
|
|
1971
|
+
const initialChunkIds = new Set(chunk.ids);
|
|
1972
|
+
for (const c of chunk.getAllInitialChunks())if (!(c === chunk || filterFn(c, chunkGraph)) && c.ids) for (const id of c.ids)initialChunkIds.add(id);
|
|
1973
|
+
return initialChunkIds;
|
|
1974
|
+
}
|
|
1975
|
+
function chunkHasJs(chunk, chunkGraph) {
|
|
1976
|
+
if (chunkGraph.getNumberOfEntryModules(chunk) > 0) return true;
|
|
1977
|
+
return !!chunkGraph.getChunkModulesIterableBySourceType(chunk, "javascript");
|
|
1978
|
+
}
|
|
1979
|
+
function getReachableChunks(chunkGroup, reachableChunks = new Set(), visitedChunkGroups = new Set()) {
|
|
1980
|
+
for (const x of chunkGroup.getChildren())if (!visitedChunkGroups.has(x)) {
|
|
1981
|
+
visitedChunkGroups.add(x);
|
|
1982
|
+
x.chunks.forEach((c)=>reachableChunks.add(c));
|
|
1983
|
+
getReachableChunks(x, reachableChunks, visitedChunkGroups);
|
|
1984
|
+
}
|
|
1985
|
+
return reachableChunks;
|
|
1986
|
+
}
|
|
1987
|
+
class WebExtensionContentScriptEntryPlugin {
|
|
1988
|
+
options;
|
|
1989
|
+
constructor(options){
|
|
1990
|
+
this.options = options;
|
|
1991
|
+
}
|
|
1992
|
+
apply(compiler) {
|
|
1993
|
+
const { WebpackError, sources, Template } = compiler.webpack;
|
|
1994
|
+
const { experimental_output } = this.options;
|
|
1995
|
+
if (!experimental_output) return;
|
|
1996
|
+
{
|
|
1997
|
+
const sw = this.options.background?.serviceWorkerEntry;
|
|
1998
|
+
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.`);
|
|
1999
|
+
}
|
|
2000
|
+
compiler.hooks.thisCompilation.tap(WebExtensionContentScriptEntryPlugin.name, (compilation)=>{
|
|
2001
|
+
compilation.hooks.processAssets.tap({
|
|
2002
|
+
name: WebExtensionContentScriptEntryPlugin.name,
|
|
2003
|
+
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED
|
|
2004
|
+
}, (assets)=>{
|
|
2005
|
+
let manifest;
|
|
2006
|
+
const serviceWorkerEntry = this.options.background?.serviceWorkerEntry;
|
|
2007
|
+
if (serviceWorkerEntry && !(serviceWorkerEntry in experimental_output) && (getInitialFiles(compilation, serviceWorkerEntry)?.length || 0) > 1) {
|
|
2008
|
+
const e = new WebpackError(`[webpack-extension-target] Entry ${JSON.stringify(serviceWorkerEntry)} is not specified in options.experimental_output.`);
|
|
2009
|
+
e.stack = '';
|
|
2010
|
+
compilation.warnings.push(e);
|
|
2011
|
+
}
|
|
2012
|
+
for(const entry in experimental_output){
|
|
2013
|
+
const isBackgroundEntry = entry === serviceWorkerEntry;
|
|
2014
|
+
const entryOption = experimental_output[entry];
|
|
2015
|
+
const initialFiles = getInitialFiles(compilation, entry);
|
|
2016
|
+
if (!initialFiles || 0 === initialFiles.length) {
|
|
2017
|
+
const name = JSON.stringify(entry);
|
|
2018
|
+
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).`);
|
|
2019
|
+
e.stack = '';
|
|
2020
|
+
compilation.errors.push(e);
|
|
2021
|
+
continue;
|
|
2022
|
+
}
|
|
2023
|
+
if (false === entryOption) {
|
|
2024
|
+
if (initialFiles.length > 1) {
|
|
2025
|
+
const name = JSON.stringify(entry);
|
|
2026
|
+
const e = new WebpackError(`[webpack-extension-target] Entry ${name} emits more than one initial file which is prohibited (specified in options.experimental_output).`);
|
|
2027
|
+
e.stack = '';
|
|
2028
|
+
compilation.errors.push(e);
|
|
2029
|
+
}
|
|
2030
|
+
continue;
|
|
2031
|
+
}
|
|
2032
|
+
const emitFile = (entryOptionFile, files)=>{
|
|
2033
|
+
if (entryOptionFile in assets) {
|
|
2034
|
+
const e = new WebpackError(`[webpack-extension-target] Cannot override an existing file ${JSON.stringify(entryOptionFile)} (specified by options.experimental_output[${JSON.stringify(entry)}]).`);
|
|
2035
|
+
e.stack = '';
|
|
2036
|
+
compilation.errors.push(e);
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
let code;
|
|
2040
|
+
if (isBackgroundEntry) {
|
|
2041
|
+
const asyncAndSyncFiles = getInitialAndAsyncFiles(compilation, entry);
|
|
2042
|
+
code = 'module' === compilation.outputOptions.chunkFormat ? asyncAndSyncFiles.map((file)=>`import ${JSON.stringify('./' + file)};`) : [
|
|
2043
|
+
'try {',
|
|
2044
|
+
Template.indent('importScripts(' + asyncAndSyncFiles.map((file)=>JSON.stringify(file)).join(', ') + ');'),
|
|
2045
|
+
'} catch (e) {',
|
|
2046
|
+
Template.indent('Promise.reject(e);'),
|
|
2047
|
+
'}'
|
|
2048
|
+
];
|
|
2049
|
+
} else code = [
|
|
2050
|
+
';(() => {',
|
|
2051
|
+
Template.indent([
|
|
2052
|
+
'const getURL = typeof globalThis === "object" && globalThis && typeof globalThis.browser === "object" ? globalThis.browser.runtime.getURL : globalThis.chrome.runtime.getURL;',
|
|
2053
|
+
`${JSON.stringify(files)}.forEach(file => import(getURL(file)));`
|
|
2054
|
+
]),
|
|
2055
|
+
'})();',
|
|
2056
|
+
'null;'
|
|
2057
|
+
];
|
|
2058
|
+
const source = new compiler.webpack.sources.RawSource(Template.asString(code));
|
|
2059
|
+
compilation.emitAsset(entryOptionFile, source);
|
|
2060
|
+
};
|
|
2061
|
+
if ('string' == typeof entryOption) {
|
|
2062
|
+
emitFile(entryOption, initialFiles);
|
|
2063
|
+
continue;
|
|
2064
|
+
}
|
|
2065
|
+
if (!manifest) {
|
|
2066
|
+
const manifestAsset = assets['manifest.json'];
|
|
2067
|
+
const name = JSON.stringify(entry);
|
|
2068
|
+
if (!manifestAsset) {
|
|
2069
|
+
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.`);
|
|
2070
|
+
e.stack = '';
|
|
2071
|
+
compilation.errors.push(e);
|
|
2072
|
+
continue;
|
|
2073
|
+
}
|
|
2074
|
+
try {
|
|
2075
|
+
const source = manifestAsset.source();
|
|
2076
|
+
manifest = 'string' == typeof source ? JSON.parse(source) : JSON.parse(source.toString('utf-8'));
|
|
2077
|
+
} catch {
|
|
2078
|
+
const e = new WebpackError(`[webpack-extension-target] Failed to parse manifest.json (required by options.experimental_output[${name}]).`);
|
|
2079
|
+
e.stack = '';
|
|
2080
|
+
e.file = 'manifest.json';
|
|
2081
|
+
compilation.errors.push(e);
|
|
2082
|
+
continue;
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
if ('function' == typeof entryOption) entryOption(manifest, initialFiles);
|
|
2086
|
+
else if ('object' == typeof entryOption) {
|
|
2087
|
+
emitFile(entryOption.file, initialFiles);
|
|
2088
|
+
entryOption.touch(manifest, entryOption.file);
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
if (manifest) compilation.updateAsset('manifest.json', new sources.RawSource(JSON.stringify(manifest, void 0, 4)));
|
|
2092
|
+
});
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
function getInitialFiles(compilation, entry) {
|
|
2097
|
+
const entryPoint = compilation.entrypoints.get(entry);
|
|
2098
|
+
if (!entryPoint) return;
|
|
2099
|
+
const files = new Set();
|
|
2100
|
+
const runtimeChunk = entryPoint.getRuntimeChunk();
|
|
2101
|
+
if (runtimeChunk) runtimeChunk.files.forEach((file)=>{
|
|
2102
|
+
if (!isJSFile(file)) return;
|
|
2103
|
+
const asset = compilation.getAsset(file);
|
|
2104
|
+
if (!asset) return;
|
|
2105
|
+
if (!asset.info.hotModuleReplacement) files.add(file);
|
|
2106
|
+
});
|
|
2107
|
+
entryPoint.getFiles().forEach((file)=>{
|
|
2108
|
+
if (!isJSFile(file)) return;
|
|
2109
|
+
const asset = compilation.getAsset(file);
|
|
2110
|
+
if (!asset) return;
|
|
2111
|
+
if (!asset.info.hotModuleReplacement) files.add(file);
|
|
2112
|
+
});
|
|
2113
|
+
return [
|
|
2114
|
+
...files
|
|
2115
|
+
];
|
|
2116
|
+
}
|
|
2117
|
+
function getInitialAndAsyncFiles(compilation, entry) {
|
|
2118
|
+
const entryPoint = compilation.entrypoints.get(entry);
|
|
2119
|
+
if (!entryPoint) return [];
|
|
2120
|
+
const files = new Set();
|
|
2121
|
+
const visitedChunk = new Set();
|
|
2122
|
+
function visit(chunk) {
|
|
2123
|
+
const chunkId = 'rspack' in compilation.compiler ? chunk.id && chunk.hash ? chunk.id + chunk.hash : Array.from(chunk.files).join('') : chunk;
|
|
2124
|
+
if (visitedChunk.has(chunkId)) return;
|
|
2125
|
+
visitedChunk.add(chunkId);
|
|
2126
|
+
chunk.files.forEach((file)=>{
|
|
2127
|
+
if (!isJSFile(file)) return;
|
|
2128
|
+
const asset = compilation.getAsset(file);
|
|
2129
|
+
if (!asset) return;
|
|
2130
|
+
if (!asset.info.hotModuleReplacement) files.add(file);
|
|
2131
|
+
});
|
|
2132
|
+
for (const child of chunk.getAllAsyncChunks())visit(child);
|
|
2133
|
+
}
|
|
2134
|
+
entryPoint.chunks.forEach(visit);
|
|
2135
|
+
const allFiles = [
|
|
2136
|
+
...files
|
|
2137
|
+
].filter((file)=>isJSFile(file) && compilation.getAsset(file));
|
|
2138
|
+
return allFiles;
|
|
2139
|
+
}
|
|
2140
|
+
function isJSFile(file) {
|
|
2141
|
+
return file.endsWith('.js') || file.endsWith('.mjs');
|
|
2142
|
+
}
|
|
2143
|
+
class DevServerConfigPlugin {
|
|
2144
|
+
apply(compiler) {
|
|
2145
|
+
if (!compiler.options.devServer) compiler.options.devServer = {};
|
|
2146
|
+
const devServer = compiler.options.devServer;
|
|
2147
|
+
setDefault(devServer, 'devMiddleware', {});
|
|
2148
|
+
setDefault(devServer.devMiddleware, 'writeToDisk', true);
|
|
2149
|
+
if (void 0 === devServer.hot) devServer.hot = 'only';
|
|
2150
|
+
if (!devServer.hot) return;
|
|
2151
|
+
setDefault(devServer, 'host', '127.0.0.1');
|
|
2152
|
+
setDefault(devServer, 'client', {
|
|
2153
|
+
overlay: false,
|
|
2154
|
+
progress: false,
|
|
2155
|
+
webSocketURL: {
|
|
2156
|
+
protocol: 'ws'
|
|
2157
|
+
}
|
|
2158
|
+
});
|
|
2159
|
+
setDefault(devServer.client, 'overlay', false);
|
|
2160
|
+
setDefault(devServer.client, 'progress', false);
|
|
2161
|
+
setDefault(devServer.client, 'webSocketURL', {
|
|
2162
|
+
protocol: 'ws'
|
|
2163
|
+
});
|
|
2164
|
+
setDefault(devServer.client.webSocketURL, 'protocol', 'ws');
|
|
2165
|
+
const connectableHost = resolveClientHost(process.env.EXTENSION_DEV_SERVER_CONNECTABLE_HOST, devServer.host);
|
|
2166
|
+
if (connectableHost) setDefault(devServer.client.webSocketURL, 'hostname', connectableHost);
|
|
2167
|
+
if (devServer.port) setDefault(devServer.client.webSocketURL, 'port', devServer.port);
|
|
2168
|
+
setDefault(devServer, 'allowedHosts', 'all');
|
|
2169
|
+
setDefault(devServer, 'headers', {
|
|
2170
|
+
'Access-Control-Allow-Origin': '*'
|
|
2171
|
+
});
|
|
2172
|
+
setDefault(devServer, 'static', {
|
|
2173
|
+
watch: {
|
|
2174
|
+
ignored: /\bnode_modules\b/
|
|
2175
|
+
}
|
|
2176
|
+
});
|
|
2177
|
+
setDefault(devServer.static, 'watch', {
|
|
2178
|
+
ignored: /\bnode_modules\b/
|
|
2179
|
+
});
|
|
2180
|
+
isObject(devServer.static) && 'watch' in devServer.static && isObject(devServer.static.watch) && setDefault(devServer.static.watch, 'ignored', /\bnode_modules\b/);
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
function setDefault(obj, key, val) {
|
|
2184
|
+
if (isObject(obj) && void 0 === obj[key]) obj[key] = val;
|
|
2185
|
+
}
|
|
2186
|
+
const WILDCARD_HOSTS = new Set([
|
|
2187
|
+
'0.0.0.0',
|
|
2188
|
+
'::',
|
|
2189
|
+
'[::]',
|
|
2190
|
+
'::0',
|
|
2191
|
+
'*',
|
|
2192
|
+
''
|
|
2193
|
+
]);
|
|
2194
|
+
function resolveClientHost(connectable, bindHost) {
|
|
2195
|
+
const resolved = String(connectable ?? '').trim();
|
|
2196
|
+
if (resolved) return resolved;
|
|
2197
|
+
const bind = 'string' == typeof bindHost ? bindHost.trim() : '';
|
|
2198
|
+
if (!bind) return;
|
|
2199
|
+
return WILDCARD_HOSTS.has(bind) ? '127.0.0.1' : bind;
|
|
2200
|
+
}
|
|
2201
|
+
function isObject(x) {
|
|
2202
|
+
return 'object' == typeof x && null !== x;
|
|
2203
|
+
}
|
|
2204
|
+
class WebExtensionPlugin {
|
|
2205
|
+
options;
|
|
2206
|
+
constructor(options = {}){
|
|
2207
|
+
const { background } = options;
|
|
2208
|
+
if (background) {
|
|
2209
|
+
const { serviceWorkerEntry, pageEntry } = background;
|
|
2210
|
+
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.');
|
|
2211
|
+
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).
|
|
2212
|
+
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.`);
|
|
2213
|
+
}
|
|
2214
|
+
this.options = options;
|
|
2215
|
+
}
|
|
2216
|
+
apply(compiler) {
|
|
2217
|
+
const { background, experimental_output } = this.options;
|
|
2218
|
+
if (background?.serviceWorkerEntry) {
|
|
2219
|
+
const serviceWorkerEntry = this.options.background?.serviceWorkerEntry;
|
|
2220
|
+
const output = serviceWorkerEntry ? this.options.experimental_output?.[serviceWorkerEntry] : false;
|
|
2221
|
+
new WebExtensionServiceWorkerEntryPlugin(background, !!output).apply(compiler);
|
|
2222
|
+
}
|
|
2223
|
+
if (experimental_output) new WebExtensionContentScriptEntryPlugin(this.options).apply(compiler);
|
|
2224
|
+
if (false !== this.options.hmrConfig) new DevServerConfigPlugin().apply(compiler);
|
|
2225
|
+
new WebExtensionChuckLoaderRuntimePlugin(this.options).apply(compiler);
|
|
2226
|
+
new NoDangerNamePlugin().apply(compiler);
|
|
2227
|
+
compiler.hooks.environment.tap(WebExtensionPlugin.name, ()=>{
|
|
2228
|
+
const { output } = compiler.options;
|
|
2229
|
+
if (false !== output.environment.dynamicImport) output.environment.dynamicImport = true;
|
|
2230
|
+
if (void 0 !== output.hotUpdateChunkFilename) output.hotUpdateChunkFilename = 'hot/[id].[fullhash].js';
|
|
2231
|
+
if (void 0 !== output.hotUpdateMainFilename) output.hotUpdateMainFilename = 'hot/[runtime].[fullhash].json';
|
|
2232
|
+
});
|
|
2233
|
+
compiler.hooks.compilation.tap(WebExtensionPlugin.name, (compilation)=>{
|
|
2234
|
+
compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobal).tap(WebExtensionPlugin.name, (chunk)=>{
|
|
2235
|
+
compilation.addRuntimeModule(chunk, BrowserRuntimeModule(compiler.webpack, !!this.options.weakRuntimeCheck));
|
|
2236
|
+
return true;
|
|
2237
|
+
});
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
const webpack_target_webextension_fork = WebExtensionPlugin;
|
|
2242
|
+
function isFromFilepathList(filePath, filepathList) {
|
|
2243
|
+
return Object.values(filepathList || {}).some((value)=>value === filePath);
|
|
2244
|
+
}
|
|
2245
|
+
function getFilename(feature, filePath) {
|
|
2246
|
+
const entryExt = __rspack_external_path.extname(filePath);
|
|
2247
|
+
let fileOutputpath = feature;
|
|
2248
|
+
if ([
|
|
2249
|
+
'.js',
|
|
2250
|
+
'.jsx',
|
|
2251
|
+
'.tsx',
|
|
2252
|
+
'.ts',
|
|
2253
|
+
'.vue',
|
|
2254
|
+
'.svelte'
|
|
2255
|
+
].includes(entryExt)) fileOutputpath = fileOutputpath.replace(entryExt, '.js');
|
|
2256
|
+
if ([
|
|
2257
|
+
'.html',
|
|
2258
|
+
'.njk',
|
|
2259
|
+
'.nunjucks'
|
|
2260
|
+
].includes(entryExt)) fileOutputpath = fileOutputpath.replace(entryExt, '.html');
|
|
2261
|
+
if ([
|
|
2262
|
+
'.css',
|
|
2263
|
+
'.scss',
|
|
2264
|
+
'.sass',
|
|
2265
|
+
'.less'
|
|
2266
|
+
].includes(entryExt)) fileOutputpath = fileOutputpath.replace(entryExt, '.css');
|
|
2267
|
+
return unixify(fileOutputpath || '');
|
|
2268
|
+
}
|
|
2269
|
+
function unixify(filePath) {
|
|
2270
|
+
return filePath.replace(/\\/g, '/');
|
|
2271
|
+
}
|
|
2272
|
+
function resolveRootAbsoluteRef(ref, projectRoot, publicRoot) {
|
|
2273
|
+
if (!ref || !ref.startsWith('/')) return;
|
|
2274
|
+
if (projectRoot && ref.startsWith(projectRoot)) return;
|
|
2275
|
+
if (ref.startsWith('//')) return;
|
|
2276
|
+
const trimmed = ref.replace(/^\/+/, '');
|
|
2277
|
+
if (!trimmed) return;
|
|
2278
|
+
if (publicRoot && __rspack_external_fs.existsSync(__rspack_external_path.join(publicRoot, trimmed))) return;
|
|
2279
|
+
const candidate = __rspack_external_path.resolve(projectRoot, trimmed);
|
|
2280
|
+
const rel = __rspack_external_path.relative(projectRoot, candidate);
|
|
2281
|
+
if (rel.startsWith('..') || __rspack_external_path.isAbsolute(rel)) return;
|
|
2282
|
+
if (!__rspack_external_fs.existsSync(candidate) || !__rspack_external_fs.statSync(candidate).isFile()) return;
|
|
2283
|
+
return candidate;
|
|
2284
|
+
}
|
|
2285
|
+
function collectRootAbsoluteRefs(source) {
|
|
2286
|
+
const refs = new Set();
|
|
2287
|
+
const attrRe = /(?:src|href)\s*=\s*["'](\/[^"'#?]*)["']/gi;
|
|
2288
|
+
const urlRe = /url\(\s*["']?(\/[^"')#?]+)["']?\s*\)/gi;
|
|
2289
|
+
let m;
|
|
2290
|
+
while(m = attrRe.exec(source))refs.add(m[1]);
|
|
2291
|
+
while(m = urlRe.exec(source))refs.add(m[1]);
|
|
2292
|
+
return refs;
|
|
2293
|
+
}
|
|
2294
|
+
function background_background(manifest) {
|
|
2295
|
+
return manifest.background && manifest.background.scripts && {
|
|
2296
|
+
background: {
|
|
2297
|
+
scripts: [
|
|
2298
|
+
...new Set(manifest.background.scripts.map((script)=>getFilename("background/scripts.js", String(script))))
|
|
2299
|
+
]
|
|
2300
|
+
}
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
function normalizeManifestOutputPath(originalPath) {
|
|
2304
|
+
if (!originalPath) return originalPath;
|
|
2305
|
+
const unixPath = originalPath.replace(/\\/g, '/');
|
|
2306
|
+
if (/^\/public\//i.test(unixPath)) return unixPath.replace(/^\/public\//i, '');
|
|
2307
|
+
if (/^(?:\.\/)?public\//i.test(unixPath)) return unixPath.replace(/^(?:\.\/)?public\//i, '');
|
|
2308
|
+
if (/^\//.test(unixPath)) return unixPath.replace(/^\//, '');
|
|
2309
|
+
return unixPath;
|
|
2310
|
+
}
|
|
2311
|
+
function manifestPageOutputTarget(raw, compiledTarget, manifestPath) {
|
|
2312
|
+
const unixPath = raw.replace(/\\/g, '/');
|
|
2313
|
+
if (/^(?:\/public\/|(?:\.\/)?public\/)/i.test(unixPath)) return normalizeManifestOutputPath(unixPath);
|
|
2314
|
+
if (/^\//.test(unixPath)) {
|
|
2315
|
+
const rest = unixPath.replace(/^\/+/, '');
|
|
2316
|
+
if (manifestPath && rest) {
|
|
2317
|
+
const manifestDir = __rspack_external_path.dirname(manifestPath);
|
|
2318
|
+
const inPublic = __rspack_external_fs.existsSync(__rspack_external_path.join(manifestDir, 'public', rest));
|
|
2319
|
+
const inRoot = __rspack_external_fs.existsSync(__rspack_external_path.join(manifestDir, rest));
|
|
2320
|
+
if (inRoot && !inPublic) return compiledTarget;
|
|
2321
|
+
}
|
|
2322
|
+
return normalizeManifestOutputPath(unixPath);
|
|
2323
|
+
}
|
|
2324
|
+
return compiledTarget;
|
|
2325
|
+
}
|
|
2326
|
+
function iconOutputPath(raw) {
|
|
2327
|
+
if (/^(?:\/public\/|(?:\.\/)?public\/)/i.test(raw)) return normalizeManifestOutputPath(raw);
|
|
2328
|
+
const normalized = normalizeManifestOutputPath(raw).replace(/^\.\//, '');
|
|
2329
|
+
const escapes = !normalized || normalized.split('/').includes('..') || /^[A-Za-z]:/.test(normalized);
|
|
2330
|
+
return escapes ? `icons/${normalized.split('/').pop() || ''}` : normalized;
|
|
2331
|
+
}
|
|
2332
|
+
function browserAction(manifest) {
|
|
2333
|
+
return manifest.browser_action && {
|
|
2334
|
+
browser_action: {
|
|
2335
|
+
...manifest.browser_action,
|
|
2336
|
+
...manifest.browser_action.default_popup && {
|
|
2337
|
+
default_popup: getFilename('action/index.html', manifest.browser_action.default_popup)
|
|
2338
|
+
},
|
|
2339
|
+
...manifest.browser_action.default_icon && {
|
|
2340
|
+
default_icon: 'string' == typeof manifest.browser_action.default_icon ? (()=>{
|
|
2341
|
+
const raw = String(manifest.browser_action.default_icon);
|
|
2342
|
+
return getFilename(iconOutputPath(raw), raw);
|
|
2343
|
+
})() : Object.fromEntries(Object.entries(manifest.browser_action.default_icon).map(([size, icon])=>{
|
|
2344
|
+
const raw = String(icon);
|
|
2345
|
+
return [
|
|
2346
|
+
size,
|
|
2347
|
+
getFilename(iconOutputPath(raw), raw)
|
|
2348
|
+
];
|
|
2349
|
+
}))
|
|
2350
|
+
},
|
|
2351
|
+
...manifest.browser_action.theme_icons && {
|
|
2352
|
+
theme_icons: manifest.browser_action.theme_icons.map((themeIcon)=>({
|
|
2353
|
+
...themeIcon,
|
|
2354
|
+
...themeIcon.light && {
|
|
2355
|
+
light: (()=>{
|
|
2356
|
+
const raw = String(themeIcon.light);
|
|
2357
|
+
const isPublic = /^(?:\/public\/|(?:\.\/)?public\/)/i.test(raw);
|
|
2358
|
+
const target = isPublic ? normalizeManifestOutputPath(raw) : `browser_action/${__rspack_external_path.basename(raw)}`;
|
|
2359
|
+
return getFilename(target, raw);
|
|
2360
|
+
})()
|
|
2361
|
+
},
|
|
2362
|
+
...themeIcon.dark && {
|
|
2363
|
+
dark: (()=>{
|
|
2364
|
+
const raw = String(themeIcon.dark);
|
|
2365
|
+
const isPublic = /^(?:\/public\/|(?:\.\/)?public\/)/i.test(raw);
|
|
2366
|
+
const target = isPublic ? normalizeManifestOutputPath(raw) : `browser_action/${__rspack_external_path.basename(raw)}`;
|
|
2367
|
+
return getFilename(target, raw);
|
|
2368
|
+
})()
|
|
2369
|
+
}
|
|
2370
|
+
}))
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
};
|
|
2374
|
+
}
|
|
2375
|
+
function chromeSettingsOverrides(manifest) {
|
|
2376
|
+
return manifest.chrome_settings_overrides && {
|
|
2377
|
+
chrome_settings_overrides: {
|
|
2378
|
+
...manifest.chrome_settings_overrides.homepage && {
|
|
2379
|
+
homepage: manifest.chrome_settings_overrides.homepage
|
|
2380
|
+
},
|
|
2381
|
+
...manifest.chrome_settings_overrides.search_provider && {
|
|
2382
|
+
search_provider: {
|
|
2383
|
+
...manifest.chrome_settings_overrides.search_provider,
|
|
2384
|
+
...manifest.chrome_settings_overrides.search_provider.favicon_url && {
|
|
2385
|
+
favicon_url: (()=>{
|
|
2386
|
+
const fav = manifest.chrome_settings_overrides.search_provider.favicon_url;
|
|
2387
|
+
const isUrl = /^(?:[a-z]+:)?\/\//i.test(fav);
|
|
2388
|
+
return isUrl ? fav : getFilename(`chrome_settings_overrides/${fav.split('/').pop()}`, fav);
|
|
2389
|
+
})()
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
},
|
|
2393
|
+
...manifest.chrome_settings_overrides.startup_pages && {
|
|
2394
|
+
startup_pages: manifest.chrome_settings_overrides.startup_pages.map((page, index)=>getFilename(`chrome_settings_overrides/startup-${index}.html`, page))
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
};
|
|
2398
|
+
}
|
|
2399
|
+
function pageAction(manifest) {
|
|
2400
|
+
return manifest.page_action && {
|
|
2401
|
+
page_action: {
|
|
2402
|
+
...manifest.page_action,
|
|
2403
|
+
...manifest.page_action.default_popup && {
|
|
2404
|
+
default_popup: getFilename('action/index.html', manifest.page_action.default_popup)
|
|
2405
|
+
},
|
|
2406
|
+
...manifest.page_action.default_icon && {
|
|
2407
|
+
default_icon: 'string' == typeof manifest.page_action.default_icon ? (()=>{
|
|
2408
|
+
const raw = String(manifest.page_action.default_icon);
|
|
2409
|
+
return getFilename(iconOutputPath(raw), raw);
|
|
2410
|
+
})() : Object.fromEntries(Object.entries(manifest.page_action.default_icon).map(([size, icon])=>{
|
|
2411
|
+
const raw = String(icon);
|
|
2412
|
+
return [
|
|
2413
|
+
size,
|
|
2414
|
+
getFilename(iconOutputPath(raw), raw)
|
|
2415
|
+
];
|
|
2416
|
+
}))
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
};
|
|
2420
|
+
}
|
|
2421
|
+
function sidebarAction(manifest) {
|
|
2422
|
+
return manifest.sidebar_action && {
|
|
2423
|
+
sidebar_action: {
|
|
2424
|
+
...manifest.sidebar_action,
|
|
2425
|
+
...manifest.sidebar_action.default_panel && {
|
|
2426
|
+
default_panel: getFilename("sidebar/index.html", manifest.sidebar_action.default_panel)
|
|
2427
|
+
},
|
|
2428
|
+
...manifest.sidebar_action.default_icon && {
|
|
2429
|
+
default_icon: 'string' == typeof manifest.sidebar_action.default_icon ? (()=>{
|
|
2430
|
+
const raw = String(manifest.sidebar_action.default_icon);
|
|
2431
|
+
return getFilename(iconOutputPath(raw), raw);
|
|
2432
|
+
})() : Object.fromEntries(Object.entries(manifest.sidebar_action.default_icon).map(([size, icon])=>{
|
|
2433
|
+
const raw = String(icon);
|
|
2434
|
+
return [
|
|
2435
|
+
size,
|
|
2436
|
+
getFilename(iconOutputPath(raw), raw)
|
|
2437
|
+
];
|
|
2438
|
+
}))
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
};
|
|
2442
|
+
}
|
|
2443
|
+
const getBasename = (filepath)=>__rspack_external_path.basename(filepath);
|
|
2444
|
+
function themeExperiment(manifest) {
|
|
2445
|
+
const te = manifest.theme_experiment;
|
|
2446
|
+
return te && {
|
|
2447
|
+
theme_experiment: {
|
|
2448
|
+
...te,
|
|
2449
|
+
...'string' == typeof te.stylesheet && {
|
|
2450
|
+
stylesheet: getFilename(`theme_experiment/${getBasename(te.stylesheet)}`, te.stylesheet)
|
|
2451
|
+
},
|
|
2452
|
+
...Array.isArray(te.stylesheets) && {
|
|
2453
|
+
stylesheets: te.stylesheets.map((s, i)=>getFilename(`theme_experiment/stylesheet-${i}.css`, s))
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
function manifestV2(manifest) {
|
|
2459
|
+
return {
|
|
2460
|
+
...background_background(manifest),
|
|
2461
|
+
...browserAction(manifest),
|
|
2462
|
+
...pageAction(manifest),
|
|
2463
|
+
...sidebarAction(manifest),
|
|
2464
|
+
...chromeSettingsOverrides(manifest),
|
|
2465
|
+
...themeExperiment(manifest)
|
|
2466
|
+
};
|
|
2467
|
+
}
|
|
2468
|
+
function action(manifest) {
|
|
2469
|
+
return manifest.action && {
|
|
2470
|
+
action: {
|
|
2471
|
+
...manifest.action,
|
|
2472
|
+
...manifest.action.default_popup && {
|
|
2473
|
+
default_popup: getFilename("action/index.html", manifest.action.default_popup)
|
|
2474
|
+
},
|
|
2475
|
+
...manifest.action.default_icon && {
|
|
2476
|
+
default_icon: 'string' == typeof manifest.action.default_icon ? (()=>{
|
|
2477
|
+
const raw = String(manifest.action.default_icon);
|
|
2478
|
+
return getFilename(iconOutputPath(raw), raw);
|
|
2479
|
+
})() : Object.fromEntries(Object.entries(manifest.action.default_icon).map(([size, icon])=>{
|
|
2480
|
+
const raw = String(icon);
|
|
2481
|
+
return [
|
|
2482
|
+
size,
|
|
2483
|
+
getFilename(iconOutputPath(raw), raw)
|
|
2484
|
+
];
|
|
2485
|
+
}))
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
function backgroundServiceWorker(manifest) {
|
|
2491
|
+
return manifest.background && manifest.background.service_worker && {
|
|
2492
|
+
background: {
|
|
2493
|
+
service_worker: (()=>{
|
|
2494
|
+
const raw = String(manifest.background.service_worker);
|
|
2495
|
+
return getFilename('background/service_worker.js', raw);
|
|
2496
|
+
})()
|
|
2497
|
+
}
|
|
2498
|
+
};
|
|
2499
|
+
}
|
|
2500
|
+
function declarativeNetRequest(manifest) {
|
|
2501
|
+
return manifest.declarative_net_request && {
|
|
2502
|
+
declarative_net_request: {
|
|
2503
|
+
...manifest.declarative_net_request,
|
|
2504
|
+
rule_resources: manifest.declarative_net_request.rule_resources.map((resourceObj)=>({
|
|
2505
|
+
...resourceObj,
|
|
2506
|
+
path: resourceObj.path && getFilename(`declarative_net_request/${resourceObj.id}.json`, resourceObj.path)
|
|
2507
|
+
}))
|
|
2508
|
+
}
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2511
|
+
function hostPermissions(manifest) {
|
|
2512
|
+
return manifest.host_permissions && {
|
|
2513
|
+
host_permissions: manifest.host_permissions
|
|
2514
|
+
};
|
|
2515
|
+
}
|
|
2516
|
+
function sidePanel(manifest) {
|
|
2517
|
+
return manifest.side_panel && {
|
|
2518
|
+
side_panel: {
|
|
2519
|
+
...manifest.side_panel,
|
|
2520
|
+
...manifest.side_panel.default_path && {
|
|
2521
|
+
default_path: (()=>{
|
|
2522
|
+
const raw = String(manifest.side_panel.default_path);
|
|
2523
|
+
return getFilename('sidebar/index.html', raw);
|
|
2524
|
+
})()
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
};
|
|
2528
|
+
}
|
|
2529
|
+
function manifestV3(manifest) {
|
|
2530
|
+
return {
|
|
2531
|
+
...action(manifest),
|
|
2532
|
+
...backgroundServiceWorker(manifest),
|
|
2533
|
+
...declarativeNetRequest(manifest),
|
|
2534
|
+
...hostPermissions(manifest),
|
|
2535
|
+
...sidePanel(manifest)
|
|
2536
|
+
};
|
|
2537
|
+
}
|
|
2538
|
+
function backgroundPage(manifest) {
|
|
2539
|
+
return manifest.background && manifest.background.page && {
|
|
2540
|
+
background: {
|
|
2541
|
+
page: (()=>{
|
|
2542
|
+
const raw = String(manifest.background.page);
|
|
2543
|
+
return getFilename('background/index.html', raw);
|
|
2544
|
+
})()
|
|
2545
|
+
}
|
|
2546
|
+
};
|
|
2547
|
+
}
|
|
2548
|
+
function chromeUrlOverrides(manifest, manifestPath) {
|
|
2549
|
+
return manifest.chrome_url_overrides && {
|
|
2550
|
+
chrome_url_overrides: {
|
|
2551
|
+
...manifest.chrome_url_overrides.bookmarks && {
|
|
2552
|
+
bookmarks: (()=>{
|
|
2553
|
+
const raw = String(manifest.chrome_url_overrides.bookmarks);
|
|
2554
|
+
return getFilename(manifestPageOutputTarget(raw, 'chrome_url_overrides/bookmarks.html', manifestPath), raw);
|
|
2555
|
+
})()
|
|
2556
|
+
},
|
|
2557
|
+
...manifest.chrome_url_overrides.history && {
|
|
2558
|
+
history: (()=>{
|
|
2559
|
+
const raw = String(manifest.chrome_url_overrides.history);
|
|
2560
|
+
return getFilename(manifestPageOutputTarget(raw, 'chrome_url_overrides/history.html', manifestPath), raw);
|
|
2561
|
+
})()
|
|
2562
|
+
},
|
|
2563
|
+
...manifest.chrome_url_overrides.newtab && {
|
|
2564
|
+
newtab: (()=>{
|
|
2565
|
+
const raw = String(manifest.chrome_url_overrides.newtab);
|
|
2566
|
+
return getFilename(manifestPageOutputTarget(raw, 'chrome_url_overrides/newtab.html', manifestPath), raw);
|
|
2567
|
+
})()
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
function isBundledContentPath(filePath, ext) {
|
|
2573
|
+
const normalized = String(filePath || '').replace(/\\/g, '/');
|
|
2574
|
+
const bundledAsset = parseCanonicalContentScriptAsset(normalized);
|
|
2575
|
+
return bundledAsset?.extension === ext;
|
|
2576
|
+
}
|
|
2577
|
+
function isAlreadyBundledContentScripts(contentScripts) {
|
|
2578
|
+
if (!Array.isArray(contentScripts) || 0 === contentScripts.length) return false;
|
|
2579
|
+
return contentScripts.every((contentObj)=>{
|
|
2580
|
+
const js = Array.isArray(contentObj?.js) ? contentObj.js : [];
|
|
2581
|
+
const css = Array.isArray(contentObj?.css) ? contentObj.css : [];
|
|
2582
|
+
return js.every((filePath)=>isBundledContentPath(filePath, 'js')) && css.every((filePath)=>isBundledContentPath(filePath, 'css'));
|
|
2583
|
+
});
|
|
2584
|
+
}
|
|
2585
|
+
function content_scripts_contentScripts(manifest, manifestPath) {
|
|
2586
|
+
if (!manifest.content_scripts) return;
|
|
2587
|
+
const original = manifest.content_scripts;
|
|
2588
|
+
if (isAlreadyBundledContentScripts(original)) return {
|
|
2589
|
+
content_scripts: original
|
|
2590
|
+
};
|
|
2591
|
+
const originalCount = original.length;
|
|
2592
|
+
const result = [];
|
|
2593
|
+
let bridgeOrdinal = 0;
|
|
2594
|
+
for(let index = 0; index < original.length; index++){
|
|
2595
|
+
const contentObj = original[index] || {};
|
|
2596
|
+
const contentJs = [
|
|
2597
|
+
...new Set(contentObj.js || [])
|
|
2598
|
+
];
|
|
2599
|
+
const contentCss = [
|
|
2600
|
+
...new Set(contentObj.css || [])
|
|
2601
|
+
];
|
|
2602
|
+
if ('MAIN' === contentObj.world) {
|
|
2603
|
+
const bridgeIndex = originalCount + bridgeOrdinal++;
|
|
2604
|
+
const { world: _ignoredWorld, js: _ignoredJs, css: _ignoredCss, ...rest } = contentObj;
|
|
2605
|
+
result.push({
|
|
2606
|
+
...rest,
|
|
2607
|
+
js: [
|
|
2608
|
+
getFilename(getCanonicalContentScriptJsAssetName(bridgeIndex), 'main-world-bridge.js')
|
|
2609
|
+
],
|
|
2610
|
+
css: []
|
|
2611
|
+
});
|
|
2612
|
+
}
|
|
2613
|
+
result.push({
|
|
2614
|
+
...original[index] || {},
|
|
2615
|
+
js: [
|
|
2616
|
+
...new Set(contentJs.map((js)=>getFilename(getCanonicalContentScriptJsAssetName(index), js)))
|
|
2617
|
+
],
|
|
2618
|
+
css: [
|
|
2619
|
+
...new Set(contentCss.map((css)=>getFilename(getCanonicalContentScriptCssAssetName(index), css)))
|
|
2620
|
+
]
|
|
2621
|
+
});
|
|
2622
|
+
}
|
|
2623
|
+
return {
|
|
2624
|
+
content_scripts: result
|
|
2625
|
+
};
|
|
2626
|
+
}
|
|
2627
|
+
function devtoolsPage(manifest, manifestPath) {
|
|
2628
|
+
return manifest.devtools_page && {
|
|
2629
|
+
devtools_page: (()=>{
|
|
2630
|
+
const raw = String(manifest.devtools_page);
|
|
2631
|
+
return getFilename(manifestPageOutputTarget(raw, 'devtools/index.html', manifestPath), raw);
|
|
2632
|
+
})()
|
|
2633
|
+
};
|
|
2634
|
+
}
|
|
2635
|
+
function icons(manifest) {
|
|
2636
|
+
return manifest.icons && {
|
|
2637
|
+
icons: Object.fromEntries(Object.entries(manifest.icons).map(([size, icon])=>{
|
|
2638
|
+
const raw = String(icon);
|
|
2639
|
+
return [
|
|
2640
|
+
size,
|
|
2641
|
+
getFilename(iconOutputPath(raw), raw)
|
|
2642
|
+
];
|
|
2643
|
+
}))
|
|
2644
|
+
};
|
|
2645
|
+
}
|
|
2646
|
+
function commands(manifest) {
|
|
2647
|
+
return manifest.commands && {
|
|
2648
|
+
commands: manifest.commands
|
|
2649
|
+
};
|
|
2650
|
+
}
|
|
2651
|
+
function permissions(manifest) {
|
|
2652
|
+
return manifest.permissions && {
|
|
2653
|
+
permissions: manifest.permissions
|
|
2654
|
+
};
|
|
2655
|
+
}
|
|
2656
|
+
function optionsPage(manifest, manifestPath) {
|
|
2657
|
+
return manifest.options_page && {
|
|
2658
|
+
options_page: (()=>{
|
|
2659
|
+
const raw = String(manifest.options_page);
|
|
2660
|
+
return getFilename(manifestPageOutputTarget(raw, 'options/index.html', manifestPath), raw);
|
|
2661
|
+
})()
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2664
|
+
function optionsUi(manifest, manifestPath) {
|
|
2665
|
+
return manifest.options_ui && {
|
|
2666
|
+
options_ui: {
|
|
2667
|
+
...manifest.options_ui,
|
|
2668
|
+
...manifest.options_ui.page && {
|
|
2669
|
+
page: (()=>{
|
|
2670
|
+
const raw = String(manifest.options_ui.page);
|
|
2671
|
+
return getFilename(manifestPageOutputTarget(raw, 'options/index.html', manifestPath), raw);
|
|
2672
|
+
})()
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
};
|
|
2676
|
+
}
|
|
2677
|
+
function sandbox(manifest) {
|
|
2678
|
+
return manifest.sandbox && {
|
|
2679
|
+
sandbox: {
|
|
2680
|
+
...manifest.sandbox,
|
|
2681
|
+
...manifest.sandbox.pages && {
|
|
2682
|
+
pages: manifest.sandbox.pages.map((page, index)=>getFilename(`sandbox/page-${index}.html`, page))
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
};
|
|
2686
|
+
}
|
|
2687
|
+
function storage(manifest) {
|
|
2688
|
+
return manifest.storage && {
|
|
2689
|
+
storage: {
|
|
2690
|
+
...manifest.storage.managed_schema && {
|
|
2691
|
+
managed_schema: getFilename('storage/managed_schema.json', manifest.storage.managed_schema)
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
const theme_getBasename = (filepath)=>__rspack_external_path.basename(filepath);
|
|
2697
|
+
const rewriteThemeImage = (value)=>getFilename(`theme/images/${theme_getBasename(value)}`, value);
|
|
2698
|
+
function theme(manifest) {
|
|
2699
|
+
return manifest.theme && {
|
|
2700
|
+
theme: {
|
|
2701
|
+
...manifest.theme,
|
|
2702
|
+
...manifest.theme.images && {
|
|
2703
|
+
images: Object.fromEntries(Object.entries(manifest.theme.images).map(([key, value])=>[
|
|
2704
|
+
key,
|
|
2705
|
+
Array.isArray(value) ? value.map(rewriteThemeImage) : rewriteThemeImage(value)
|
|
2706
|
+
]))
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
};
|
|
2710
|
+
}
|
|
2711
|
+
function userScripts(manifest) {
|
|
2712
|
+
return manifest.user_scripts && {
|
|
2713
|
+
user_scripts: {
|
|
2714
|
+
...manifest.user_scripts,
|
|
2715
|
+
...manifest.user_scripts.api_script && {
|
|
2716
|
+
api_script: getFilename("user_scripts/api_script.js", manifest.user_scripts.api_script)
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
};
|
|
2720
|
+
}
|
|
2721
|
+
function normalizeOutputPath(originalPath) {
|
|
2722
|
+
if (!originalPath) return originalPath;
|
|
2723
|
+
const unix = originalPath.replace(/\\/g, '/');
|
|
2724
|
+
if (/[*?\[\]{}]/.test(unix)) return unix;
|
|
2725
|
+
if (/^\/public\//i.test(unix)) return unix.replace(/^\/public\//i, '');
|
|
2726
|
+
if (/^(?:\.\/)?public\//i.test(unix)) return unix.replace(/^(?:\.\/)?public\//i, '');
|
|
2727
|
+
if (/^\//.test(unix)) return unix.replace(/^\//, '');
|
|
2728
|
+
return unix;
|
|
2729
|
+
}
|
|
2730
|
+
function webAccessibleResources(manifest) {
|
|
2731
|
+
if (!manifest.web_accessible_resources || !manifest.web_accessible_resources.length) return;
|
|
2732
|
+
const mapResource = (resource)=>getFilename(normalizeOutputPath(resource), resource);
|
|
2733
|
+
const entries = manifest.web_accessible_resources.map((entry)=>{
|
|
2734
|
+
if ('string' == typeof entry) return mapResource(entry);
|
|
2735
|
+
if (!entry || !Array.isArray(entry.resources)) return;
|
|
2736
|
+
return {
|
|
2737
|
+
...entry,
|
|
2738
|
+
resources: entry.resources.map(mapResource)
|
|
2739
|
+
};
|
|
2740
|
+
}).filter((entry)=>void 0 !== entry);
|
|
2741
|
+
if (0 === entries.length) return;
|
|
2742
|
+
return {
|
|
2743
|
+
web_accessible_resources: entries
|
|
2744
|
+
};
|
|
2745
|
+
}
|
|
2746
|
+
function contentSecurityPolicy(manifest) {
|
|
2747
|
+
return manifest.content_security_policy && {
|
|
2748
|
+
content_security_policy: manifest.content_security_policy
|
|
2749
|
+
};
|
|
2750
|
+
}
|
|
2751
|
+
const omnibox_getBasename = (filepath)=>__rspack_external_path.basename(filepath);
|
|
2752
|
+
function omnibox_omnibox(manifest) {
|
|
2753
|
+
const omnibox = manifest.omnibox;
|
|
2754
|
+
return omnibox && {
|
|
2755
|
+
omnibox: {
|
|
2756
|
+
...omnibox,
|
|
2757
|
+
...omnibox.default_icon && {
|
|
2758
|
+
default_icon: 'string' == typeof omnibox.default_icon ? getFilename(`icons/${omnibox_getBasename(omnibox.default_icon)}`, omnibox.default_icon) : Object.fromEntries(Object.entries(omnibox.default_icon).map(([size, icon])=>[
|
|
2759
|
+
size,
|
|
2760
|
+
getFilename(`icons/${omnibox_getBasename(icon)}`, icon)
|
|
2761
|
+
]))
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
};
|
|
2765
|
+
}
|
|
2766
|
+
function manifestCommon(manifest, manifestPath) {
|
|
2767
|
+
return {
|
|
2768
|
+
...backgroundPage(manifest),
|
|
2769
|
+
...chromeUrlOverrides(manifest, manifestPath),
|
|
2770
|
+
...content_scripts_contentScripts(manifest, manifestPath),
|
|
2771
|
+
...devtoolsPage(manifest, manifestPath),
|
|
2772
|
+
...icons(manifest),
|
|
2773
|
+
...commands(manifest),
|
|
2774
|
+
...permissions(manifest),
|
|
2775
|
+
...optionsPage(manifest, manifestPath),
|
|
2776
|
+
...optionsUi(manifest, manifestPath),
|
|
2777
|
+
...sandbox(manifest),
|
|
2778
|
+
...storage(manifest),
|
|
2779
|
+
...theme(manifest),
|
|
2780
|
+
...userScripts(manifest),
|
|
2781
|
+
...webAccessibleResources(manifest),
|
|
2782
|
+
...contentSecurityPolicy(manifest),
|
|
2783
|
+
...omnibox_omnibox(manifest)
|
|
2784
|
+
};
|
|
2785
|
+
}
|
|
2786
|
+
function getManifestOverrides(manifestPath, manifest) {
|
|
2787
|
+
const manifestContent = manifest || JSON.parse(stripBom(__rspack_external_fs.readFileSync(manifestPath, 'utf8')));
|
|
2788
|
+
const omit = (obj, key)=>{
|
|
2789
|
+
if (!obj) return {};
|
|
2790
|
+
const { [key]: _ignored, ...rest } = obj;
|
|
2791
|
+
return rest;
|
|
2792
|
+
};
|
|
2793
|
+
const pickBackground = (obj)=>{
|
|
2794
|
+
const value = obj.background;
|
|
2795
|
+
return value && 'object' == typeof value ? value : {};
|
|
2796
|
+
};
|
|
2797
|
+
const common = manifestCommon(manifestContent, manifestPath);
|
|
2798
|
+
const mv2 = manifestV2(manifestContent);
|
|
2799
|
+
const mv3 = manifestV3(manifestContent);
|
|
2800
|
+
const backgroundMerged = {
|
|
2801
|
+
...manifestContent.background || {},
|
|
2802
|
+
...pickBackground(common),
|
|
2803
|
+
...pickBackground(mv2),
|
|
2804
|
+
...pickBackground(mv3)
|
|
2805
|
+
};
|
|
2806
|
+
const merged = {
|
|
2807
|
+
...manifestContent,
|
|
2808
|
+
...omit(common, 'background'),
|
|
2809
|
+
...omit(mv2, 'background'),
|
|
2810
|
+
...omit(mv3, 'background')
|
|
2811
|
+
};
|
|
2812
|
+
if (Object.keys(backgroundMerged).length) merged.background = backgroundMerged;
|
|
2813
|
+
return JSON.stringify(merged, null, 2);
|
|
2814
|
+
}
|
|
2815
|
+
function filterKeysForThisBrowser(manifest, browser) {
|
|
2816
|
+
const isSafariTarget = 'safari' === browser || 'webkit-based' === browser || String(browser).includes('webkit');
|
|
2817
|
+
const isChromiumTarget = isChromiumBasedBrowser(String(browser)) || isSafariTarget;
|
|
2818
|
+
const isGeckoTarget = constants_isGeckoBasedBrowser(String(browser));
|
|
2819
|
+
const chromiumPrefixes = new Set([
|
|
2820
|
+
'chromium',
|
|
2821
|
+
'chrome',
|
|
2822
|
+
'edge'
|
|
2823
|
+
]);
|
|
2824
|
+
const geckoPrefixes = new Set([
|
|
2825
|
+
'gecko',
|
|
2826
|
+
'firefox'
|
|
2827
|
+
]);
|
|
2828
|
+
const webkitPrefixes = new Set([
|
|
2829
|
+
'safari',
|
|
2830
|
+
'webkit'
|
|
2831
|
+
]);
|
|
2832
|
+
const isFamilyPrefix = (prefix)=>isChromiumTarget && chromiumPrefixes.has(prefix) || isGeckoTarget && geckoPrefixes.has(prefix);
|
|
2833
|
+
const isSpecificPrefix = (prefix)=>prefix === browser || isSafariTarget && webkitPrefixes.has(prefix);
|
|
2834
|
+
const resolve = (node)=>{
|
|
2835
|
+
if (Array.isArray(node)) return node.map((item)=>resolve(item));
|
|
2836
|
+
if (node && 'object' == typeof node) {
|
|
2837
|
+
const result = {};
|
|
2838
|
+
const familyMatches = {};
|
|
2839
|
+
const specificMatches = {};
|
|
2840
|
+
for (const [key, value] of Object.entries(node)){
|
|
2841
|
+
const indexOfColon = key.indexOf(':');
|
|
2842
|
+
if (-1 === indexOfColon) {
|
|
2843
|
+
result[key] = resolve(value);
|
|
2844
|
+
continue;
|
|
2845
|
+
}
|
|
2846
|
+
const prefix = key.substring(0, indexOfColon);
|
|
2847
|
+
const strippedKey = key.substring(indexOfColon + 1);
|
|
2848
|
+
if (isSpecificPrefix(prefix)) specificMatches[strippedKey] = resolve(value);
|
|
2849
|
+
else if (isFamilyPrefix(prefix)) familyMatches[strippedKey] = resolve(value);
|
|
2850
|
+
}
|
|
2851
|
+
for (const [strippedKey, value] of Object.entries(familyMatches))result[strippedKey] = value;
|
|
2852
|
+
for (const [strippedKey, value] of Object.entries(specificMatches))result[strippedKey] = value;
|
|
2853
|
+
return result;
|
|
2854
|
+
}
|
|
2855
|
+
return node;
|
|
2856
|
+
};
|
|
2857
|
+
return resolve(manifest);
|
|
2858
|
+
}
|
|
2859
|
+
const manifest_cjsRequire = createRequire(import.meta.url);
|
|
2860
|
+
const INTERNAL_MANIFEST_SOURCE = '__extensionjs_manifest_source__';
|
|
2861
|
+
const INTERNAL_MANIFEST_CURRENT_SOURCE = '__extensionjs_manifest_current_source__';
|
|
2862
|
+
function readAssetSource(asset) {
|
|
2863
|
+
if (!asset) return '';
|
|
2864
|
+
const source = asset.source;
|
|
2865
|
+
if ('string' == typeof source) return source;
|
|
2866
|
+
if ('function' == typeof source) {
|
|
2867
|
+
const out = source();
|
|
2868
|
+
return 'string' == typeof out ? out : String(out || '');
|
|
2869
|
+
}
|
|
2870
|
+
if (source && 'function' == typeof source.source) {
|
|
2871
|
+
const out = source.source();
|
|
2872
|
+
return 'string' == typeof out ? out : String(out || '');
|
|
2873
|
+
}
|
|
2874
|
+
return '';
|
|
2875
|
+
}
|
|
2876
|
+
function setOriginalManifestContent(compilation, source) {
|
|
2877
|
+
compilation[INTERNAL_MANIFEST_SOURCE] = source;
|
|
2878
|
+
}
|
|
2879
|
+
function getOriginalManifestContent(compilation) {
|
|
2880
|
+
return compilation[INTERNAL_MANIFEST_SOURCE];
|
|
2881
|
+
}
|
|
2882
|
+
function setCurrentManifestContent(compilation, source) {
|
|
2883
|
+
compilation[INTERNAL_MANIFEST_CURRENT_SOURCE] = source;
|
|
2884
|
+
}
|
|
2885
|
+
function getCurrentManifestContent(compilation) {
|
|
2886
|
+
return compilation[INTERNAL_MANIFEST_CURRENT_SOURCE];
|
|
2887
|
+
}
|
|
2888
|
+
function getManifestContent(compilation, manifestPath) {
|
|
2889
|
+
const currentManifest = getCurrentManifestContent(compilation);
|
|
2890
|
+
if (currentManifest) return parseJsonSafe(currentManifest);
|
|
2891
|
+
const getAsset = compilation.getAsset;
|
|
2892
|
+
if ('function' == typeof getAsset) {
|
|
2893
|
+
const manifestAsset = getAsset.call(compilation, 'manifest.json');
|
|
2894
|
+
const manifest = readAssetSource(manifestAsset);
|
|
2895
|
+
if (manifest) return parseJsonSafe(manifest);
|
|
2896
|
+
}
|
|
2897
|
+
const manifestAsset = compilation.assets?.['manifest.json'];
|
|
2898
|
+
if (manifestAsset) {
|
|
2899
|
+
const manifest = readAssetSource(manifestAsset);
|
|
2900
|
+
if (manifest) return parseJsonSafe(manifest);
|
|
2901
|
+
}
|
|
2902
|
+
const originalManifest = getOriginalManifestContent(compilation);
|
|
2903
|
+
if (originalManifest) return parseJsonSafe(originalManifest);
|
|
2904
|
+
try {
|
|
2905
|
+
const text = __rspack_external_fs.readFileSync(manifestPath, 'utf8');
|
|
2906
|
+
return parseJsonSafe(text);
|
|
2907
|
+
} catch {
|
|
2908
|
+
try {
|
|
2909
|
+
const resolved = manifest_cjsRequire.resolve(manifestPath);
|
|
2910
|
+
delete manifest_cjsRequire.cache[resolved];
|
|
2911
|
+
} catch {}
|
|
2912
|
+
return manifest_cjsRequire(manifestPath);
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
function buildCanonicalManifest(manifestPath, manifest, browser) {
|
|
2916
|
+
const filteredManifest = filterKeysForThisBrowser(manifest, browser);
|
|
2917
|
+
return {
|
|
2918
|
+
...filteredManifest,
|
|
2919
|
+
...JSON.parse(getManifestOverrides(manifestPath, filteredManifest))
|
|
2920
|
+
};
|
|
2921
|
+
}
|
|
2922
|
+
function backgroundIsRequiredMessageOnly(backgroundChunkName) {
|
|
2923
|
+
return `Check the ${pintor.yellow(backgroundChunkName.replace('/', '.'))} field in your ${pintor.yellow('manifest.json')} file.`;
|
|
2924
|
+
}
|
|
2925
|
+
function createIssue(compiler, message, type = 'error') {
|
|
2926
|
+
const ErrorCtor = compiler?.rspack?.WebpackError || Error;
|
|
2927
|
+
const issue = new ErrorCtor(message);
|
|
2928
|
+
issue.name = 'warning' === type ? 'ExtensionWarning' : 'ExtensionError';
|
|
2929
|
+
return issue;
|
|
2930
|
+
}
|
|
2931
|
+
function reportToCompilation(compilation, compiler, message, type = 'error', file) {
|
|
2932
|
+
const issue = createIssue(compiler, message, type);
|
|
2933
|
+
if (file) issue.file = file;
|
|
2934
|
+
const bucket = 'warning' === type ? 'warnings' : 'errors';
|
|
2935
|
+
compilation[bucket] ||= [];
|
|
2936
|
+
const already = compilation[bucket].some((e)=>(e?.file || '') === (issue.file || '') && String(e?.message) === String(issue.message));
|
|
2937
|
+
if (already) return;
|
|
2938
|
+
compilation[bucket].push(issue);
|
|
2939
|
+
}
|
|
2940
|
+
class SetupBackgroundEntry {
|
|
2941
|
+
manifestPath;
|
|
2942
|
+
browser;
|
|
2943
|
+
constructor(options){
|
|
2944
|
+
this.manifestPath = options.manifestPath;
|
|
2945
|
+
this.browser = options.browser || 'chrome';
|
|
2946
|
+
}
|
|
2947
|
+
getMissingBackgroundError(bgFile) {
|
|
2948
|
+
if (!__rspack_external_fs.existsSync(bgFile) && this.manifestPath) {
|
|
2949
|
+
const manifest = JSON.parse(stripBom(__rspack_external_fs.readFileSync(this.manifestPath, 'utf8')));
|
|
2950
|
+
const patched = filterKeysForThisBrowser(manifest, this.browser);
|
|
2951
|
+
const fieldKey = 3 === patched.manifest_version ? 'background/service_worker' : "background/scripts";
|
|
2952
|
+
return backgroundIsRequiredMessageOnly(fieldKey);
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
addDefaultEntry(compiler, name, defaultScript) {
|
|
2956
|
+
compiler.options.entry = {
|
|
2957
|
+
...compiler.options.entry,
|
|
2958
|
+
[name]: {
|
|
2959
|
+
import: [
|
|
2960
|
+
defaultScript
|
|
2961
|
+
]
|
|
2962
|
+
}
|
|
2963
|
+
};
|
|
2964
|
+
}
|
|
2965
|
+
apply(compiler) {
|
|
2966
|
+
const manifest = JSON.parse(stripBom(__rspack_external_fs.readFileSync(this.manifestPath, 'utf-8')));
|
|
2967
|
+
const browser = this.browser;
|
|
2968
|
+
const isGecko = constants_isGeckoBasedBrowser(String(browser));
|
|
2969
|
+
const minimumBgScript = resolveDevelopDistFile(isGecko ? 'minimum-firefox-file' : 'minimum-chromium-file');
|
|
2970
|
+
const dirname = __rspack_external_path.dirname(this.manifestPath);
|
|
2971
|
+
const filteredManifest = filterKeysForThisBrowser(manifest, browser) || manifest;
|
|
2972
|
+
const manifestBg = filteredManifest?.background ?? manifest.background;
|
|
2973
|
+
const manifestVersion = filteredManifest?.manifest_version ?? manifest.manifest_version;
|
|
2974
|
+
function hookError(maybeError) {
|
|
2975
|
+
compiler.hooks.thisCompilation.tap('run-chromium:setup-background-entry', (compilation)=>{
|
|
2976
|
+
reportToCompilation(compilation, compiler, maybeError, 'error');
|
|
2977
|
+
});
|
|
2978
|
+
}
|
|
2979
|
+
if (isGecko) {
|
|
2980
|
+
const bgScripts = manifestBg?.scripts;
|
|
2981
|
+
if (bgScripts && bgScripts.length > 0) {
|
|
2982
|
+
const bgScriptPath = __rspack_external_path.join(dirname, bgScripts[0]);
|
|
2983
|
+
const maybeError = this.getMissingBackgroundError(bgScriptPath);
|
|
2984
|
+
if (maybeError) hookError(maybeError);
|
|
2985
|
+
} else this.addDefaultEntry(compiler, "background/script", minimumBgScript);
|
|
2986
|
+
return;
|
|
2987
|
+
}
|
|
2988
|
+
if (2 === manifestVersion) {
|
|
2989
|
+
const bgScripts = manifestBg?.scripts;
|
|
2990
|
+
if (bgScripts && bgScripts.length > 0) {
|
|
2991
|
+
const bgScriptPath = __rspack_external_path.join(dirname, bgScripts[0]);
|
|
2992
|
+
const maybeError = this.getMissingBackgroundError(bgScriptPath);
|
|
2993
|
+
if (maybeError) hookError(maybeError);
|
|
2994
|
+
} else this.addDefaultEntry(compiler, "background/script", minimumBgScript);
|
|
2995
|
+
} else {
|
|
2996
|
+
const serviceWorker = manifestBg?.service_worker;
|
|
2997
|
+
if (serviceWorker) {
|
|
2998
|
+
const swPath = __rspack_external_path.join(dirname, serviceWorker);
|
|
2999
|
+
const maybeError = this.getMissingBackgroundError(swPath);
|
|
3000
|
+
if (maybeError) hookError(maybeError);
|
|
3001
|
+
const existingEntry = compiler.options.entry && 'background/service_worker' in compiler.options.entry ? compiler.options.entry['background/service_worker'] : void 0;
|
|
3002
|
+
if (!existingEntry && __rspack_external_fs.existsSync(swPath)) this.addDefaultEntry(compiler, 'background/service_worker', swPath);
|
|
3003
|
+
} else this.addDefaultEntry(compiler, 'background/service_worker', minimumBgScript);
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
class SetupReloadStrategy {
|
|
3008
|
+
manifestPath;
|
|
3009
|
+
browser;
|
|
3010
|
+
constructor(options){
|
|
3011
|
+
this.manifestPath = options.manifestPath;
|
|
3012
|
+
this.browser = options.browser || 'chrome';
|
|
3013
|
+
}
|
|
3014
|
+
getEntryName(manifest) {
|
|
3015
|
+
if (manifest.background) {
|
|
3016
|
+
if (constants_isGeckoBasedBrowser(String(this.browser))) return {
|
|
3017
|
+
pageEntry: "background/script",
|
|
3018
|
+
tryCatchWrapper: true,
|
|
3019
|
+
eagerChunkLoading: false
|
|
3020
|
+
};
|
|
3021
|
+
if (3 === manifest.manifest_version) return {
|
|
3022
|
+
serviceWorkerEntry: 'background/service_worker',
|
|
3023
|
+
tryCatchWrapper: true,
|
|
3024
|
+
eagerChunkLoading: false
|
|
3025
|
+
};
|
|
3026
|
+
if (2 === manifest.manifest_version) return {
|
|
3027
|
+
pageEntry: "background/script",
|
|
3028
|
+
tryCatchWrapper: true,
|
|
3029
|
+
eagerChunkLoading: false
|
|
3030
|
+
};
|
|
3031
|
+
}
|
|
3032
|
+
return {
|
|
3033
|
+
pageEntry: 'background',
|
|
3034
|
+
tryCatchWrapper: true,
|
|
3035
|
+
eagerChunkLoading: false
|
|
3036
|
+
};
|
|
3037
|
+
}
|
|
3038
|
+
apply(compiler) {
|
|
3039
|
+
const manifest = JSON.parse(stripBom(__rspack_external_fs.readFileSync(this.manifestPath, 'utf-8')));
|
|
3040
|
+
const patchedManifest = filterKeysForThisBrowser(manifest, this.browser);
|
|
3041
|
+
const contentScriptsMeta = {};
|
|
3042
|
+
try {
|
|
3043
|
+
const csList = Array.isArray(patchedManifest.content_scripts) ? patchedManifest.content_scripts : [];
|
|
3044
|
+
const originalCount = csList.length;
|
|
3045
|
+
let bridgeOrdinal = 0;
|
|
3046
|
+
for(let i = 0; i < csList.length; i++){
|
|
3047
|
+
const cs = csList[i];
|
|
3048
|
+
const bundleId = getCanonicalContentScriptJsAssetName(i);
|
|
3049
|
+
const isMain = cs?.world === 'MAIN';
|
|
3050
|
+
if (isMain) {
|
|
3051
|
+
const bridgeIndex = originalCount + bridgeOrdinal++;
|
|
3052
|
+
const bridgeBundleId = getCanonicalContentScriptJsAssetName(bridgeIndex);
|
|
3053
|
+
contentScriptsMeta[bundleId] = {
|
|
3054
|
+
index: i,
|
|
3055
|
+
bundleId,
|
|
3056
|
+
world: 'main',
|
|
3057
|
+
bridgeBundleId
|
|
3058
|
+
};
|
|
3059
|
+
contentScriptsMeta[bridgeBundleId] = {
|
|
3060
|
+
index: bridgeIndex,
|
|
3061
|
+
bundleId: bridgeBundleId,
|
|
3062
|
+
world: 'extension',
|
|
3063
|
+
role: 'main_world_bridge',
|
|
3064
|
+
mainBundleId: bundleId
|
|
3065
|
+
};
|
|
3066
|
+
} else contentScriptsMeta[bundleId] = {
|
|
3067
|
+
index: i,
|
|
3068
|
+
bundleId,
|
|
3069
|
+
world: 'extension'
|
|
3070
|
+
};
|
|
3071
|
+
}
|
|
3072
|
+
} catch {}
|
|
3073
|
+
new SetupBackgroundEntry({
|
|
3074
|
+
manifestPath: this.manifestPath,
|
|
3075
|
+
browser: this.browser
|
|
3076
|
+
}).apply(compiler);
|
|
3077
|
+
new webpack_target_webextension_fork({
|
|
3078
|
+
background: this.getEntryName(patchedManifest),
|
|
3079
|
+
hmrConfig: false,
|
|
3080
|
+
weakRuntimeCheck: true,
|
|
3081
|
+
contentScriptsMeta
|
|
3082
|
+
}).apply(compiler);
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
const DEV_SERVER_STRONG_MARKERS = [
|
|
3086
|
+
'@rspack/dev-server/client/index.js?',
|
|
3087
|
+
'@rspack/dev-server/client/utils/ansiHTML.js',
|
|
3088
|
+
'[HMR] Waiting for update signal from WDS...'
|
|
3089
|
+
];
|
|
3090
|
+
const DEV_SERVER_WEAK_MARKERS = [
|
|
3091
|
+
'webpack-dev-server',
|
|
3092
|
+
'WebSocketClient',
|
|
3093
|
+
'.hot.check()',
|
|
3094
|
+
'[HMR] Cannot find update. Need to do a full reload!'
|
|
3095
|
+
];
|
|
3096
|
+
function contentScriptRetainsDevServerRuntime(source) {
|
|
3097
|
+
return source.includes('@rspack/dev-server/client');
|
|
3098
|
+
}
|
|
3099
|
+
function stripDevServerStartupFromContentScript(source) {
|
|
3100
|
+
let nextSource = source;
|
|
3101
|
+
const startupModuleIds = getStartupModuleIds(source);
|
|
3102
|
+
for (const moduleId of startupModuleIds){
|
|
3103
|
+
const moduleBody = getModuleBody(source, moduleId);
|
|
3104
|
+
if (!moduleBody) continue;
|
|
3105
|
+
const strongHit = DEV_SERVER_STRONG_MARKERS.some((marker)=>moduleBody.includes(marker));
|
|
3106
|
+
const weakHits = DEV_SERVER_WEAK_MARKERS.filter((marker)=>moduleBody.includes(marker)).length;
|
|
3107
|
+
if (strongHit || weakHits >= 2) nextSource = stripStartupRequire(nextSource, moduleId);
|
|
3108
|
+
}
|
|
3109
|
+
return nextSource;
|
|
3110
|
+
}
|
|
3111
|
+
function getStartupModuleIds(source) {
|
|
3112
|
+
const startupIndex = source.indexOf('// startup');
|
|
3113
|
+
if (-1 === startupIndex) return [];
|
|
3114
|
+
const startupSection = source.slice(startupIndex);
|
|
3115
|
+
const requirePattern = /__webpack_require__\((\d+)\);/g;
|
|
3116
|
+
const ids = [];
|
|
3117
|
+
let match = null;
|
|
3118
|
+
while(match = requirePattern.exec(startupSection))ids.push(match[1]);
|
|
3119
|
+
return ids;
|
|
3120
|
+
}
|
|
3121
|
+
function getModuleBody(source, moduleId) {
|
|
3122
|
+
const moduleHeaderPattern = new RegExp(`(?:^|\\n)${moduleId}\\([^)]*\\)\\s*\\{`, 'm');
|
|
3123
|
+
const headerMatch = moduleHeaderPattern.exec(source);
|
|
3124
|
+
if (!headerMatch) return null;
|
|
3125
|
+
const moduleStart = headerMatch.index;
|
|
3126
|
+
const nextHeaderPattern = /(?:^|\n)\d+\([^)]*\)\s*\{/g;
|
|
3127
|
+
nextHeaderPattern.lastIndex = moduleStart + headerMatch[0].length;
|
|
3128
|
+
const nextHeaderMatch = nextHeaderPattern.exec(source);
|
|
3129
|
+
return source.slice(moduleStart, nextHeaderMatch ? nextHeaderMatch.index : source.length);
|
|
3130
|
+
}
|
|
3131
|
+
function stripStartupRequire(source, moduleId) {
|
|
3132
|
+
const startupRequirePattern = new RegExp(`^\\s*__webpack_require__\\(${moduleId}\\);\\n?`, 'm');
|
|
3133
|
+
return source.replace(startupRequirePattern, '');
|
|
3134
|
+
}
|
|
3135
|
+
const CONTENT_SCRIPT_ASSET = /(^|\/)content_scripts\/content-\d+(?:\.[a-f0-9]+)?\.js$/i;
|
|
3136
|
+
class StripContentScriptDevServerRuntime {
|
|
3137
|
+
apply(compiler) {
|
|
3138
|
+
compiler.hooks.thisCompilation.tap(StripContentScriptDevServerRuntime.name, (compilation)=>{
|
|
3139
|
+
compilation.hooks.processAssets.tap({
|
|
3140
|
+
name: StripContentScriptDevServerRuntime.name,
|
|
3141
|
+
stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT
|
|
3142
|
+
}, ()=>{
|
|
3143
|
+
for (const asset of compilation.getAssets()){
|
|
3144
|
+
if (!CONTENT_SCRIPT_ASSET.test(asset.name)) continue;
|
|
3145
|
+
const originalSource = asset.source.source().toString();
|
|
3146
|
+
const strippedSource = stripDevServerStartupFromContentScript(originalSource);
|
|
3147
|
+
if (strippedSource !== originalSource) compilation.updateAsset(asset.name, new core_sources.RawSource(strippedSource));
|
|
3148
|
+
if (contentScriptRetainsDevServerRuntime(strippedSource)) {
|
|
3149
|
+
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.`);
|
|
3150
|
+
warning.name = 'ContentScriptDevServerRuntimeWarning';
|
|
3151
|
+
warning.file = asset.name;
|
|
3152
|
+
compilation.warnings ||= [];
|
|
3153
|
+
compilation.warnings.push(warning);
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
});
|
|
3157
|
+
});
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
const SCRIPTS_REPLAY_SHIM_SOURCE = `;(function () {
|
|
3161
|
+
try {
|
|
3162
|
+
if (typeof globalThis !== "object" || !globalThis) return;
|
|
3163
|
+
var chromeRef =
|
|
3164
|
+
(globalThis.chrome && globalThis.chrome.scripting && typeof globalThis.chrome.scripting.executeScript === "function")
|
|
3165
|
+
? globalThis.chrome
|
|
3166
|
+
: (globalThis.browser && globalThis.browser.scripting && typeof globalThis.browser.scripting.executeScript === "function")
|
|
3167
|
+
? globalThis.browser
|
|
3168
|
+
: null;
|
|
3169
|
+
if (!chromeRef) return;
|
|
3170
|
+
if (globalThis.__extjsScriptsReplayInstalled) return;
|
|
3171
|
+
globalThis.__extjsScriptsReplayInstalled = true;
|
|
3172
|
+
|
|
3173
|
+
var registry = new Map();
|
|
3174
|
+
var originalExecuteScript = chromeRef.scripting.executeScript.bind(chromeRef.scripting);
|
|
3175
|
+
|
|
3176
|
+
var serialize = function (entry) {
|
|
3177
|
+
try {
|
|
3178
|
+
return JSON.stringify({
|
|
3179
|
+
files: entry && Array.isArray(entry.files) ? entry.files : [],
|
|
3180
|
+
world: entry && entry.world ? String(entry.world) : ""
|
|
3181
|
+
});
|
|
3182
|
+
} catch (error) {
|
|
3183
|
+
return "";
|
|
3184
|
+
}
|
|
3185
|
+
};
|
|
3186
|
+
|
|
3187
|
+
var track = function (injection) {
|
|
3188
|
+
try {
|
|
3189
|
+
var tabId = injection && injection.target && injection.target.tabId;
|
|
3190
|
+
var files =
|
|
3191
|
+
injection && Array.isArray(injection.files) ? injection.files.slice() : null;
|
|
3192
|
+
if (typeof tabId !== "number") return;
|
|
3193
|
+
if (!files || !files.length) return;
|
|
3194
|
+
var world = injection.world ? String(injection.world) : undefined;
|
|
3195
|
+
var entry = { files: files, world: world, sig: serialize({ files: files, world: world }) };
|
|
3196
|
+
var existing = registry.get(tabId) || [];
|
|
3197
|
+
// Dedupe by signature — repeated identical injections (e.g. user
|
|
3198
|
+
// clicks the action twice) shouldn't stack in the replay list.
|
|
3199
|
+
if (existing.some(function (e) { return e.sig === entry.sig; })) return;
|
|
3200
|
+
existing.push(entry);
|
|
3201
|
+
// Cap to the most recent 50 distinct injections per tab so a misbehaving
|
|
3202
|
+
// user loop can't grow the registry unbounded.
|
|
3203
|
+
registry.set(tabId, existing.slice(-50));
|
|
3204
|
+
} catch (error) {
|
|
3205
|
+
// Tracking is best-effort; never break the user's executeScript call.
|
|
3206
|
+
}
|
|
3207
|
+
};
|
|
3208
|
+
|
|
3209
|
+
chromeRef.scripting.executeScript = function (injection, callback) {
|
|
3210
|
+
track(injection);
|
|
3211
|
+
return originalExecuteScript(injection, callback);
|
|
3212
|
+
};
|
|
3213
|
+
|
|
3214
|
+
var normalizeFile = function (value) {
|
|
3215
|
+
return String(value || "").replace(/^[/\\\\]+/, "");
|
|
3216
|
+
};
|
|
3217
|
+
|
|
3218
|
+
var fileMatches = function (entryFile, changedNormalized) {
|
|
3219
|
+
var fn = normalizeFile(entryFile);
|
|
3220
|
+
for (var i = 0; i < changedNormalized.length; i++) {
|
|
3221
|
+
var c = changedNormalized[i];
|
|
3222
|
+
if (!c) continue;
|
|
3223
|
+
if (fn === c) return true;
|
|
3224
|
+
if (fn.length > c.length && fn.slice(fn.length - c.length - 1) === "/" + c) return true;
|
|
3225
|
+
if (c.length > fn.length && c.slice(c.length - fn.length - 1) === "/" + fn) return true;
|
|
3226
|
+
}
|
|
3227
|
+
return false;
|
|
3228
|
+
};
|
|
3229
|
+
|
|
3230
|
+
globalThis.__extjsScriptsReplay = function (changedFiles) {
|
|
3231
|
+
var changedNormalized = (Array.isArray(changedFiles) ? changedFiles : []).map(normalizeFile);
|
|
3232
|
+
var promises = [];
|
|
3233
|
+
registry.forEach(function (entries, tabId) {
|
|
3234
|
+
entries.forEach(function (entry) {
|
|
3235
|
+
var matches = false;
|
|
3236
|
+
for (var i = 0; i < entry.files.length; i++) {
|
|
3237
|
+
if (fileMatches(entry.files[i], changedNormalized)) {
|
|
3238
|
+
matches = true;
|
|
3239
|
+
break;
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
if (!matches) return;
|
|
3243
|
+
try {
|
|
3244
|
+
promises.push(
|
|
3245
|
+
originalExecuteScript({
|
|
3246
|
+
target: { tabId: tabId },
|
|
3247
|
+
files: entry.files,
|
|
3248
|
+
world: entry.world
|
|
3249
|
+
}).then(
|
|
3250
|
+
function () { return { ok: true, tabId: tabId, files: entry.files }; },
|
|
3251
|
+
function (error) {
|
|
3252
|
+
return {
|
|
3253
|
+
ok: false,
|
|
3254
|
+
tabId: tabId,
|
|
3255
|
+
files: entry.files,
|
|
3256
|
+
error: String((error && error.message) || error)
|
|
3257
|
+
};
|
|
3258
|
+
}
|
|
3259
|
+
)
|
|
3260
|
+
);
|
|
3261
|
+
} catch (error) {
|
|
3262
|
+
// Tab may have closed; skip silently.
|
|
3263
|
+
}
|
|
3264
|
+
});
|
|
3265
|
+
});
|
|
3266
|
+
return Promise.all(promises);
|
|
3267
|
+
};
|
|
3268
|
+
} catch (error) {
|
|
3269
|
+
// Best-effort shim; do not break the SW bootstrap on any failure.
|
|
3270
|
+
}
|
|
3271
|
+
})();
|
|
3272
|
+
`;
|
|
3273
|
+
const BACKGROUND_ASSET = /(^|\/)background\/(?:service_worker|script)\.js$/i;
|
|
3274
|
+
class InjectScriptsReplayShim {
|
|
3275
|
+
apply(compiler) {
|
|
3276
|
+
compiler.hooks.thisCompilation.tap(InjectScriptsReplayShim.name, (compilation)=>{
|
|
3277
|
+
compilation.hooks.processAssets.tap({
|
|
3278
|
+
name: InjectScriptsReplayShim.name,
|
|
3279
|
+
stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT + 100
|
|
3280
|
+
}, ()=>{
|
|
3281
|
+
for (const asset of compilation.getAssets()){
|
|
3282
|
+
if (!BACKGROUND_ASSET.test(asset.name)) continue;
|
|
3283
|
+
const original = asset.source.source().toString();
|
|
3284
|
+
if (-1 !== original.indexOf('__extjsScriptsReplayInstalled')) continue;
|
|
3285
|
+
const next = SCRIPTS_REPLAY_SHIM_SOURCE + '\n' + original;
|
|
3286
|
+
compilation.updateAsset(asset.name, new core_sources.RawSource(next));
|
|
3287
|
+
}
|
|
3288
|
+
});
|
|
3289
|
+
});
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
const BRIDGE_PRODUCER_SOURCE = `;(function () {
|
|
3293
|
+
try {
|
|
3294
|
+
var g = (typeof globalThis === "object" && globalThis) ? globalThis : this;
|
|
3295
|
+
if (!g || g.__extjsBridgeProducerInstalled) return;
|
|
3296
|
+
|
|
3297
|
+
var PORT = parseInt("__EXTJS_CONTROL_PORT__", 10);
|
|
3298
|
+
var INSTANCE_ID = "__EXTJS_INSTANCE_ID__";
|
|
3299
|
+
var CONTEXT = "__EXTJS_CONTEXT__";
|
|
3300
|
+
// Connectable host of the dev-server control WS. Baked from the resolved
|
|
3301
|
+
// connectable host (loopback locally; the public host for remote/devcontainer).
|
|
3302
|
+
var HOST = "__EXTJS_CONTROL_HOST__";
|
|
3303
|
+
if (!HOST || HOST.indexOf("__EXTJS") === 0) HOST = "127.0.0.1";
|
|
3304
|
+
if (!PORT || PORT < 1) return;
|
|
3305
|
+
|
|
3306
|
+
var WS = g.WebSocket;
|
|
3307
|
+
if (typeof WS !== "function") return;
|
|
3308
|
+
|
|
3309
|
+
var consoleRef = g.console || {};
|
|
3310
|
+
g.__extjsBridgeProducerInstalled = true;
|
|
3311
|
+
|
|
3312
|
+
// Capture extension event listeners at install time. The producer is
|
|
3313
|
+
// prepended to the background bundle, so these wraps run BEFORE user code
|
|
3314
|
+
// registers its listeners — letting the bridge replay them on demand. The
|
|
3315
|
+
// platform exposes no API to dispatch these events, and CDP attaches too
|
|
3316
|
+
// late to wrap addListener, so this is the only path. Replay invokes the
|
|
3317
|
+
// handler WITHOUT a user gesture, so the gesture-derived activeTab grant
|
|
3318
|
+
// does NOT apply (callers are told gesture:false). This is engine-agnostic:
|
|
3319
|
+
// it works on Chromium and Gecko because it only touches addListener.
|
|
3320
|
+
//
|
|
3321
|
+
// captureEvent transparently wraps addListener/removeListener (delegating to
|
|
3322
|
+
// the originals) and records callbacks into the sink array.
|
|
3323
|
+
function captureEvent(event, sink) {
|
|
3324
|
+
try {
|
|
3325
|
+
if (!event || typeof event.addListener !== "function") return;
|
|
3326
|
+
var origAdd = event.addListener.bind(event);
|
|
3327
|
+
event.addListener = function (cb) {
|
|
3328
|
+
if (typeof cb === "function" && sink.indexOf(cb) === -1) sink.push(cb);
|
|
3329
|
+
return origAdd(cb);
|
|
3330
|
+
};
|
|
3331
|
+
if (typeof event.removeListener === "function") {
|
|
3332
|
+
var origRemove = event.removeListener.bind(event);
|
|
3333
|
+
event.removeListener = function (cb) {
|
|
3334
|
+
var i = sink.indexOf(cb);
|
|
3335
|
+
if (i !== -1) sink.splice(i, 1);
|
|
3336
|
+
return origRemove(cb);
|
|
3337
|
+
};
|
|
3338
|
+
}
|
|
3339
|
+
} catch (e) { /* non-fatal: trigger falls back to its no-listener reply */ }
|
|
3340
|
+
}
|
|
3341
|
+
|
|
3342
|
+
// Use g.chrome (not the hoisted chrome var below) — this runs first.
|
|
3343
|
+
var actionClickedListeners = [];
|
|
3344
|
+
var commandListeners = [];
|
|
3345
|
+
if (g.chrome) {
|
|
3346
|
+
captureEvent(g.chrome.action && g.chrome.action.onClicked, actionClickedListeners);
|
|
3347
|
+
captureEvent(g.chrome.commands && g.chrome.commands.onCommand, commandListeners);
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
var LEVELS = ["log", "info", "warn", "error", "debug", "trace"];
|
|
3351
|
+
var socket = null;
|
|
3352
|
+
var open = false;
|
|
3353
|
+
var queue = [];
|
|
3354
|
+
var backoff = 250;
|
|
3355
|
+
var MAX_BACKOFF = 5000;
|
|
3356
|
+
var MAX_QUEUE = 1000;
|
|
3357
|
+
var MAX_RESULT_BYTES = 256 * 1024;
|
|
3358
|
+
|
|
3359
|
+
function nowId() {
|
|
3360
|
+
return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
|
|
3361
|
+
}
|
|
3362
|
+
|
|
3363
|
+
function engineName() {
|
|
3364
|
+
try {
|
|
3365
|
+
return (typeof navigator !== "undefined" && /firefox/i.test(navigator.userAgent)) ? "firefox" : "chromium";
|
|
3366
|
+
} catch (e) { return "chromium"; }
|
|
3367
|
+
}
|
|
3368
|
+
|
|
3369
|
+
// JSON-safe, byte-capped clone of a result value.
|
|
3370
|
+
function safeValue(v) {
|
|
3371
|
+
try {
|
|
3372
|
+
var s = JSON.stringify(v);
|
|
3373
|
+
if (s === undefined) return {value: undefined, truncated: false};
|
|
3374
|
+
if (s.length > MAX_RESULT_BYTES) {
|
|
3375
|
+
return {value: {__type: "truncated", preview: s.slice(0, 1024)}, truncated: true};
|
|
3376
|
+
}
|
|
3377
|
+
return {value: JSON.parse(s), truncated: false};
|
|
3378
|
+
} catch (e) {
|
|
3379
|
+
return {value: {__type: "unserializable", preview: String(v).slice(0, 512)}, truncated: false};
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3383
|
+
function replyOk(cmdId, value) {
|
|
3384
|
+
var s = safeValue(value);
|
|
3385
|
+
var frame = {type: "result", cmdId: cmdId, ok: true, value: s.value};
|
|
3386
|
+
if (s.truncated) frame.truncated = true;
|
|
3387
|
+
try { socket && socket.send(JSON.stringify(frame)); } catch (e) {}
|
|
3388
|
+
}
|
|
3389
|
+
|
|
3390
|
+
function replyErr(cmdId, name, message) {
|
|
3391
|
+
var frame = {type: "result", cmdId: cmdId, ok: false, error: {name: name, message: String(message), engine: engineName()}};
|
|
3392
|
+
try { socket && socket.send(JSON.stringify(frame)); } catch (e) {}
|
|
3393
|
+
}
|
|
3394
|
+
|
|
3395
|
+
// Execute one authorized command in the SW (or route to a tab). chrome.*
|
|
3396
|
+
// promise APIs are used; callback-only APIs are wrapped.
|
|
3397
|
+
function executeCommand(cmd) {
|
|
3398
|
+
var op = cmd.op, target = cmd.target || {}, args = cmd.args || {};
|
|
3399
|
+
var ctx = target.context || "background";
|
|
3400
|
+
var chrome = g.chrome;
|
|
3401
|
+
var cmdId = cmd.cmdId;
|
|
3402
|
+
try {
|
|
3403
|
+
if (!chrome) { replyErr(cmdId, "Unsupported", "chrome.* not available in this context"); return; }
|
|
3404
|
+
if (op === "eval") {
|
|
3405
|
+
if (ctx === "background") {
|
|
3406
|
+
Promise.resolve().then(function () { return (0, eval)(args.expression); })
|
|
3407
|
+
.then(function (r) { replyOk(cmdId, r); }, function (e) {
|
|
3408
|
+
var msg = (e && e.message) || String(e);
|
|
3409
|
+
// MV3 forbids eval of strings in the SW/extension pages; Chrome
|
|
3410
|
+
// rejects 'unsafe-eval' in MV3 extension_pages, so this can't be
|
|
3411
|
+
// relaxed. Surface it honestly with a usable alternative.
|
|
3412
|
+
if (/Content Security Policy|unsafe-eval/i.test(msg)) {
|
|
3413
|
+
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());
|
|
3414
|
+
} else {
|
|
3415
|
+
replyErr(cmdId, (e && e.name) || "EvalError", msg);
|
|
3416
|
+
}
|
|
3417
|
+
});
|
|
3418
|
+
} else if ((ctx === "content" || ctx === "page") && target.tabId && chrome.scripting) {
|
|
3419
|
+
chrome.scripting.executeScript({
|
|
3420
|
+
target: {tabId: target.tabId},
|
|
3421
|
+
world: ctx === "page" ? "MAIN" : "ISOLATED",
|
|
3422
|
+
func: function (src) { return eval(src); },
|
|
3423
|
+
args: [String(args.expression)]
|
|
3424
|
+
}).then(function (res) { replyOk(cmdId, res && res[0] ? res[0].result : undefined); },
|
|
3425
|
+
function (e) { replyErr(cmdId, "EvalError", e); });
|
|
3426
|
+
} else {
|
|
3427
|
+
replyErr(cmdId, "Unsupported", "eval in context " + ctx + " requires a tabId");
|
|
3428
|
+
}
|
|
3429
|
+
return;
|
|
3430
|
+
}
|
|
3431
|
+
if (op === "storage.get") {
|
|
3432
|
+
chrome.storage[args.area || "local"].get(args.key != null ? args.key : null)
|
|
3433
|
+
.then(function (r) { replyOk(cmdId, r); }, function (e) { replyErr(cmdId, "StorageError", e); });
|
|
3434
|
+
return;
|
|
3435
|
+
}
|
|
3436
|
+
if (op === "storage.set") {
|
|
3437
|
+
chrome.storage[args.area || "local"].set(args.items || {})
|
|
3438
|
+
.then(function () { replyOk(cmdId, {set: Object.keys(args.items || {})}); }, function (e) { replyErr(cmdId, "StorageError", e); });
|
|
3439
|
+
return;
|
|
3440
|
+
}
|
|
3441
|
+
if (op === "reload") {
|
|
3442
|
+
if (ctx === "background") {
|
|
3443
|
+
replyOk(cmdId, {reloading: true});
|
|
3444
|
+
setTimeout(function () { try { chrome.runtime.reload(); } catch (e) {} }, 50);
|
|
3445
|
+
} else if (target.tabId) {
|
|
3446
|
+
chrome.tabs.reload(target.tabId).then(function () { replyOk(cmdId, {reloaded: target.tabId}); }, function (e) { replyErr(cmdId, "ReloadError", e); });
|
|
3447
|
+
} else {
|
|
3448
|
+
replyErr(cmdId, "Unsupported", "reload needs background or a tabId");
|
|
3449
|
+
}
|
|
3450
|
+
return;
|
|
3451
|
+
}
|
|
3452
|
+
if (op === "open") {
|
|
3453
|
+
var surface = args.surface || ctx;
|
|
3454
|
+
// getPopup is promise-style on Gecko and (MV3) Chromium; fall back to
|
|
3455
|
+
// callback-style for older Chromium. Always resolves to a string.
|
|
3456
|
+
var getActionPopup = function (cb) {
|
|
3457
|
+
try {
|
|
3458
|
+
var r = chrome.action && chrome.action.getPopup && chrome.action.getPopup({});
|
|
3459
|
+
if (r && typeof r.then === "function") {
|
|
3460
|
+
r.then(function (p) { cb(p || ""); }, function () { cb(""); });
|
|
3461
|
+
return;
|
|
3462
|
+
}
|
|
3463
|
+
} catch (e) {}
|
|
3464
|
+
try { chrome.action.getPopup({}, function (p) { cb(p || ""); }); }
|
|
3465
|
+
catch (e) { cb(""); }
|
|
3466
|
+
};
|
|
3467
|
+
// Resolve the tab a replayed event should carry: an explicit args.tabId,
|
|
3468
|
+
// else the active tab of the focused window.
|
|
3469
|
+
var resolveActiveTab = function (a, cb) {
|
|
3470
|
+
if (a && typeof a.tabId === "number") {
|
|
3471
|
+
try { chrome.tabs.get(a.tabId, function (t) { cb(t || {id: a.tabId}); }); return; }
|
|
3472
|
+
catch (e) {}
|
|
3473
|
+
}
|
|
3474
|
+
try { chrome.tabs.query({active: true, lastFocusedWindow: true}, function (tabs) { cb((tabs && tabs[0]) || undefined); }); }
|
|
3475
|
+
catch (e) { cb(undefined); }
|
|
3476
|
+
};
|
|
3477
|
+
// Replaying a listener carries no user gesture, so activeTab is never
|
|
3478
|
+
// granted. Warn when the manifest declares it (handler will diverge
|
|
3479
|
+
// from a real click).
|
|
3480
|
+
var activeTabWarning = function () {
|
|
3481
|
+
try {
|
|
3482
|
+
var m = chrome.runtime.getManifest();
|
|
3483
|
+
var perms = (m && m.permissions) || [];
|
|
3484
|
+
if (perms.indexOf("activeTab") !== -1) {
|
|
3485
|
+
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";
|
|
3486
|
+
}
|
|
3487
|
+
} catch (e) {}
|
|
3488
|
+
return null;
|
|
3489
|
+
};
|
|
3490
|
+
if (surface === "popup") {
|
|
3491
|
+
if (chrome.action && chrome.action.openPopup) {
|
|
3492
|
+
chrome.action.openPopup().then(function () { replyOk(cmdId, {opened: "popup"}); }, function (e) { replyErr(cmdId, "Unsupported", "openPopup: " + e); });
|
|
3493
|
+
} else { replyErr(cmdId, "Unsupported", "action.openPopup not available"); }
|
|
3494
|
+
} else if (surface === "options") {
|
|
3495
|
+
try { chrome.runtime.openOptionsPage(function () { replyOk(cmdId, {opened: "options"}); }); }
|
|
3496
|
+
catch (e) { replyErr(cmdId, "Unsupported", "openOptionsPage: " + e); }
|
|
3497
|
+
} else if (surface === "sidebar") {
|
|
3498
|
+
if (chrome.sidePanel && chrome.sidePanel.open && chrome.windows) {
|
|
3499
|
+
chrome.windows.getCurrent(function (w) {
|
|
3500
|
+
chrome.sidePanel.open({windowId: w.id}).then(function () { replyOk(cmdId, {opened: "sidebar"}); }, function (e) { replyErr(cmdId, "Unsupported", "sidePanel.open: " + e); });
|
|
3501
|
+
});
|
|
3502
|
+
} else { replyErr(cmdId, "Unsupported", "sidePanel not available (engine: " + engineName() + ")"); }
|
|
3503
|
+
} else if (surface === "action") {
|
|
3504
|
+
// Trigger the toolbar action. With a default_popup, clicking the icon
|
|
3505
|
+
// opens it (reuse openPopup). Without a popup, clicking fires
|
|
3506
|
+
// chrome.action.onClicked — we replay the listeners captured at install.
|
|
3507
|
+
if (chrome.action) {
|
|
3508
|
+
getActionPopup(function (popup) {
|
|
3509
|
+
if (popup) {
|
|
3510
|
+
try {
|
|
3511
|
+
chrome.action.openPopup().then(function () { replyOk(cmdId, {triggered: "popup"}); }, function (e) { replyErr(cmdId, "Unsupported", "openPopup: " + e); });
|
|
3512
|
+
} catch (e) { replyErr(cmdId, "Unsupported", "openPopup: " + e); }
|
|
3513
|
+
} else if (actionClickedListeners.length) {
|
|
3514
|
+
resolveActiveTab(args, function (tab) {
|
|
3515
|
+
var fired = 0;
|
|
3516
|
+
for (var i = 0; i < actionClickedListeners.length; i++) {
|
|
3517
|
+
try { actionClickedListeners[i](tab); fired++; } catch (e) {}
|
|
3518
|
+
}
|
|
3519
|
+
var reply = {triggered: "onClicked", listeners: fired, gesture: false};
|
|
3520
|
+
var warning = activeTabWarning();
|
|
3521
|
+
if (warning) reply.warning = warning;
|
|
3522
|
+
replyOk(cmdId, reply);
|
|
3523
|
+
});
|
|
3524
|
+
} else {
|
|
3525
|
+
replyErr(cmdId, "Unsupported", "action has no popup and no onClicked listener registered");
|
|
3526
|
+
}
|
|
3527
|
+
});
|
|
3528
|
+
} else { replyErr(cmdId, "Unsupported", "chrome.action not available (engine: " + engineName() + ")"); }
|
|
3529
|
+
} else if (surface === "command") {
|
|
3530
|
+
// Replay a captured chrome.commands.onCommand listener (keyboard
|
|
3531
|
+
// shortcut). Same no-gesture caveat as onClicked.
|
|
3532
|
+
var commandName = (args && args.name) || undefined;
|
|
3533
|
+
if (!commandListeners.length) {
|
|
3534
|
+
replyErr(cmdId, "Unsupported", "no chrome.commands.onCommand listener registered");
|
|
3535
|
+
} else {
|
|
3536
|
+
resolveActiveTab(args, function (tab) {
|
|
3537
|
+
var fired = 0;
|
|
3538
|
+
for (var i = 0; i < commandListeners.length; i++) {
|
|
3539
|
+
try { commandListeners[i](commandName, tab); fired++; } catch (e) {}
|
|
3540
|
+
}
|
|
3541
|
+
replyOk(cmdId, {triggered: "command", command: commandName || null, listeners: fired, gesture: false});
|
|
3542
|
+
});
|
|
3543
|
+
}
|
|
3544
|
+
} else { replyErr(cmdId, "BadRequest", "unknown surface: " + surface); }
|
|
3545
|
+
return;
|
|
3546
|
+
}
|
|
3547
|
+
if (op === "tabs.query") {
|
|
3548
|
+
chrome.tabs.query(args || {}).then(function (tabs) {
|
|
3549
|
+
replyOk(cmdId, (tabs || []).map(function (t) { return {id: t.id, url: t.url, title: t.title, active: t.active, windowId: t.windowId}; }));
|
|
3550
|
+
}, function (e) { replyErr(cmdId, "TabsError", e); });
|
|
3551
|
+
return;
|
|
3552
|
+
}
|
|
3553
|
+
if (op === "inspect") {
|
|
3554
|
+
// Slice 3 sidecar/default path: extract a DOM snapshot from the target
|
|
3555
|
+
// page via chrome.scripting (CDP-free). Closed shadow roots need the
|
|
3556
|
+
// --deep-dom CDP escape hatch (separate tool); here we read open ones.
|
|
3557
|
+
if ((ctx === "content" || ctx === "page") && target.tabId && chrome.scripting) {
|
|
3558
|
+
var maxBytes = (args && args.maxBytes) || 262144;
|
|
3559
|
+
var includeHtml = !args || !args.include || args.include.indexOf("html") !== -1;
|
|
3560
|
+
chrome.scripting.executeScript({
|
|
3561
|
+
target: {tabId: target.tabId},
|
|
3562
|
+
world: ctx === "page" ? "MAIN" : "ISOLATED",
|
|
3563
|
+
func: function (wantHtml, cap) {
|
|
3564
|
+
function countShadow(root) {
|
|
3565
|
+
var n = 0, els = root.querySelectorAll("*");
|
|
3566
|
+
for (var i = 0; i < els.length; i++) { if (els[i].shadowRoot) { n++; n += countShadow(els[i].shadowRoot); } }
|
|
3567
|
+
return n;
|
|
3568
|
+
}
|
|
3569
|
+
var doc = document;
|
|
3570
|
+
var roots = doc.querySelectorAll('#extension-root,[data-extension-root]');
|
|
3571
|
+
var out = {
|
|
3572
|
+
url: location.href,
|
|
3573
|
+
title: doc.title,
|
|
3574
|
+
summary: {
|
|
3575
|
+
htmlLength: doc.documentElement.outerHTML.length,
|
|
3576
|
+
scriptCount: doc.querySelectorAll("script").length,
|
|
3577
|
+
styleCount: doc.querySelectorAll("style,link[rel=stylesheet]").length,
|
|
3578
|
+
extensionRootCount: roots.length,
|
|
3579
|
+
openShadowRoots: countShadow(doc),
|
|
3580
|
+
bodyChildCount: doc.body ? doc.body.children.length : 0
|
|
3581
|
+
}
|
|
3582
|
+
};
|
|
3583
|
+
if (wantHtml) {
|
|
3584
|
+
var html = doc.documentElement.outerHTML;
|
|
3585
|
+
if (cap > 0 && html.length > cap) { out.html = html.slice(0, cap); out.htmlTruncated = true; }
|
|
3586
|
+
else { out.html = html; }
|
|
3587
|
+
}
|
|
3588
|
+
return out;
|
|
3589
|
+
},
|
|
3590
|
+
args: [includeHtml, maxBytes]
|
|
3591
|
+
}).then(function (res) { replyOk(cmdId, res && res[0] ? res[0].result : undefined); },
|
|
3592
|
+
function (e) { replyErr(cmdId, "InspectError", e); });
|
|
3593
|
+
} else if (ctx === "popup" || ctx === "options" || ctx === "sidebar" || ctx === "devtools") {
|
|
3594
|
+
// The SW can't read a surface page's DOM; ask the surface's own
|
|
3595
|
+
// in-bundle relay (only the open, matching-context page responds).
|
|
3596
|
+
chrome.runtime.sendMessage(
|
|
3597
|
+
{__extjsInspectRequest: true, target: target, args: args},
|
|
3598
|
+
function (resp) {
|
|
3599
|
+
if (chrome.runtime.lastError || !resp) {
|
|
3600
|
+
replyErr(cmdId, "Unsupported", "surface '" + ctx + "' is not open (open it first: extension open " + ctx + ")");
|
|
3601
|
+
} else if (resp.ok) {
|
|
3602
|
+
replyOk(cmdId, resp.value);
|
|
3603
|
+
} else {
|
|
3604
|
+
replyErr(cmdId, (resp.error && resp.error.name) || "InspectError", (resp.error && resp.error.message) || "inspect failed");
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
);
|
|
3608
|
+
} else if (ctx === "background") {
|
|
3609
|
+
replyErr(cmdId, "Unsupported", "the service worker has no DOM; inspect a content/page (with --tab) or an open surface");
|
|
3610
|
+
} else {
|
|
3611
|
+
replyErr(cmdId, "Unsupported", "inspect of " + ctx + " requires a tabId (content/page) or an open surface");
|
|
3612
|
+
}
|
|
3613
|
+
return;
|
|
3614
|
+
}
|
|
3615
|
+
replyErr(cmdId, "BadRequest", "unknown op: " + op);
|
|
3616
|
+
} catch (e) {
|
|
3617
|
+
replyErr(cmdId, "ExecutorError", e);
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
|
|
3621
|
+
function noopLastError() { try { void g.chrome.runtime.lastError; } catch (e) {} }
|
|
3622
|
+
|
|
3623
|
+
// Only http(s)/file/ftp tabs can run a content script; skip chrome://, the
|
|
3624
|
+
// extension's own pages, about:blank, etc.
|
|
3625
|
+
function isInjectableUrl(url) {
|
|
3626
|
+
return typeof url === "string" && /^(https?|file|ftp):/i.test(url);
|
|
3627
|
+
}
|
|
3628
|
+
|
|
3629
|
+
// Re-inject a single declared content-script entry's fresh files into every
|
|
3630
|
+
// open tab it matches, in place. The wrapper's reinject runtime tears down
|
|
3631
|
+
// the previous mount (matched by data-extjs-reinject-owner + build hash) and
|
|
3632
|
+
// mounts the new one — so this is the controller-less equivalent of the CDP
|
|
3633
|
+
// controller's reinjection, just driven from inside the extension.
|
|
3634
|
+
//
|
|
3635
|
+
// exclude_matches MUST be honored: the browser's own static injection never
|
|
3636
|
+
// touches excluded pages, and extensions can rely on that (e.g. a script
|
|
3637
|
+
// whose message handler opens the very page it excludes — injecting there
|
|
3638
|
+
// anyway creates an open-a-tab → inject → open-a-tab runaway loop). Chrome's
|
|
3639
|
+
// tabs.query has no exclude support, so query the exclude patterns
|
|
3640
|
+
// separately and subtract — the browser stays the single pattern matcher.
|
|
3641
|
+
function reinjectContentScriptEntry(entry) {
|
|
3642
|
+
var chrome = g.chrome;
|
|
3643
|
+
var matches = (entry && entry.matches) || [];
|
|
3644
|
+
if (!Array.isArray(matches) || !matches.length) return;
|
|
3645
|
+
var excludeMatches = (entry && entry.exclude_matches) || [];
|
|
3646
|
+
var jsFiles = ((entry && entry.js) || []).filter(function (f) { return typeof f === "string"; });
|
|
3647
|
+
var cssFiles = ((entry && entry.css) || []).filter(function (f) { return typeof f === "string"; });
|
|
3648
|
+
if (!jsFiles.length && !cssFiles.length) return;
|
|
3649
|
+
var world = entry.world === "MAIN" ? "MAIN" : "ISOLATED";
|
|
3650
|
+
var allFrames = !!entry.all_frames;
|
|
3651
|
+
function injectInto(excludedTabIds) {
|
|
3652
|
+
try {
|
|
3653
|
+
chrome.tabs.query({url: matches}, function (tabs) {
|
|
3654
|
+
var err = null;
|
|
3655
|
+
try { err = chrome.runtime.lastError; } catch (e) {}
|
|
3656
|
+
if (err || !tabs) return;
|
|
3657
|
+
for (var i = 0; i < tabs.length; i++) {
|
|
3658
|
+
(function (tab) {
|
|
3659
|
+
if (!tab || tab.id == null || !isInjectableUrl(tab.url)) return;
|
|
3660
|
+
if (excludedTabIds[tab.id]) return;
|
|
3661
|
+
var target = {tabId: tab.id, allFrames: allFrames};
|
|
3662
|
+
if (cssFiles.length && chrome.scripting.insertCSS) {
|
|
3663
|
+
try { chrome.scripting.insertCSS({target: target, files: cssFiles}, noopLastError); } catch (e) {}
|
|
3664
|
+
}
|
|
3665
|
+
if (jsFiles.length && chrome.scripting.executeScript) {
|
|
3666
|
+
try {
|
|
3667
|
+
chrome.scripting.executeScript(
|
|
3668
|
+
{target: target, files: jsFiles, world: world, injectImmediately: true},
|
|
3669
|
+
noopLastError
|
|
3670
|
+
);
|
|
3671
|
+
} catch (e) {}
|
|
3672
|
+
}
|
|
3673
|
+
})(tabs[i]);
|
|
3674
|
+
}
|
|
3675
|
+
});
|
|
3676
|
+
} catch (e) {}
|
|
3677
|
+
}
|
|
3678
|
+
if (Array.isArray(excludeMatches) && excludeMatches.length) {
|
|
3679
|
+
try {
|
|
3680
|
+
chrome.tabs.query({url: excludeMatches}, function (excludedTabs) {
|
|
3681
|
+
try { void chrome.runtime.lastError; } catch (e) {}
|
|
3682
|
+
var excluded = {};
|
|
3683
|
+
if (excludedTabs) {
|
|
3684
|
+
for (var i = 0; i < excludedTabs.length; i++) {
|
|
3685
|
+
if (excludedTabs[i] && excludedTabs[i].id != null) excluded[excludedTabs[i].id] = true;
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
3688
|
+
injectInto(excluded);
|
|
3689
|
+
});
|
|
3690
|
+
return;
|
|
3691
|
+
} catch (e) {}
|
|
3692
|
+
}
|
|
3693
|
+
injectInto({});
|
|
3694
|
+
}
|
|
3695
|
+
|
|
3696
|
+
// Re-inject all content scripts into their open tabs. Reads the manifest
|
|
3697
|
+
// FROM DISK (not chrome.runtime.getManifest(), which is frozen at extension-
|
|
3698
|
+
// registration time) because dev content-script filenames are content-hashed
|
|
3699
|
+
// and change on every edit — disk has the new js/css paths.
|
|
3700
|
+
function reinjectContentScripts(onDone) {
|
|
3701
|
+
var chrome = g.chrome;
|
|
3702
|
+
try {
|
|
3703
|
+
if (typeof g.fetch !== "function" || !chrome.scripting) return false;
|
|
3704
|
+
g.fetch(chrome.runtime.getURL("manifest.json"), {cache: "no-store"})
|
|
3705
|
+
.then(function (r) { return r.json(); })
|
|
3706
|
+
.then(function (manifest) {
|
|
3707
|
+
var entries = (manifest && manifest.content_scripts) || [];
|
|
3708
|
+
for (var i = 0; i < entries.length; i++) reinjectContentScriptEntry(entries[i]);
|
|
3709
|
+
reregisterForFutureNavigations(entries);
|
|
3710
|
+
if (onDone) { try { onDone(); } catch (e) {} }
|
|
3711
|
+
})
|
|
3712
|
+
.catch(function () {});
|
|
3713
|
+
return true;
|
|
3714
|
+
} catch (e) { return false; }
|
|
3715
|
+
}
|
|
3716
|
+
|
|
3717
|
+
function mapRunAt(runAt) {
|
|
3718
|
+
if (runAt === "document_start") return "document_start";
|
|
3719
|
+
if (runAt === "document_end") return "document_end";
|
|
3720
|
+
return "document_idle";
|
|
3721
|
+
}
|
|
3722
|
+
|
|
3723
|
+
// Re-register the content scripts dynamically (chrome.scripting) pointing at
|
|
3724
|
+
// the fresh files, so tabs opened AFTER an edit also get the new build. The
|
|
3725
|
+
// static manifest registration still points at the old content-hashed file
|
|
3726
|
+
// (it only updates on an extension reload) and keeps firing on new
|
|
3727
|
+
// navigations; the dynamic registration injects the new file alongside it,
|
|
3728
|
+
// and the wrapper's reinject runtime converges the mount to the newest build.
|
|
3729
|
+
// Idempotent: update an existing id, else register it.
|
|
3730
|
+
function reregisterForFutureNavigations(entries) {
|
|
3731
|
+
var chrome = g.chrome;
|
|
3732
|
+
if (
|
|
3733
|
+
!chrome.scripting ||
|
|
3734
|
+
!chrome.scripting.registerContentScripts ||
|
|
3735
|
+
!chrome.scripting.getRegisteredContentScripts
|
|
3736
|
+
) return;
|
|
3737
|
+
|
|
3738
|
+
var scripts = [];
|
|
3739
|
+
for (var i = 0; i < entries.length; i++) {
|
|
3740
|
+
var e = entries[i] || {};
|
|
3741
|
+
if (!Array.isArray(e.matches) || !e.matches.length) continue;
|
|
3742
|
+
var js = (e.js || []).filter(function (f) { return typeof f === "string"; });
|
|
3743
|
+
var css = (e.css || []).filter(function (f) { return typeof f === "string"; });
|
|
3744
|
+
if (!js.length && !css.length) continue;
|
|
3745
|
+
var s = {
|
|
3746
|
+
id: "extjs-dev-cs-" + i,
|
|
3747
|
+
matches: e.matches,
|
|
3748
|
+
runAt: mapRunAt(e.run_at),
|
|
3749
|
+
allFrames: !!e.all_frames,
|
|
3750
|
+
world: e.world === "MAIN" ? "MAIN" : "ISOLATED"
|
|
3751
|
+
};
|
|
3752
|
+
// Dev registration must not be BROADER than the static one: dropping
|
|
3753
|
+
// exclude_matches injects into pages the browser itself would skip.
|
|
3754
|
+
if (Array.isArray(e.exclude_matches) && e.exclude_matches.length) {
|
|
3755
|
+
s.excludeMatches = e.exclude_matches;
|
|
3756
|
+
}
|
|
3757
|
+
if (js.length) s.js = js;
|
|
3758
|
+
if (css.length) s.css = css;
|
|
3759
|
+
scripts.push(s);
|
|
3760
|
+
}
|
|
3761
|
+
if (!scripts.length) return;
|
|
3762
|
+
|
|
3763
|
+
try {
|
|
3764
|
+
chrome.scripting.getRegisteredContentScripts(function (existing) {
|
|
3765
|
+
try { void chrome.runtime.lastError; } catch (e) {}
|
|
3766
|
+
var have = {};
|
|
3767
|
+
if (existing) for (var k = 0; k < existing.length; k++) have[existing[k].id] = true;
|
|
3768
|
+
var toRegister = [], toUpdate = [];
|
|
3769
|
+
for (var j = 0; j < scripts.length; j++) {
|
|
3770
|
+
(have[scripts[j].id] ? toUpdate : toRegister).push(scripts[j]);
|
|
3771
|
+
}
|
|
3772
|
+
if (toRegister.length) {
|
|
3773
|
+
try { chrome.scripting.registerContentScripts(toRegister, noopLastError); } catch (e) {}
|
|
3774
|
+
}
|
|
3775
|
+
if (toUpdate.length) {
|
|
3776
|
+
try { chrome.scripting.updateContentScripts(toUpdate, noopLastError); } catch (e) {}
|
|
3777
|
+
}
|
|
3778
|
+
});
|
|
3779
|
+
} catch (e) {}
|
|
3780
|
+
}
|
|
3781
|
+
|
|
3782
|
+
// Perform a dev-loop reload without the CDP controller:
|
|
3783
|
+
// - content-scripts: re-inject the fresh script into open matching tabs in
|
|
3784
|
+
// place (no extension restart), the controller-less equivalent of CDP
|
|
3785
|
+
// reinjection. Needs the scripting permission + host access to the tab —
|
|
3786
|
+
// both injected in dev by ApplyDevDefaults. Falls back to a full reload
|
|
3787
|
+
// if scripting is unavailable.
|
|
3788
|
+
// - service-worker / full / manifest: restart the whole extension so it
|
|
3789
|
+
// re-registers from the fresh manifest.
|
|
3790
|
+
function performDevReload(type, onDone) {
|
|
3791
|
+
var chrome = g.chrome;
|
|
3792
|
+
if (!chrome) return;
|
|
3793
|
+
|
|
3794
|
+
var fullReload = function () {
|
|
3795
|
+
// Leave a pending-reinject flag for the NEXT producer generation:
|
|
3796
|
+
// Chrome never re-runs content scripts in tabs that were already open
|
|
3797
|
+
// when the extension reloads, and runtime.reload() does NOT fire
|
|
3798
|
+
// onInstalled (verified Chromium 146), so the boot-time heal below is
|
|
3799
|
+
// the only thing that converges open tabs after a SW/full reload —
|
|
3800
|
+
// without it a shared SW+content module edit restarts the SW while
|
|
3801
|
+
// every open tab keeps the stale content world (family F).
|
|
3802
|
+
try {
|
|
3803
|
+
if (chrome.storage && chrome.storage.local) {
|
|
3804
|
+
chrome.storage.local.set({__extjsDevPendingReinject: Date.now()}, noopLastError);
|
|
3805
|
+
}
|
|
3806
|
+
} catch (e) {}
|
|
3807
|
+
// Deferred so any in-flight result/log frame — and the "Reloading…"
|
|
3808
|
+
// console announcement dispatched into tabs — flushes before the SW
|
|
3809
|
+
// dies. runtime.reload() restarts the whole extension; the devtools
|
|
3810
|
+
// companion confirms completion via chrome.management events, so no
|
|
3811
|
+
// onDone here.
|
|
3812
|
+
try { setTimeout(function () { try { chrome.runtime.reload(); } catch (e) {} }, 150); } catch (e) {}
|
|
3813
|
+
};
|
|
3814
|
+
|
|
3815
|
+
if (type === "content-scripts" && chrome.scripting && chrome.tabs && chrome.tabs.query) {
|
|
3816
|
+
if (reinjectContentScripts(onDone)) return;
|
|
3817
|
+
}
|
|
3818
|
+
|
|
3819
|
+
fullReload();
|
|
3820
|
+
}
|
|
3821
|
+
|
|
3822
|
+
// ---- Dev-reload announcement surfaces --------------------------------
|
|
3823
|
+
// One server-built label travels the ReloadFrame; the producer echoes it
|
|
3824
|
+
// 1:1 into (a) the page's devtools console and (b) the bundled
|
|
3825
|
+
// extension-js-devtools companion (the bottom-left pill). Both fire HERE,
|
|
3826
|
+
// next to the actual reload action, so a surface can only say
|
|
3827
|
+
// "Reloading…" when a reload is actually being performed.
|
|
3828
|
+
|
|
3829
|
+
// Stable IDs of the bundled extension-js-devtools companion: Chromium pins
|
|
3830
|
+
// via the manifest "key"; Firefox via browser_specific_settings.gecko.id.
|
|
3831
|
+
// Absent companion (e.g. --no-browser in the user's own browser) is fine —
|
|
3832
|
+
// sendMessage just reports no receiver and we swallow it.
|
|
3833
|
+
var DEVTOOLS_COMPANION_ID_CHROMIUM = "kgdaecdpfkikjncaalnmmnjjfpofkcbl";
|
|
3834
|
+
var DEVTOOLS_COMPANION_ID_FIREFOX = "devtools@extension.js";
|
|
3835
|
+
|
|
3836
|
+
function notifyDevtoolsCompanion(phase, label, kind) {
|
|
3837
|
+
try {
|
|
3838
|
+
var chrome = g.chrome;
|
|
3839
|
+
if (!chrome || !chrome.runtime || typeof chrome.runtime.sendMessage !== "function") return;
|
|
3840
|
+
var id = engineName() === "firefox" ? DEVTOOLS_COMPANION_ID_FIREFOX : DEVTOOLS_COMPANION_ID_CHROMIUM;
|
|
3841
|
+
chrome.runtime.sendMessage(
|
|
3842
|
+
id,
|
|
3843
|
+
{type: "extjs-dev-reload-state", phase: phase, label: label || "", kind: kind || "", instanceId: INSTANCE_ID},
|
|
3844
|
+
function () { noopLastError(); }
|
|
3845
|
+
);
|
|
3846
|
+
} catch (e) {}
|
|
3847
|
+
}
|
|
3848
|
+
|
|
3849
|
+
// console.log the reload line into every open injectable tab. Runs in the
|
|
3850
|
+
// user extension's ISOLATED world, where the bridge relay patched console —
|
|
3851
|
+
// so the same line also lands in the centralized dev-server log stream.
|
|
3852
|
+
function announceReloadInTabs(text) {
|
|
3853
|
+
var chrome = g.chrome;
|
|
3854
|
+
if (!chrome || !chrome.tabs || !chrome.tabs.query) return;
|
|
3855
|
+
try {
|
|
3856
|
+
chrome.tabs.query({}, function (tabs) {
|
|
3857
|
+
noopLastError();
|
|
3858
|
+
if (!tabs) return;
|
|
3859
|
+
for (var i = 0; i < tabs.length; i++) {
|
|
3860
|
+
(function (tab) {
|
|
3861
|
+
if (!tab || tab.id == null || !isInjectableUrl(tab.url)) return;
|
|
3862
|
+
if (chrome.scripting && chrome.scripting.executeScript) {
|
|
3863
|
+
try {
|
|
3864
|
+
chrome.scripting.executeScript(
|
|
3865
|
+
{target: {tabId: tab.id}, func: function (t) { console.log(t); }, args: [text]},
|
|
3866
|
+
noopLastError
|
|
3867
|
+
);
|
|
3868
|
+
return;
|
|
3869
|
+
} catch (e) {}
|
|
3870
|
+
}
|
|
3871
|
+
// MV2 fallback (Firefox without the scripting API).
|
|
3872
|
+
if (chrome.tabs.executeScript) {
|
|
3873
|
+
try {
|
|
3874
|
+
chrome.tabs.executeScript(tab.id, {code: "console.log(" + JSON.stringify(text) + ");"}, noopLastError);
|
|
3875
|
+
} catch (e) {}
|
|
3876
|
+
}
|
|
3877
|
+
})(tabs[i]);
|
|
3878
|
+
}
|
|
3879
|
+
});
|
|
3880
|
+
} catch (e) {}
|
|
3881
|
+
}
|
|
3882
|
+
|
|
3883
|
+
function handleDevReloadFrame(frame) {
|
|
3884
|
+
var kind = frame.reloadType || "full";
|
|
3885
|
+
var label = typeof frame.label === "string" ? frame.label : "";
|
|
3886
|
+
// The server always builds the label; the kind-derived fallback only
|
|
3887
|
+
// covers a malformed frame so the announcement stays truthful.
|
|
3888
|
+
var fallback = kind === "content-scripts" ? "content_script"
|
|
3889
|
+
: kind === "service-worker" ? "service_worker"
|
|
3890
|
+
: "extension";
|
|
3891
|
+
var announced = "[extension.js] Reloading " + (label || fallback) + "…";
|
|
3892
|
+
|
|
3893
|
+
notifyDevtoolsCompanion("reloading", label, kind);
|
|
3894
|
+
|
|
3895
|
+
if (kind === "page") {
|
|
3896
|
+
// Notify-only: rspack-dev-server's livereload refreshes the open
|
|
3897
|
+
// surface; reloading the extension here would race it. No tab console
|
|
3898
|
+
// line either — the reloading page clears its own console anyway.
|
|
3899
|
+
return;
|
|
3900
|
+
}
|
|
3901
|
+
|
|
3902
|
+
// Delivery ack for the broker: proves this SW's message pump actually
|
|
3903
|
+
// processed the frame — a successful socket WRITE proves nothing when
|
|
3904
|
+
// the worker is wedged-but-connected (bug 27). The broker latches a
|
|
3905
|
+
// content-scripts reload until this ack and replays it to the next
|
|
3906
|
+
// producer hello, so an edit that landed on a dead pump still converges.
|
|
3907
|
+
// Sent on receipt (not after reinjection): the scripting-unavailable
|
|
3908
|
+
// fallback restarts the whole extension, and an unacked latch would
|
|
3909
|
+
// replay into the fresh SW and restart it a second time.
|
|
3910
|
+
send({type: "reload-ack", reloadType: kind, label: label});
|
|
3911
|
+
|
|
3912
|
+
announceReloadInTabs(announced);
|
|
3913
|
+
performDevReload(kind, function () {
|
|
3914
|
+
// Only the content-scripts path confirms from here (reinjection ran to
|
|
3915
|
+
// completion in this same SW). Full/SW reloads are confirmed by the
|
|
3916
|
+
// devtools companion's chrome.management listeners.
|
|
3917
|
+
notifyDevtoolsCompanion("reloaded", label, kind);
|
|
3918
|
+
});
|
|
3919
|
+
}
|
|
3920
|
+
|
|
3921
|
+
function sanitize(args) {
|
|
3922
|
+
var out = [];
|
|
3923
|
+
for (var i = 0; i < args.length; i++) {
|
|
3924
|
+
var p = args[i];
|
|
3925
|
+
try {
|
|
3926
|
+
if (typeof p === "string") out.push(p.length > 2048 ? p.slice(0, 2048) + "..." : p);
|
|
3927
|
+
else if (p instanceof Error) out.push(p.name + ": " + p.message);
|
|
3928
|
+
else out.push(JSON.stringify(p));
|
|
3929
|
+
} catch (e) {
|
|
3930
|
+
out.push(String(p));
|
|
3931
|
+
}
|
|
3932
|
+
if (out.join(" ").length > 8192) break;
|
|
3933
|
+
}
|
|
3934
|
+
return out;
|
|
3935
|
+
}
|
|
3936
|
+
|
|
3937
|
+
function flush() {
|
|
3938
|
+
while (queue.length && open && socket) {
|
|
3939
|
+
var f = queue.shift();
|
|
3940
|
+
try { socket.send(JSON.stringify(f)); } catch (e) { break; }
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
|
|
3944
|
+
function send(frame) {
|
|
3945
|
+
if (open && socket) {
|
|
3946
|
+
try { socket.send(JSON.stringify(frame)); return; } catch (e) {}
|
|
3947
|
+
}
|
|
3948
|
+
if (queue.length < MAX_QUEUE) queue.push(frame);
|
|
3949
|
+
}
|
|
3950
|
+
|
|
3951
|
+
function schedule() {
|
|
3952
|
+
var delay = backoff;
|
|
3953
|
+
backoff = Math.min(backoff * 2, MAX_BACKOFF);
|
|
3954
|
+
try { setTimeout(connect, delay); } catch (e) {}
|
|
3955
|
+
}
|
|
3956
|
+
|
|
3957
|
+
function connect() {
|
|
3958
|
+
try {
|
|
3959
|
+
socket = new WS("ws://" + HOST + ":" + PORT + "/extjs-control");
|
|
3960
|
+
} catch (e) {
|
|
3961
|
+
schedule();
|
|
3962
|
+
return;
|
|
3963
|
+
}
|
|
3964
|
+
socket.onopen = function () {
|
|
3965
|
+
open = true;
|
|
3966
|
+
backoff = 250;
|
|
3967
|
+
try {
|
|
3968
|
+
socket.send(JSON.stringify({type: "hello", v: 1, role: "producer", instanceId: INSTANCE_ID}));
|
|
3969
|
+
} catch (e) {}
|
|
3970
|
+
flush();
|
|
3971
|
+
};
|
|
3972
|
+
socket.onmessage = function (ev) {
|
|
3973
|
+
// Slice 2: the broker routes authorized command frames to this SW.
|
|
3974
|
+
var frame;
|
|
3975
|
+
try { frame = JSON.parse(typeof ev.data === "string" ? ev.data : ""); } catch (e) { return; }
|
|
3976
|
+
if (frame && frame.type === "command") {
|
|
3977
|
+
try { executeCommand(frame); } catch (e) { replyErr(frame.cmdId, "ExecutorError", e); }
|
|
3978
|
+
} else if (frame && frame.type === "reload") {
|
|
3979
|
+
// Dev-loop reload broadcast (see broker.broadcastReload).
|
|
3980
|
+
// Fire-and-forget: no result frame is expected.
|
|
3981
|
+
try { handleDevReloadFrame(frame); } catch (e) {}
|
|
3982
|
+
} else if (frame && frame.type === "ping") {
|
|
3983
|
+
// Server keepalive: merely RECEIVING this message resets the MV3
|
|
3984
|
+
// service worker's ~30s idle timer (Chrome 116+), keeping this SW
|
|
3985
|
+
// reachable for reload broadcasts. Nothing to do.
|
|
3986
|
+
}
|
|
3987
|
+
};
|
|
3988
|
+
socket.onclose = function () {
|
|
3989
|
+
open = false;
|
|
3990
|
+
socket = null;
|
|
3991
|
+
schedule();
|
|
3992
|
+
};
|
|
3993
|
+
socket.onerror = function () {
|
|
3994
|
+
try { socket && socket.close(); } catch (e) {}
|
|
3995
|
+
};
|
|
3996
|
+
}
|
|
3997
|
+
|
|
3998
|
+
LEVELS.forEach(function (level) {
|
|
3999
|
+
var orig = typeof consoleRef[level] === "function"
|
|
4000
|
+
? consoleRef[level].bind(consoleRef)
|
|
4001
|
+
: function () {};
|
|
4002
|
+
consoleRef[level] = function () {
|
|
4003
|
+
try {
|
|
4004
|
+
send({
|
|
4005
|
+
type: "log",
|
|
4006
|
+
event: {
|
|
4007
|
+
v: 1,
|
|
4008
|
+
id: nowId(),
|
|
4009
|
+
timestamp: Date.now(),
|
|
4010
|
+
level: level,
|
|
4011
|
+
context: CONTEXT,
|
|
4012
|
+
messageParts: sanitize([].slice.call(arguments)),
|
|
4013
|
+
runId: INSTANCE_ID
|
|
4014
|
+
}
|
|
4015
|
+
});
|
|
4016
|
+
} catch (e) {}
|
|
4017
|
+
return orig.apply(consoleRef, arguments);
|
|
4018
|
+
};
|
|
4019
|
+
});
|
|
4020
|
+
|
|
4021
|
+
// Multi-context console forwarding: other contexts (content scripts, surface
|
|
4022
|
+
// pages) can't reliably open ws://127.0.0.1 (page CSP / connect-src), so they
|
|
4023
|
+
// relay console to this SW, which owns the WS. The relay travels a NAMED
|
|
4024
|
+
// runtime.Port — never runtime.sendMessage — because port traffic is
|
|
4025
|
+
// invisible to the extension's own onMessage listeners. With sendMessage, an
|
|
4026
|
+
// extension whose SW echoes every message back to its tabs (wild newtube:
|
|
4027
|
+
// onMessage → tabs.sendMessage(msg) to all tabs) turned each relayed log
|
|
4028
|
+
// into a new tab message, whose subject-side console.log became another
|
|
4029
|
+
// relayed envelope — a self-sustaining dev-only message storm (25k rows/10min
|
|
4030
|
+
// observed) that starved the extension system and wedged Chromium's DevTools
|
|
4031
|
+
// endpoint (family B). We enrich with the real tabId/url from the port sender.
|
|
4032
|
+
function shipRelayedLog(ev, sender) {
|
|
4033
|
+
try {
|
|
4034
|
+
send({
|
|
4035
|
+
type: "log",
|
|
4036
|
+
event: {
|
|
4037
|
+
v: 1,
|
|
4038
|
+
id: nowId(),
|
|
4039
|
+
timestamp: Date.now(),
|
|
4040
|
+
level: ev.level || "log",
|
|
4041
|
+
context: ev.context || "content",
|
|
4042
|
+
messageParts: Array.isArray(ev.messageParts) ? ev.messageParts : [],
|
|
4043
|
+
url: ev.url || (sender ? sender.url : undefined),
|
|
4044
|
+
tabId: sender && sender.tab ? sender.tab.id : undefined,
|
|
4045
|
+
frameId: sender ? sender.frameId : undefined,
|
|
4046
|
+
runId: INSTANCE_ID
|
|
4047
|
+
}
|
|
4048
|
+
});
|
|
4049
|
+
} catch (e) {}
|
|
4050
|
+
}
|
|
4051
|
+
try {
|
|
4052
|
+
var rtPort = g.chrome;
|
|
4053
|
+
if (rtPort && rtPort.runtime && rtPort.runtime.onConnect) {
|
|
4054
|
+
rtPort.runtime.onConnect.addListener(function (port) {
|
|
4055
|
+
if (!port || port.name !== "__extjs-bridge-log__") return;
|
|
4056
|
+
try {
|
|
4057
|
+
port.onMessage.addListener(function (msg) {
|
|
4058
|
+
if (!msg || !msg.__extjsBridgeLog) return;
|
|
4059
|
+
shipRelayedLog(msg.__extjsBridgeLog, port.sender);
|
|
4060
|
+
});
|
|
4061
|
+
} catch (e) {}
|
|
4062
|
+
});
|
|
4063
|
+
}
|
|
4064
|
+
} catch (e) {}
|
|
4065
|
+
// Legacy sendMessage path: kept for any surface still relaying the old way
|
|
4066
|
+
// (harmless one-predicate listener; new relays never use it).
|
|
4067
|
+
try {
|
|
4068
|
+
var rt = g.chrome;
|
|
4069
|
+
if (rt && rt.runtime && rt.runtime.onMessage) {
|
|
4070
|
+
rt.runtime.onMessage.addListener(function (msg, sender) {
|
|
4071
|
+
if (!msg || !msg.__extjsBridgeLog) return;
|
|
4072
|
+
shipRelayedLog(msg.__extjsBridgeLog, sender);
|
|
4073
|
+
// No async response.
|
|
4074
|
+
});
|
|
4075
|
+
}
|
|
4076
|
+
} catch (e) {}
|
|
4077
|
+
|
|
4078
|
+
// Chrome never injects manifest content scripts into tabs that were
|
|
4079
|
+
// already open when the extension (re)loads, so after a dev full reload
|
|
4080
|
+
// (manifest edit, SW edit, chrome.runtime.reload) every open tab keeps the
|
|
4081
|
+
// PREVIOUS build's content-script DOM as a zombie: the nodes still render
|
|
4082
|
+
// but their listeners died with the old world, and nothing heals the tab
|
|
4083
|
+
// until the next recompile happens to reinject. Fire one producer-driven
|
|
4084
|
+
// reinject when this SW generation is a fresh install/reload so open tabs
|
|
4085
|
+
// converge to the new build immediately. onInstalled is the gate: it fires
|
|
4086
|
+
// for install/update/reload (fresh worlds) but NOT for idle-stop SW wakes,
|
|
4087
|
+
// where reinjecting would churn mounted UI for no reason.
|
|
4088
|
+
try {
|
|
4089
|
+
var rtBoot = g.chrome;
|
|
4090
|
+
if (rtBoot && rtBoot.runtime && rtBoot.runtime.onInstalled) {
|
|
4091
|
+
rtBoot.runtime.onInstalled.addListener(function () {
|
|
4092
|
+
try {
|
|
4093
|
+
setTimeout(function () {
|
|
4094
|
+
try { reinjectContentScripts(); } catch (e) {}
|
|
4095
|
+
}, 250);
|
|
4096
|
+
} catch (e) {}
|
|
4097
|
+
});
|
|
4098
|
+
}
|
|
4099
|
+
} catch (e) {}
|
|
4100
|
+
|
|
4101
|
+
// runtime.reload() does NOT fire onInstalled (verified Chromium 146), so
|
|
4102
|
+
// a dev-driven SW/full reload leaves the listener above silent and every
|
|
4103
|
+
// open tab keeps the previous build's content world. fullReload() stamps
|
|
4104
|
+
// __extjsDevPendingReinject into storage.local right before reloading;
|
|
4105
|
+
// consume it here (fresh flags only — a stale one from a crashed session
|
|
4106
|
+
// is dropped) and heal open tabs from the on-disk manifest. Idle-stop SW
|
|
4107
|
+
// wakes never see the flag, so this cannot churn mounted UI.
|
|
4108
|
+
try {
|
|
4109
|
+
var stBoot = g.chrome;
|
|
4110
|
+
if (stBoot && stBoot.storage && stBoot.storage.local) {
|
|
4111
|
+
stBoot.storage.local.get("__extjsDevPendingReinject", function (res) {
|
|
4112
|
+
noopLastError();
|
|
4113
|
+
var ts = res && res.__extjsDevPendingReinject;
|
|
4114
|
+
if (ts == null) return;
|
|
4115
|
+
try { stBoot.storage.local.remove("__extjsDevPendingReinject", noopLastError); } catch (e) {}
|
|
4116
|
+
if (typeof ts !== "number" || Date.now() - ts > 30000) return;
|
|
4117
|
+
setTimeout(function () {
|
|
4118
|
+
try { reinjectContentScripts(); } catch (e) {}
|
|
4119
|
+
}, 250);
|
|
4120
|
+
});
|
|
4121
|
+
}
|
|
4122
|
+
} catch (e) {}
|
|
4123
|
+
|
|
4124
|
+
connect();
|
|
4125
|
+
} catch (e) {}
|
|
4126
|
+
})();
|
|
4127
|
+
`;
|
|
4128
|
+
const BRIDGE_RELAY_SOURCE = `;(function () {
|
|
4129
|
+
try {
|
|
4130
|
+
var g = (typeof globalThis === "object" && globalThis) ? globalThis : this;
|
|
4131
|
+
if (!g || g.__extjsBridgeRelayInstalled) return;
|
|
4132
|
+
|
|
4133
|
+
var CONTEXT = "__EXTJS_CONTEXT__";
|
|
4134
|
+
var chrome = g.chrome;
|
|
4135
|
+
if (!chrome || !chrome.runtime) return;
|
|
4136
|
+
var canRelay = typeof chrome.runtime.connect === "function";
|
|
4137
|
+
|
|
4138
|
+
g.__extjsBridgeRelayInstalled = true;
|
|
4139
|
+
var consoleRef = g.console || {};
|
|
4140
|
+
var LEVELS = ["log", "info", "warn", "error", "debug", "trace"];
|
|
4141
|
+
|
|
4142
|
+
function sanitize(args) {
|
|
4143
|
+
var out = [];
|
|
4144
|
+
for (var i = 0; i < args.length; i++) {
|
|
4145
|
+
var p = args[i];
|
|
4146
|
+
try {
|
|
4147
|
+
if (typeof p === "string") out.push(p.length > 2048 ? p.slice(0, 2048) + "..." : p);
|
|
4148
|
+
else if (p instanceof Error) out.push(p.name + ": " + p.message);
|
|
4149
|
+
else out.push(JSON.stringify(p));
|
|
4150
|
+
} catch (e) { out.push(String(p)); }
|
|
4151
|
+
if (out.join(" ").length > 8192) break;
|
|
4152
|
+
}
|
|
4153
|
+
return out;
|
|
4154
|
+
}
|
|
4155
|
+
|
|
4156
|
+
function here() { try { return g.location ? g.location.href : undefined; } catch (e) { return undefined; } }
|
|
4157
|
+
|
|
4158
|
+
// Lazy named port to the SW producer. Connecting wakes an idle SW like
|
|
4159
|
+
// sendMessage does, but port frames never reach the extension's own
|
|
4160
|
+
// onMessage listeners (see the loop note above). Reconnect on demand: the
|
|
4161
|
+
// SW idling out (or reloading) disconnects the port; the next log redials.
|
|
4162
|
+
var logPort = null;
|
|
4163
|
+
function getLogPort() {
|
|
4164
|
+
if (!canRelay) return null;
|
|
4165
|
+
if (logPort) return logPort;
|
|
4166
|
+
try {
|
|
4167
|
+
logPort = chrome.runtime.connect({name: "__extjs-bridge-log__"});
|
|
4168
|
+
logPort.onDisconnect.addListener(function () {
|
|
4169
|
+
try { void chrome.runtime.lastError; } catch (e) {}
|
|
4170
|
+
logPort = null;
|
|
4171
|
+
});
|
|
4172
|
+
} catch (e) { logPort = null; }
|
|
4173
|
+
return logPort;
|
|
4174
|
+
}
|
|
4175
|
+
|
|
4176
|
+
LEVELS.forEach(function (level) {
|
|
4177
|
+
var orig = typeof consoleRef[level] === "function" ? consoleRef[level].bind(consoleRef) : function () {};
|
|
4178
|
+
consoleRef[level] = function () {
|
|
4179
|
+
try {
|
|
4180
|
+
var p = getLogPort();
|
|
4181
|
+
if (p) {
|
|
4182
|
+
try {
|
|
4183
|
+
p.postMessage({__extjsBridgeLog: {level: level, context: CONTEXT, messageParts: sanitize([].slice.call(arguments)), url: here()}});
|
|
4184
|
+
} catch (e) {
|
|
4185
|
+
// stale port (SW restarted): drop it and redial once
|
|
4186
|
+
logPort = null;
|
|
4187
|
+
var p2 = getLogPort();
|
|
4188
|
+
if (p2) { try { p2.postMessage({__extjsBridgeLog: {level: level, context: CONTEXT, messageParts: sanitize([].slice.call(arguments)), url: here()}}); } catch (e2) {} }
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
} catch (e) {}
|
|
4192
|
+
return orig.apply(consoleRef, arguments);
|
|
4193
|
+
};
|
|
1308
4194
|
});
|
|
4195
|
+
|
|
4196
|
+
// Surface DOM inspection: the SW executor can't reach popup/options/sidebar
|
|
4197
|
+
// DOM (separate extension pages), and the sidecar can't either (cross-extension
|
|
4198
|
+
// isolation). So THIS page — the user extension's own surface — answers an
|
|
4199
|
+
// inspect request for its own context. The SW broadcasts the request; only the
|
|
4200
|
+
// matching-context surface responds with its DOM snapshot.
|
|
4201
|
+
try {
|
|
4202
|
+
if (chrome.runtime && chrome.runtime.onMessage) {
|
|
4203
|
+
chrome.runtime.onMessage.addListener(function (msg, _sender, sendResponse) {
|
|
4204
|
+
if (!msg || !msg.__extjsInspectRequest) return;
|
|
4205
|
+
if (!msg.target || msg.target.context !== CONTEXT) return; // not for me
|
|
4206
|
+
try {
|
|
4207
|
+
var args = msg.args || {};
|
|
4208
|
+
var wantHtml = !args.include || args.include.indexOf("html") !== -1;
|
|
4209
|
+
var cap = args.maxBytes || 262144;
|
|
4210
|
+
function countShadow(root) {
|
|
4211
|
+
var n = 0, els = root.querySelectorAll("*");
|
|
4212
|
+
for (var i = 0; i < els.length; i++) { if (els[i].shadowRoot) { n++; n += countShadow(els[i].shadowRoot); } }
|
|
4213
|
+
return n;
|
|
4214
|
+
}
|
|
4215
|
+
var doc = g.document;
|
|
4216
|
+
var roots = doc.querySelectorAll('#extension-root,[data-extension-root]');
|
|
4217
|
+
var snap = {
|
|
4218
|
+
context: CONTEXT,
|
|
4219
|
+
url: here(),
|
|
4220
|
+
title: doc.title,
|
|
4221
|
+
summary: {
|
|
4222
|
+
htmlLength: doc.documentElement.outerHTML.length,
|
|
4223
|
+
scriptCount: doc.querySelectorAll("script").length,
|
|
4224
|
+
styleCount: doc.querySelectorAll("style,link[rel=stylesheet]").length,
|
|
4225
|
+
extensionRootCount: roots.length,
|
|
4226
|
+
openShadowRoots: countShadow(doc),
|
|
4227
|
+
bodyChildCount: doc.body ? doc.body.children.length : 0
|
|
4228
|
+
}
|
|
4229
|
+
};
|
|
4230
|
+
if (wantHtml) {
|
|
4231
|
+
var html = doc.documentElement.outerHTML;
|
|
4232
|
+
if (cap > 0 && html.length > cap) { snap.html = html.slice(0, cap); snap.htmlTruncated = true; }
|
|
4233
|
+
else { snap.html = html; }
|
|
4234
|
+
}
|
|
4235
|
+
sendResponse({ok: true, value: snap});
|
|
4236
|
+
} catch (e) {
|
|
4237
|
+
sendResponse({ok: false, error: {name: "InspectError", message: String(e)}});
|
|
4238
|
+
}
|
|
4239
|
+
return true; // responded
|
|
4240
|
+
});
|
|
4241
|
+
}
|
|
4242
|
+
} catch (e) {}
|
|
4243
|
+
} catch (e) {}
|
|
4244
|
+
})();
|
|
4245
|
+
`;
|
|
4246
|
+
function buildBridgeRelaySource(opts) {
|
|
4247
|
+
return BRIDGE_RELAY_SOURCE.replace(/__EXTJS_CONTEXT__/g, String(opts.context || 'content'));
|
|
1309
4248
|
}
|
|
1310
|
-
function
|
|
1311
|
-
|
|
4249
|
+
function buildBridgeProducerSource(opts) {
|
|
4250
|
+
if (!opts.controlPort || opts.controlPort < 1) return '';
|
|
4251
|
+
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'));
|
|
1312
4252
|
}
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
const
|
|
1318
|
-
|
|
4253
|
+
const inject_bridge_producer_BACKGROUND_ASSET = /(^|\/)background\/(?:service_worker|scripts?)\.js$/i;
|
|
4254
|
+
class InjectBridgeProducer {
|
|
4255
|
+
apply(compiler) {
|
|
4256
|
+
const controlPort = parseInt(String(process.env.EXTENSION_CONTROL_PORT || ''), 10);
|
|
4257
|
+
const instanceId = String(process.env.EXTENSION_INSTANCE_ID || '');
|
|
4258
|
+
const host = String(process.env.EXTENSION_DEV_SERVER_CONNECTABLE_HOST || '').trim() || '127.0.0.1';
|
|
4259
|
+
const source = buildBridgeProducerSource({
|
|
4260
|
+
controlPort: Number.isFinite(controlPort) ? controlPort : null,
|
|
4261
|
+
instanceId,
|
|
4262
|
+
context: 'background',
|
|
4263
|
+
host
|
|
4264
|
+
});
|
|
4265
|
+
if (!source) return;
|
|
4266
|
+
compiler.hooks.thisCompilation.tap(InjectBridgeProducer.name, (compilation)=>{
|
|
4267
|
+
compilation.hooks.processAssets.tap({
|
|
4268
|
+
name: InjectBridgeProducer.name,
|
|
4269
|
+
stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT + 101
|
|
4270
|
+
}, ()=>{
|
|
4271
|
+
for (const asset of compilation.getAssets()){
|
|
4272
|
+
if (!inject_bridge_producer_BACKGROUND_ASSET.test(asset.name)) continue;
|
|
4273
|
+
const original = asset.source.source().toString();
|
|
4274
|
+
if (-1 !== original.indexOf('__extjsBridgeProducerInstalled')) continue;
|
|
4275
|
+
const next = source + '\n' + original;
|
|
4276
|
+
compilation.updateAsset(asset.name, new core_sources.RawSource(next));
|
|
4277
|
+
}
|
|
4278
|
+
});
|
|
4279
|
+
});
|
|
1319
4280
|
}
|
|
1320
4281
|
}
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
4282
|
+
const RELAY_TARGETS = [
|
|
4283
|
+
{
|
|
4284
|
+
re: /(^|\/)content_scripts\/.+\.js$/i,
|
|
4285
|
+
context: 'content'
|
|
4286
|
+
},
|
|
4287
|
+
{
|
|
4288
|
+
re: /(^|\/)action\/index\.js$/i,
|
|
4289
|
+
context: 'popup'
|
|
4290
|
+
},
|
|
4291
|
+
{
|
|
4292
|
+
re: /(^|\/)options\/index\.js$/i,
|
|
4293
|
+
context: 'options'
|
|
4294
|
+
},
|
|
4295
|
+
{
|
|
4296
|
+
re: /(^|\/)sidebar\/index\.js$/i,
|
|
4297
|
+
context: 'sidebar'
|
|
4298
|
+
},
|
|
4299
|
+
{
|
|
4300
|
+
re: /(^|\/)devtools\/index\.js$/i,
|
|
4301
|
+
context: 'devtools'
|
|
4302
|
+
}
|
|
4303
|
+
];
|
|
4304
|
+
class InjectBridgeRelay {
|
|
4305
|
+
apply(compiler) {
|
|
4306
|
+
const controlPort = parseInt(String(process.env.EXTENSION_CONTROL_PORT || ''), 10);
|
|
4307
|
+
if (!Number.isFinite(controlPort) || controlPort < 1) return;
|
|
4308
|
+
const sourceFor = new Map();
|
|
4309
|
+
for (const t of RELAY_TARGETS)if (!sourceFor.has(t.context)) sourceFor.set(t.context, buildBridgeRelaySource({
|
|
4310
|
+
context: t.context
|
|
4311
|
+
}));
|
|
4312
|
+
compiler.hooks.thisCompilation.tap(InjectBridgeRelay.name, (compilation)=>{
|
|
4313
|
+
compilation.hooks.processAssets.tap({
|
|
4314
|
+
name: InjectBridgeRelay.name,
|
|
4315
|
+
stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT + 101
|
|
4316
|
+
}, ()=>{
|
|
4317
|
+
for (const asset of compilation.getAssets()){
|
|
4318
|
+
const target = RELAY_TARGETS.find((t)=>t.re.test(asset.name));
|
|
4319
|
+
if (!target) continue;
|
|
4320
|
+
const original = asset.source.source().toString();
|
|
4321
|
+
if (-1 !== original.indexOf('__extjsBridgeRelayInstalled')) continue;
|
|
4322
|
+
const next = sourceFor.get(target.context) + '\n' + original;
|
|
4323
|
+
compilation.updateAsset(asset.name, new core_sources.RawSource(next));
|
|
4324
|
+
}
|
|
4325
|
+
});
|
|
4326
|
+
});
|
|
4327
|
+
}
|
|
4328
|
+
}
|
|
4329
|
+
const HOT_DIR = 'hot';
|
|
4330
|
+
class PruneStaleHotUpdates {
|
|
4331
|
+
static name = 'plugin-reload:prune-stale-hot-updates';
|
|
4332
|
+
previousGeneration = new Set();
|
|
4333
|
+
apply(compiler) {
|
|
4334
|
+
if (!compiler?.hooks?.done?.tap) return;
|
|
4335
|
+
compiler.hooks.done.tap(PruneStaleHotUpdates.name, (stats)=>{
|
|
4336
|
+
try {
|
|
4337
|
+
const outputPath = compiler.options.output?.path;
|
|
4338
|
+
if (!outputPath) return;
|
|
4339
|
+
const hotDir = __rspack_external_path.join(outputPath, HOT_DIR);
|
|
4340
|
+
if (!__rspack_external_fs.existsSync(hotDir)) return;
|
|
4341
|
+
const emitted = new Set();
|
|
4342
|
+
for (const asset of stats.compilation.getAssets()){
|
|
4343
|
+
const name = String(asset.name || '').replace(/\\/g, '/');
|
|
4344
|
+
if (name.startsWith(`${HOT_DIR}/`)) emitted.add(name.slice(HOT_DIR.length + 1));
|
|
4345
|
+
}
|
|
4346
|
+
const keep = new Set([
|
|
4347
|
+
...emitted,
|
|
4348
|
+
...this.previousGeneration
|
|
4349
|
+
]);
|
|
4350
|
+
const pruneDir = (dir, relPrefix)=>{
|
|
4351
|
+
for (const entry of __rspack_external_fs.readdirSync(dir, {
|
|
4352
|
+
withFileTypes: true
|
|
4353
|
+
})){
|
|
4354
|
+
const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
|
|
4355
|
+
const absolute = __rspack_external_path.join(dir, entry.name);
|
|
4356
|
+
if (entry.isDirectory()) {
|
|
4357
|
+
pruneDir(absolute, rel);
|
|
4358
|
+
try {
|
|
4359
|
+
if (0 === __rspack_external_fs.readdirSync(absolute).length) __rspack_external_fs.rmdirSync(absolute);
|
|
4360
|
+
} catch {}
|
|
4361
|
+
continue;
|
|
4362
|
+
}
|
|
4363
|
+
if (!keep.has(rel)) try {
|
|
4364
|
+
__rspack_external_fs.rmSync(absolute, {
|
|
4365
|
+
force: true
|
|
4366
|
+
});
|
|
4367
|
+
} catch {}
|
|
4368
|
+
}
|
|
4369
|
+
};
|
|
4370
|
+
pruneDir(hotDir, '');
|
|
4371
|
+
this.previousGeneration = emitted;
|
|
4372
|
+
} catch {}
|
|
4373
|
+
});
|
|
4374
|
+
}
|
|
1344
4375
|
}
|
|
1345
4376
|
function formatReloadContextLabel(context, files) {
|
|
1346
4377
|
if (!files.length) return context;
|
|
@@ -1523,6 +4554,91 @@ function classifyReloadFromSources(opts) {
|
|
|
1523
4554
|
label: formatReloadContextLabel(pageContextFromSources(changedSources), changedSources)
|
|
1524
4555
|
};
|
|
1525
4556
|
}
|
|
4557
|
+
function readContentScriptCount(compilation, outputPath) {
|
|
4558
|
+
try {
|
|
4559
|
+
const asset = compilation.getAsset?.('manifest.json');
|
|
4560
|
+
if (asset?.source) {
|
|
4561
|
+
const manifest = JSON.parse(String(asset.source.source()));
|
|
4562
|
+
const list = manifest?.content_scripts;
|
|
4563
|
+
if (Array.isArray(list)) return list.length;
|
|
4564
|
+
}
|
|
4565
|
+
} catch {}
|
|
4566
|
+
try {
|
|
4567
|
+
const manifestPath = __rspack_external_path.join(outputPath, 'manifest.json');
|
|
4568
|
+
if (__rspack_external_fs.existsSync(manifestPath)) {
|
|
4569
|
+
const manifest = JSON.parse(stripBom(__rspack_external_fs.readFileSync(manifestPath, 'utf8')));
|
|
4570
|
+
const list = manifest?.content_scripts;
|
|
4571
|
+
if (Array.isArray(list)) return list.length;
|
|
4572
|
+
}
|
|
4573
|
+
} catch {}
|
|
4574
|
+
return 0;
|
|
4575
|
+
}
|
|
4576
|
+
function viaBroker(broker, instruction) {
|
|
4577
|
+
return broker.broadcastReload({
|
|
4578
|
+
type: instruction.type,
|
|
4579
|
+
changedContentScriptEntries: instruction.changedContentScriptEntries,
|
|
4580
|
+
label: instruction.label,
|
|
4581
|
+
changedFiles: instruction.changedAssets
|
|
4582
|
+
});
|
|
4583
|
+
}
|
|
4584
|
+
function formatReloadingLine(label) {
|
|
4585
|
+
return `Reloading ${pintor.brightBlue(label)}…`;
|
|
4586
|
+
}
|
|
4587
|
+
async function dispatchReload(instruction, executor) {
|
|
4588
|
+
if (!instruction) return;
|
|
4589
|
+
if ('true' === process.env.EXTENSION_NO_RELOAD) return;
|
|
4590
|
+
if (executor.broker) {
|
|
4591
|
+
const notified = viaBroker(executor.broker, instruction);
|
|
4592
|
+
if (notified > 0 && instruction.label) console.log(formatReloadingLine(instruction.label));
|
|
4593
|
+
}
|
|
4594
|
+
}
|
|
4595
|
+
function createChangedSourcesTracker(compiler) {
|
|
4596
|
+
let forcedFull = false;
|
|
4597
|
+
let changedSources = [];
|
|
4598
|
+
compiler.hooks.watchRun.tap('extjs-reload-changed-sources', ()=>{
|
|
4599
|
+
forcedFull = false;
|
|
4600
|
+
changedSources = [];
|
|
4601
|
+
const modifiedFiles = compiler.modifiedFiles;
|
|
4602
|
+
if (!modifiedFiles || 0 === modifiedFiles.size) return;
|
|
4603
|
+
const contextDir = compiler.options.context || '';
|
|
4604
|
+
for (const file of modifiedFiles){
|
|
4605
|
+
const normalized = __rspack_external_path.relative(contextDir, file).replace(/\\/g, '/');
|
|
4606
|
+
if (normalized) {
|
|
4607
|
+
changedSources.push(normalized);
|
|
4608
|
+
if (normalized.includes('manifest.json') || normalized.includes('_locales/')) forcedFull = true;
|
|
4609
|
+
}
|
|
4610
|
+
}
|
|
4611
|
+
});
|
|
4612
|
+
return {
|
|
4613
|
+
snapshot: ()=>({
|
|
4614
|
+
forcedFull,
|
|
4615
|
+
changedSources
|
|
4616
|
+
})
|
|
4617
|
+
};
|
|
4618
|
+
}
|
|
4619
|
+
class ReloadPlugin {
|
|
4620
|
+
static name = 'plugin-reload';
|
|
4621
|
+
manifestPath;
|
|
4622
|
+
browser;
|
|
4623
|
+
constructor(options){
|
|
4624
|
+
this.manifestPath = options.manifestPath;
|
|
4625
|
+
this.browser = options.browser || 'chrome';
|
|
4626
|
+
}
|
|
4627
|
+
apply(compiler) {
|
|
4628
|
+
if ('production' === compiler.options.mode || 'true' === process.env.EXTENSION_NO_RELOAD) return;
|
|
4629
|
+
const hasValidManifest = !!this.manifestPath && __rspack_external_fs.existsSync(this.manifestPath) && __rspack_external_fs.lstatSync(this.manifestPath).isFile();
|
|
4630
|
+
if (!hasValidManifest) return;
|
|
4631
|
+
new StripContentScriptDevServerRuntime().apply(compiler);
|
|
4632
|
+
new SetupReloadStrategy({
|
|
4633
|
+
manifestPath: this.manifestPath,
|
|
4634
|
+
browser: this.browser
|
|
4635
|
+
}).apply(compiler);
|
|
4636
|
+
new InjectScriptsReplayShim().apply(compiler);
|
|
4637
|
+
new InjectBridgeProducer().apply(compiler);
|
|
4638
|
+
new InjectBridgeRelay().apply(compiler);
|
|
4639
|
+
new PruneStaleHotUpdates().apply(compiler);
|
|
4640
|
+
}
|
|
4641
|
+
}
|
|
1526
4642
|
class BuildEmitter extends EventEmitter {
|
|
1527
4643
|
constructor(){
|
|
1528
4644
|
super();
|
|
@@ -1603,25 +4719,6 @@ class BrowsersPlugin {
|
|
|
1603
4719
|
});
|
|
1604
4720
|
}
|
|
1605
4721
|
}
|
|
1606
|
-
function readContentScriptCount(compilation, outputPath) {
|
|
1607
|
-
try {
|
|
1608
|
-
const asset = compilation.getAsset?.('manifest.json');
|
|
1609
|
-
if (asset?.source) {
|
|
1610
|
-
const manifest = JSON.parse(String(asset.source.source()));
|
|
1611
|
-
const list = manifest?.content_scripts;
|
|
1612
|
-
if (Array.isArray(list)) return list.length;
|
|
1613
|
-
}
|
|
1614
|
-
} catch {}
|
|
1615
|
-
try {
|
|
1616
|
-
const manifestPath = __rspack_external_path.join(outputPath, 'manifest.json');
|
|
1617
|
-
if (__rspack_external_fs.existsSync(manifestPath)) {
|
|
1618
|
-
const manifest = JSON.parse(stripBom(__rspack_external_fs.readFileSync(manifestPath, 'utf8')));
|
|
1619
|
-
const list = manifest?.content_scripts;
|
|
1620
|
-
if (Array.isArray(list)) return list.length;
|
|
1621
|
-
}
|
|
1622
|
-
} catch {}
|
|
1623
|
-
return 0;
|
|
1624
|
-
}
|
|
1625
4722
|
class SafariDevPlugin {
|
|
1626
4723
|
packager;
|
|
1627
4724
|
static name = 'safari-dev';
|
|
@@ -1781,4 +4878,4 @@ async function extensionDev(pathOrRemoteUrl, devOptions) {
|
|
|
1781
4878
|
process.exit(1);
|
|
1782
4879
|
}
|
|
1783
4880
|
}
|
|
1784
|
-
export { BuildEmitter, buildSourceFeatureIndex, classifyReloadFromSources, createChangedSourcesTracker, dispatchReload, ensureOptionalContractModuleLoaded, ensureTypeScriptConfig, extensionBuild, extensionDev, getUserTypeScriptConfigFile, hasDependency, isUsingCustomLoader, isUsingIntegration, isUsingTypeScript, jsFrameworksConfigsDetected, jsFrameworksHmrSummary, jsFrameworksIntegrationsEnabled, loadOptionalContractModuleWithoutInstall, optional_deps_resolver_ensureOptionalContractPackageResolved, readContentScriptCount, resolveDevelopDistFile, resolveDevelopInstallRoot, resolveOptionalContractPackageWithoutInstall };
|
|
4881
|
+
export { BuildEmitter, ReloadPlugin, buildCanonicalManifest, buildSourceFeatureIndex, classifyReloadFromSources, collectRootAbsoluteRefs, createChangedSourcesTracker, dispatchReload, ensureOptionalContractModuleLoaded, ensureTypeScriptConfig, extensionBuild, extensionDev, filterKeysForThisBrowser, getCurrentManifestContent, getManifestContent, getManifestOverrides, getUserTypeScriptConfigFile, hasDependency, isFromFilepathList, isUsingCustomLoader, isUsingIntegration, isUsingTypeScript, jsFrameworksConfigsDetected, jsFrameworksHmrSummary, jsFrameworksIntegrationsEnabled, loadOptionalContractModuleWithoutInstall, normalizeManifestOutputPath, optional_deps_resolver_ensureOptionalContractPackageResolved, readContentScriptCount, reportToCompilation, resolveDevelopDistFile, resolveDevelopInstallRoot, resolveOptionalContractPackageWithoutInstall, resolveRootAbsoluteRef, setCurrentManifestContent, setOriginalManifestContent, unixify };
|