From 927aca612edac43fd69ca01cb46520d64d7af7cb Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 29 Jan 2024 11:14:18 -0600 Subject: [PATCH] fix!: Move browser constants to `import.meta.env` (#380) BREAKING CHANGE: Renamed undocumented constants: - `__BROWSER__` → `import.meta.env.BROWSER` - `__COMMAND__` → `import.meta.env.COMMAND` - `__MANIFEST_VERSION__` → `import.meta.env.MANIFEST_VERSION` - `__IS_CHROME__` → `import.meta.env.CHROME` - `__IS_FIREFOX__` → `import.meta.env.FIREFOX` - `__IS_SAFARI__` → `import.meta.env.SAFARI` - `__IS_EDGE__` → `import.meta.env.EDGE` - `__IS_OPERA__` → `import.meta.env.OPERA` --- demo/src/entrypoints/background.ts | 8 +++--- docs/guide/multiple-browsers.md | 18 ++++++------- docs/guide/testing.md | 2 +- e2e/tests/typescript-project.test.ts | 25 ++++++++++-------- src/client/content-scripts/custom-events.ts | 8 +++--- src/client/content-scripts/ui/index.ts | 4 ++- .../vite/plugins/entrypointGroupGlobals.ts | 2 +- src/core/builders/vite/plugins/globals.ts | 2 +- src/core/utils/building/generate-wxt-dir.ts | 9 ++++--- src/core/utils/building/import-entrypoint.ts | 1 + src/core/utils/globals.ts | 26 +++++++------------ src/types/globals.d.ts | 11 +++++--- src/virtual/background-entrypoint.ts | 4 +-- ...ontent-script-isolated-world-entrypoint.ts | 4 +-- .../content-script-main-world-entrypoint.ts | 2 +- src/virtual/reload-html.ts | 2 +- src/virtual/unlisted-script-entrypoint.ts | 2 +- 17 files changed, 68 insertions(+), 62 deletions(-) diff --git a/demo/src/entrypoints/background.ts b/demo/src/entrypoints/background.ts index 9377bb1d..264a5b58 100644 --- a/demo/src/entrypoints/background.ts +++ b/demo/src/entrypoints/background.ts @@ -4,10 +4,10 @@ export default defineBackground(() => { console.log(browser.runtime.id); logId(); console.log({ - browser: __BROWSER__, - chrome: __IS_CHROME__, - firefox: __IS_FIREFOX__, - manifestVersion: __MANIFEST_VERSION__, + browser: import.meta.env.BROWSER, + chrome: import.meta.env.CHROME, + firefox: import.meta.env.FIREFOX, + manifestVersion: import.meta.env.MANIFEST_VERSION, messages, }); diff --git a/docs/guide/multiple-browsers.md b/docs/guide/multiple-browsers.md index 0ebbd005..1bdb43a8 100644 --- a/docs/guide/multiple-browsers.md +++ b/docs/guide/multiple-browsers.md @@ -115,19 +115,19 @@ Only `defineBackground` and `defineContentScript` support per-browser options ri To determine the browser or manifest version at runtime, you can use any of the below variables: -- `__BROWSER__`: A string, the target browser, usually equal to the `--browser` flag -- `__MANIFEST_VERSION__`: A number, either `2` or `3`, depending on the manifest version targetted -- `__IS_CHROME__`: A boolean equivalent to `__BROWSER__ === "chrome"` -- `__IS_FIREFOX__`: A boolean equivalent to `__BROWSER__ === "firefox"` -- `__IS_EDGE__`: A boolean equivalent to `__BROWSER__ === "edge"` -- `__IS_SAFARI__`: A boolean equivalent to `__BROWSER__ === "safari"` -- `__IS_OPERA__`: A boolean equivalent to `__BROWSER__ === "opera"` -- `__COMMAND__`: A string, `"serve"` when running `wxt` for development or `"build"` in all other cases. +- `import.meta.env.BROWSER`: A string, the target browser, usually equal to the `--browser` flag +- `import.meta.env.MANIFEST_VERSION`: A number, either `2` or `3`, depending on the manifest version targetted +- `import.meta.env.CHROME`: A boolean equivalent to `import.meta.env.BROWSER === "chrome"` +- `import.meta.env.FIREFOX`: A boolean equivalent to `import.meta.env.BROWSER === "firefox"` +- `import.meta.env.EDGE`: A boolean equivalent to `import.meta.env.BROWSER === "edge"` +- `import.meta.env.SAFARI`: A boolean equivalent to `import.meta.env.BROWSER === "safari"` +- `import.meta.env.OPERA`: A boolean equivalent to `import.meta.env.BROWSER === "opera"` +- `import.meta.env.COMMAND`: A string, `"serve"` when running `wxt` for development or `"build"` in all other cases. :::info These variables are constants defined at build time based on the build target. They do not actually detect which browser the code is running in. -For example, if you build for `--browser chrome` and publish it on Edge, `__BROWSER__` will be `"chrome"`, not `"edge"`. You have to build a separate ZIP for `--browser edge` before `__BROWSER__` will be `"edge"`. +For example, if you build for `--browser chrome` and publish it on Edge, `import.meta.env.BROWSER` will be `"chrome"`, not `"edge"`. You have to build a separate ZIP for `--browser edge` before `import.meta.env.BROWSER` will be `"edge"`. If you need to know the actual browser your code is being ran on, you should use a [user agent parser](https://www.npmjs.com/package/ua-parser-js). ::: diff --git a/docs/guide/testing.md b/docs/guide/testing.md index c516c622..535d3bfb 100644 --- a/docs/guide/testing.md +++ b/docs/guide/testing.md @@ -19,7 +19,7 @@ If you want to try to use a different framework for unit tests, you will need to - **Auto-imports**: Add `unimport` to your test environment or disable them by setting `imports: false` in your `wxt.config.ts` file - **`browser` mock**: Mock the `webextension-polyfill` module globally with `wxt/dist/virtual/mock-browser.js` - **[Remote Code Bundling](/guide/remote-code)**: If you use it, configure your environment to handle the `url:` module prefix -- **Global Variables**: If you consume them, manually define globals provided by WXT (like `__BROWSER__`) by adding them to the global scope before accessing them (`globalThis.__BROWSER__ = "chrome"`) +- **Global Variables**: If you consume them, manually define globals provided by WXT (like `import.meta.env.BROWSER`) by adding them to the global scope before accessing them (`import.meta.env.BROWSER = "chrome"`) - **Import paths**: If you use the `@/` or `~/` path aliases, add them to your test environment [Here's how Vitest is configured](https://github.com/wxt-dev/wxt/blob/main/src/testing/wxt-vitest-plugin.ts) for reference. diff --git a/e2e/tests/typescript-project.test.ts b/e2e/tests/typescript-project.test.ts index 0c2f51c0..ee256c04 100644 --- a/e2e/tests/typescript-project.test.ts +++ b/e2e/tests/typescript-project.test.ts @@ -14,16 +14,19 @@ describe('TypeScript Project', () => { ---------------------------------------- // Generated by wxt export {} - declare global { - const __MANIFEST_VERSION__: 2 | 3; - const __BROWSER__: string; - const __IS_CHROME__: boolean; - const __IS_FIREFOX__: boolean; - const __IS_SAFARI__: boolean; - const __IS_EDGE__: boolean; - const __IS_OPERA__: boolean; - const __COMMAND__: "build" | "serve"; - const __ENTRYPOINT__: string; + interface ImportMetaEnv { + readonly MANIFEST_VERSION: 2 | 3; + readonly BROWSER: string; + readonly CHROME: boolean; + readonly FIREFOX: boolean; + readonly SAFARI: boolean; + readonly EDGE: boolean; + readonly OPERA: boolean; + readonly COMMAND: "build" | "serve"; + readonly ENTRYPOINT: string; + } + interface ImportMeta { + readonly env: ImportMetaEnv } " `); @@ -133,7 +136,7 @@ describe('TypeScript Project', () => { options?: GetMessageOptions, ): string; /** - * + * No message description. * * "" */ diff --git a/src/client/content-scripts/custom-events.ts b/src/client/content-scripts/custom-events.ts index 4db3ee64..df3a9731 100644 --- a/src/client/content-scripts/custom-events.ts +++ b/src/client/content-scripts/custom-events.ts @@ -15,10 +15,12 @@ export class WxtLocationChangeEvent extends Event { * Returns an event name unique to the extension and content script that's running. */ export function getUniqueEventName(eventName: string): string { - // During the build process, __ENTRYPOINT__ is not defined when importing entrypoints to get their - // metadata. + // During the build process, import.meta.env is not defined when importing + // entrypoints to get their metadata. const entrypointName = - typeof __ENTRYPOINT__ === 'undefined' ? 'build' : __ENTRYPOINT__; + typeof import.meta.env === 'undefined' + ? 'build' + : import.meta.env.ENTRYPOINT; return `${browser.runtime.id}:${entrypointName}:${eventName}`; } diff --git a/src/client/content-scripts/ui/index.ts b/src/client/content-scripts/ui/index.ts index bd45e9d4..312f9fe2 100644 --- a/src/client/content-scripts/ui/index.ts +++ b/src/client/content-scripts/ui/index.ts @@ -236,7 +236,9 @@ function mountUi( * Load the CSS for the current entrypoint. */ async function loadCss(): Promise { - const url = browser.runtime.getURL(`/content-scripts/${__ENTRYPOINT__}.css`); + const url = browser.runtime.getURL( + `/content-scripts/${import.meta.env.ENTRYPOINT}.css`, + ); try { const res = await fetch(url); return await res.text(); diff --git a/src/core/builders/vite/plugins/entrypointGroupGlobals.ts b/src/core/builders/vite/plugins/entrypointGroupGlobals.ts index 84bec304..d3c3c03c 100644 --- a/src/core/builders/vite/plugins/entrypointGroupGlobals.ts +++ b/src/core/builders/vite/plugins/entrypointGroupGlobals.ts @@ -14,7 +14,7 @@ export function entrypointGroupGlobals( const define: vite.InlineConfig['define'] = {}; let name = Array.isArray(entrypointGroup) ? 'html' : entrypointGroup.name; for (const global of getEntrypointGlobals(name)) { - define[global.name] = JSON.stringify(global.value); + define[`import.meta.env.${global.name}`] = JSON.stringify(global.value); } return { define, diff --git a/src/core/builders/vite/plugins/globals.ts b/src/core/builders/vite/plugins/globals.ts index 5f3934bf..4a783d08 100644 --- a/src/core/builders/vite/plugins/globals.ts +++ b/src/core/builders/vite/plugins/globals.ts @@ -10,7 +10,7 @@ export function globals( config() { const define: vite.InlineConfig['define'] = {}; for (const global of getGlobals(config)) { - define[global.name] = JSON.stringify(global.value); + define[`import.meta.env.${global.name}`] = JSON.stringify(global.value); } return { define, diff --git a/src/core/utils/building/generate-wxt-dir.ts b/src/core/utils/building/generate-wxt-dir.ts index 4d334ce3..d9f9144d 100644 --- a/src/core/utils/building/generate-wxt-dir.ts +++ b/src/core/utils/building/generate-wxt-dir.ts @@ -136,7 +136,7 @@ declare module "wxt/browser" { const overrides = messages.map((message) => { return ` /** - * ${message.description ?? 'No message description.'} + * ${message.description || 'No message description.'} * * "${message.message}" */ @@ -164,8 +164,11 @@ async function writeGlobalsDeclarationFile( [ '// Generated by wxt', 'export {}', - 'declare global {', - ...globals.map((global) => ` const ${global.name}: ${global.type};`), + 'interface ImportMetaEnv {', + ...globals.map((global) => ` readonly ${global.name}: ${global.type};`), + '}', + 'interface ImportMeta {', + ' readonly env: ImportMetaEnv', '}', ].join('\n') + '\n', ); diff --git a/src/core/utils/building/import-entrypoint.ts b/src/core/utils/building/import-entrypoint.ts index b1dc090b..8a3db9bd 100644 --- a/src/core/utils/building/import-entrypoint.ts +++ b/src/core/utils/building/import-entrypoint.ts @@ -98,6 +98,7 @@ export async function importEntrypointFile( { cause: err }, ); } else { + config.logger.error(err); throw Error(`Failed to load entrypoint: ${filePath}`, { cause: err }); } } diff --git a/src/core/utils/globals.ts b/src/core/utils/globals.ts index 219c83db..5d0a3838 100644 --- a/src/core/utils/globals.ts +++ b/src/core/utils/globals.ts @@ -5,42 +5,42 @@ export function getGlobals( ): Array<{ name: string; value: any; type: string }> { return [ { - name: surroundInUnderscore('MANIFEST_VERSION'), + name: 'MANIFEST_VERSION', value: config.manifestVersion, type: `2 | 3`, }, { - name: surroundInUnderscore('BROWSER'), + name: 'BROWSER', value: config.browser, type: `string`, }, { - name: surroundInUnderscore('IS_CHROME'), + name: 'CHROME', value: config.browser === 'chrome', type: `boolean`, }, { - name: surroundInUnderscore('IS_FIREFOX'), + name: 'FIREFOX', value: config.browser === 'firefox', type: `boolean`, }, { - name: surroundInUnderscore('IS_SAFARI'), + name: 'SAFARI', value: config.browser === 'safari', type: `boolean`, }, { - name: surroundInUnderscore('IS_EDGE'), + name: 'EDGE', value: config.browser === 'edge', type: `boolean`, }, { - name: surroundInUnderscore('IS_OPERA'), + name: 'OPERA', value: config.browser === 'opera', type: `boolean`, }, { - name: surroundInUnderscore('COMMAND'), + name: 'COMMAND', value: config.command, type: `"build" | "serve"`, }, @@ -50,17 +50,9 @@ export function getGlobals( export function getEntrypointGlobals(entrypointName: string) { return [ { - name: surroundInUnderscore('ENTRYPOINT'), + name: 'ENTRYPOINT', value: entrypointName, type: `string`, }, ]; } - -/** - * Don't hardcode the complete name so that the string litterals in this file aren't replaced during - * tests (which causes syntax errors), only during builds. - */ -function surroundInUnderscore(name: string): string { - return `__${name}__`; -} diff --git a/src/types/globals.d.ts b/src/types/globals.d.ts index cfa8f0d9..5cf7be25 100644 --- a/src/types/globals.d.ts +++ b/src/types/globals.d.ts @@ -1,7 +1,10 @@ -// Globals defined by the vite-plugins/devServerGlobals.ts and utils/globals.ts -declare const __COMMAND__: 'build' | 'serve'; declare const __DEV_SERVER_PROTOCOL__: string; declare const __DEV_SERVER_HOSTNAME__: string; declare const __DEV_SERVER_PORT__: string; -declare const __MANIFEST_VERSION__: 2 | 3; -declare const __ENTRYPOINT__: string; + +// Globals defined by the vite-plugins/devServerGlobals.ts and utils/globals.ts +interface ImportMetaEnv { + readonly COMMAND: 'build' | 'serve'; + readonly MANIFEST_VERSION: 2 | 3; + readonly ENTRYPOINT: string; +} diff --git a/src/virtual/background-entrypoint.ts b/src/virtual/background-entrypoint.ts index eb448a22..24e2e54d 100644 --- a/src/virtual/background-entrypoint.ts +++ b/src/virtual/background-entrypoint.ts @@ -5,7 +5,7 @@ import { browser } from 'wxt/browser'; import { keepServiceWorkerAlive } from './utils/keep-service-worker-alive'; import { reloadContentScript } from './utils/reload-content-scripts'; -if (__COMMAND__ === 'serve') { +if (import.meta.env.COMMAND === 'serve') { try { const ws = setupWebSocket((message) => { if (message.event === 'wxt:reload-extension') browser.runtime.reload(); @@ -13,7 +13,7 @@ if (__COMMAND__ === 'serve') { reloadContentScript(message.data); }); - if (__MANIFEST_VERSION__ === 3) { + if (import.meta.env.MANIFEST_VERSION === 3) { // Tell the server the background script is loaded and ready to go ws.addEventListener('open', () => { const msg = { type: 'custom', event: 'wxt:background-initialized' }; diff --git a/src/virtual/content-script-isolated-world-entrypoint.ts b/src/virtual/content-script-isolated-world-entrypoint.ts index 429238da..e8a0ebb9 100644 --- a/src/virtual/content-script-isolated-world-entrypoint.ts +++ b/src/virtual/content-script-isolated-world-entrypoint.ts @@ -5,12 +5,12 @@ import { ContentScriptContext } from 'wxt/client'; (async () => { try { const { main, ...options } = definition; - const ctx = new ContentScriptContext(__ENTRYPOINT__, options); + const ctx = new ContentScriptContext(import.meta.env.ENTRYPOINT, options); await main(ctx); } catch (err) { logger.error( - `The content script "${__ENTRYPOINT__}" crashed on startup!`, + `The content script "${import.meta.env.ENTRYPOINT}" crashed on startup!`, err, ); } diff --git a/src/virtual/content-script-main-world-entrypoint.ts b/src/virtual/content-script-main-world-entrypoint.ts index 43fc689b..ad1248d7 100644 --- a/src/virtual/content-script-main-world-entrypoint.ts +++ b/src/virtual/content-script-main-world-entrypoint.ts @@ -7,7 +7,7 @@ import { logger } from '../sandbox/utils/logger'; await main(); } catch (err) { logger.error( - `The content script "${__ENTRYPOINT__}" crashed on startup!`, + `The content script "${import.meta.env.ENTRYPOINT}" crashed on startup!`, err, ); } diff --git a/src/virtual/reload-html.ts b/src/virtual/reload-html.ts index f2a57a17..3fd15697 100644 --- a/src/virtual/reload-html.ts +++ b/src/virtual/reload-html.ts @@ -1,7 +1,7 @@ import { logger } from '../sandbox/utils/logger'; import { setupWebSocket } from './utils/setup-web-socket'; -if (__COMMAND__ === 'serve') { +if (import.meta.env.COMMAND === 'serve') { try { setupWebSocket((message) => { if (message.event === 'wxt:reload-page') { diff --git a/src/virtual/unlisted-script-entrypoint.ts b/src/virtual/unlisted-script-entrypoint.ts index f7c1bead..9665ee62 100644 --- a/src/virtual/unlisted-script-entrypoint.ts +++ b/src/virtual/unlisted-script-entrypoint.ts @@ -6,7 +6,7 @@ import { logger } from '../sandbox/utils/logger'; await definition.main(); } catch (err) { logger.error( - `The unlisted script "${__ENTRYPOINT__}" crashed on startup!`, + `The unlisted script "${import.meta.env.ENTRYPOINT}" crashed on startup!`, err, ); }