diff --git a/demo/src/entrypoints/background.ts b/demo/src/entrypoints/background.ts index 160c4209..8b6ee0ee 100644 --- a/demo/src/entrypoints/background.ts +++ b/demo/src/entrypoints/background.ts @@ -7,4 +7,9 @@ export default defineBackground(() => { firefox: __IS_FIREFOX__, manifestVersion: __MANIFEST_VERSION__, }); + + // @ts-expect-error: should only accept entrypoints or public assets + browser.runtime.getURL('/'); + browser.runtime.getURL('/background.js'); + browser.runtime.getURL('/icon/128.png'); }); diff --git a/docs/guide/auto-imports.md b/docs/guide/auto-imports.md index 53ca9960..afb63e10 100644 --- a/docs/guide/auto-imports.md +++ b/docs/guide/auto-imports.md @@ -10,7 +10,7 @@ To setup your test environment for auto-imports, see [Testing](/get-started/test Some WXT APIs can be used without importing them: -- [`browser`](/config.md#browser) from `webextension-polyfill` +- [`browser`](/config.md#browser) from `wxt/browser`, a small wrapper around `webextension-polyfill` - [`defineContentScript`](/config.md#defiencontentscript) from `wxt/client` - [`defineBackground`](/config.md#definebackgroundscript) from `wxt/client` diff --git a/e2e/tests/auto-imports.test.ts b/e2e/tests/auto-imports.test.ts index 3aa895b3..6d9366f8 100644 --- a/e2e/tests/auto-imports.test.ts +++ b/e2e/tests/auto-imports.test.ts @@ -21,10 +21,17 @@ describe('Auto Imports', () => { ".wxt/types/paths.d.ts ---------------------------------------- // Generated by wxt - type EntrypointPath = - | \\"/background.js\\" - | \\"/content-scripts/overlay.js\\" - | \\"/popup.html\\" + import \\"wxt/browser\\"; + + declare module \\"wxt/browser\\" { + type PublicPath = + | \\"/background.js\\" + | \\"/content-scripts/overlay.js\\" + | \\"/popup.html\\" + export interface ProjectRuntime extends Runtime.Static { + getURL(path: PublicPath): string; + } + } " `); }); @@ -37,18 +44,18 @@ describe('Auto Imports', () => { expect(await project.serializeFile('.wxt/types/imports.d.ts')) .toMatchInlineSnapshot(` - ".wxt/types/imports.d.ts - ---------------------------------------- - // Generated by wxt - export {} - declare global { - const browser: typeof import('webextension-polyfill') - const defineBackground: typeof import('wxt/client')['defineBackground'] - const defineConfig: typeof import('wxt')['defineConfig'] - const defineContentScript: typeof import('wxt/client')['defineContentScript'] - const mountContentScriptUi: typeof import('wxt/client')['mountContentScriptUi'] - } - " - `); + ".wxt/types/imports.d.ts + ---------------------------------------- + // Generated by wxt + export {} + declare global { + const browser: typeof import('wxt/browser')['browser'] + const defineBackground: typeof import('wxt/client')['defineBackground'] + const defineConfig: typeof import('wxt')['defineConfig'] + const defineContentScript: typeof import('wxt/client')['defineContentScript'] + const mountContentScriptUi: typeof import('wxt/client')['mountContentScriptUi'] + } + " + `); }); }); diff --git a/package.json b/package.json index ffdf5b92..533f7f7d 100644 --- a/package.json +++ b/package.json @@ -39,9 +39,12 @@ "types": "./dist/index.d.ts" }, "./client": { - "require": "./dist/client.cjs", "import": "./dist/client.js", "types": "./dist/client.d.ts" + }, + "./browser": { + "import": "./dist/browser.js", + "types": "./dist/browser.d.ts" } }, "scripts": { diff --git a/scripts/build.ts b/scripts/build.ts index ecf21e5b..687e64cc 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -37,6 +37,13 @@ await Promise.all([ dts: true, silent: true, }), + tsup.build({ + entry: { browser: 'src/client/browser.ts' }, + format: ['esm'], + sourcemap: 'inline', + dts: true, + silent: true, + }), ...virtualEntrypoints.map((entryName) => tsup.build({ entry: { diff --git a/src/client/browser.ts b/src/client/browser.ts new file mode 100644 index 00000000..a42ef264 --- /dev/null +++ b/src/client/browser.ts @@ -0,0 +1,11 @@ +import originalBrowser, { Browser, Runtime } from 'webextension-polyfill'; + +export interface AugmentedBrowser extends Browser { + runtime: ProjectRuntime; +} + +export interface ProjectRuntime extends Runtime.Static { + // Overriden per-project +} + +export const browser: AugmentedBrowser = originalBrowser; diff --git a/src/core/build/buildEntrypoints.ts b/src/core/build/buildEntrypoints.ts index fbf4a5d2..c2c82022 100644 --- a/src/core/build/buildEntrypoints.ts +++ b/src/core/build/buildEntrypoints.ts @@ -9,9 +9,9 @@ import { import * as plugins from '../vite-plugins'; import { removeEmptyDirs } from '../utils/removeEmptyDirs'; import { getEntrypointBundlePath } from '../utils/entrypoints'; -import glob from 'fast-glob'; import fs from 'fs-extra'; import { dirname, resolve } from 'path'; +import { getPublicFiles } from '../utils/public'; export async function buildEntrypoints( groups: EntrypointGroup[], @@ -132,11 +132,10 @@ function getBuildOutputChunks( async function copyPublicDirectory( config: InternalConfig, ): Promise { + const files = await getPublicFiles(config); + if (files.length === 0) return []; + const publicAssets: BuildOutput['publicAssets'] = []; - if (!(await fs.exists(config.publicDir))) return publicAssets; - - const files = await glob('**/*', { cwd: config.publicDir }); - for (const file of files) { const srcPath = resolve(config.publicDir, file); const outPath = resolve(config.outDir, file); diff --git a/src/core/build/generateTypesDir.ts b/src/core/build/generateTypesDir.ts index 117e0def..4194057d 100644 --- a/src/core/build/generateTypesDir.ts +++ b/src/core/build/generateTypesDir.ts @@ -5,6 +5,7 @@ import { relative, resolve } from 'path'; import { getEntrypointBundlePath } from '../utils/entrypoints'; import { getUnimportOptions } from '../utils/auto-imports'; import { getGlobals } from '../utils/globals'; +import { getPublicFiles } from '../utils/public'; /** * Generate and write all the files inside the `InternalConfig.typesDir` directory. @@ -49,23 +50,33 @@ async function writePathsDeclarationFile( ): Promise { const filePath = resolve(config.typesDir, 'paths.d.ts'); const unions = entrypoints - .map((entry) => { - const path = getEntrypointBundlePath( + .map((entry) => + getEntrypointBundlePath( entry, config.outDir, entry.inputPath.endsWith('.html') ? '.html' : '.js', - ); - return ` | "/${path}"`; - }) - .sort(); + ), + ) + .concat(await getPublicFiles(config)) + .map((path) => ` | "/${path}"`) + .sort() + .join('\n'); + + const template = `// Generated by wxt +import "wxt/browser"; + +declare module "wxt/browser" { + type PublicPath = +{{ union }} + export interface ProjectRuntime extends Runtime.Static { + getURL(path: PublicPath): string; + } +} +`; await fs.writeFile( filePath, - [ - '// Generated by wxt', - 'type EntrypointPath =', - ...(unions.length === 0 ? [' never'] : unions), - ].join('\n') + '\n', + template.replace('{{ union }}', unions || ' | never'), ); return filePath; diff --git a/src/core/utils/auto-imports.ts b/src/core/utils/auto-imports.ts index 575782b1..45883ae6 100644 --- a/src/core/utils/auto-imports.ts +++ b/src/core/utils/auto-imports.ts @@ -7,11 +7,8 @@ export function getUnimportOptions( ): Partial { const defaultOptions: Partial = { debugLog: config.logger.debug, - imports: [ - { name: '*', as: 'browser', from: 'webextension-polyfill' }, - { name: 'defineConfig', from: 'wxt' }, - ], - presets: [{ package: 'wxt/client' }], + imports: [{ name: 'defineConfig', from: 'wxt' }], + presets: [{ package: 'wxt/client' }, { package: 'wxt/browser' }], warn: config.logger.warn, dirs: ['./components/*', './composables/*', './hooks/*', './utils/*'], }; diff --git a/src/core/utils/importTsFile.ts b/src/core/utils/importTsFile.ts index 54a5dd32..df150a35 100644 --- a/src/core/utils/importTsFile.ts +++ b/src/core/utils/importTsFile.ts @@ -31,7 +31,6 @@ export async function importTsFile( const unimport = createUnimport({ ...getUnimportOptions(config), // Only allow specific imports, not all from the project - imports: [{ name: '*', as: 'browser', from: 'webextension-polyfill' }], dirs: [], }); await unimport.init(); diff --git a/src/core/utils/public.ts b/src/core/utils/public.ts new file mode 100644 index 00000000..39a9fd85 --- /dev/null +++ b/src/core/utils/public.ts @@ -0,0 +1,15 @@ +import { InternalConfig } from '../types'; +import fs from 'fs-extra'; +import glob from 'fast-glob'; + +/** + * Get all the files in the project's public directory. Returned paths are relative to the + * `config.publicDir`. + */ +export async function getPublicFiles( + config: InternalConfig, +): Promise { + if (!(await fs.exists(config.publicDir))) return []; + + return await glob('**/*', { cwd: config.publicDir }); +}