From 4e4f99d33fe8836d019baf2274f33b12a3e1f01c Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 30 Dec 2024 11:30:57 -0600 Subject: [PATCH] feat!: Auto-import types (#1315) --- docs/guide/resources/upgrading.md | 16 +++ .../__snapshots__/auto-imports.test.ts.snap | 108 ++++++++++++++ packages/wxt/e2e/tests/auto-imports.test.ts | 58 ++++++-- packages/wxt/src/builtin-modules/unimport.ts | 2 +- packages/wxt/src/core/resolve-config.ts | 131 ++++++++++++++--- .../wxt/src/utils/content-script-ui/iframe.ts | 34 ++++- .../src/utils/content-script-ui/integrated.ts | 34 ++++- .../utils/content-script-ui/shadow-root.ts | 79 ++++++++++- .../wxt/src/utils/content-script-ui/types.ts | 132 ------------------ 9 files changed, 418 insertions(+), 176 deletions(-) diff --git a/docs/guide/resources/upgrading.md b/docs/guide/resources/upgrading.md index f8335e29..7862c212 100644 --- a/docs/guide/resources/upgrading.md +++ b/docs/guide/resources/upgrading.md @@ -171,6 +171,22 @@ export default defineConfig({ If you've loaded the extension into your browser manually for development, uninstall and re-install it from the new dev output directory. +### Internal Auto-import Options Changed + +Only relevant if you refer to WXT's built-in `preset`s in the `imports` config in a module or hooks. + +Instead of using `package` to auto-detect APIs to auto-import, WXT now uses `from` and `imports` to manually list APIs that are imported. + +```ts +presets: [ + { package: "wxt/browser" }, // [!code --] + { from: "wxt/browser": imports: ["browser"] }, // [!code --] + // ... +] +``` + +See [PR #1315 `packages/wxt/src/core/resolve-config.ts` changes](https://github.com/wxt-dev/wxt/pull/1315/files#diff-ff0465c3a486d3ba187204149a25fc8f632c44d65da356dc04c0f2b268a71506) for exact changes made. + ### `runner` APIs Renamed To improve consistency with the `web-ext.config.ts` file, the "runner" APIs have been renamed. You can continue using the old names, but they have been deprecated and will be removed in a future version: diff --git a/packages/wxt/e2e/tests/__snapshots__/auto-imports.test.ts.snap b/packages/wxt/e2e/tests/__snapshots__/auto-imports.test.ts.snap index 922eeab7..c0883a9a 100644 --- a/packages/wxt/e2e/tests/__snapshots__/auto-imports.test.ts.snap +++ b/packages/wxt/e2e/tests/__snapshots__/auto-imports.test.ts.snap @@ -5,10 +5,37 @@ exports[`Auto Imports > eslintrc > "enabled: 8" should output a JSON config file ---------------------------------------- { "globals": { + "AutoMount": true, + "AutoMountOptions": true, + "ContentScriptAnchoredOptions": true, + "ContentScriptAppendMode": true, "ContentScriptContext": true, + "ContentScriptInlinePositioningOptions": true, + "ContentScriptModalPositioningOptions": true, + "ContentScriptOverlayAlignment": true, + "ContentScriptOverlayPositioningOptions": true, + "ContentScriptPositioningOptions": true, + "ContentScriptUi": true, + "ContentScriptUiOptions": true, + "IframeContentScriptUi": true, + "IframeContentScriptUiOptions": true, + "InjectScriptOptions": true, + "IntegratedContentScriptUi": true, + "IntegratedContentScriptUiOptions": true, "InvalidMatchPattern": true, "MatchPattern": true, "MigrationError": true, + "ScriptPublicPath": true, + "ShadowRootContentScriptUi": true, + "ShadowRootContentScriptUiOptions": true, + "StopAutoMount": true, + "StorageArea": true, + "StorageAreaChanges": true, + "StorageItemKey": true, + "WxtAppConfig": true, + "WxtStorage": true, + "WxtStorageItem": true, + "WxtWindowEventMap": true, "browser": true, "createIframeUi": true, "createIntegratedUi": true, @@ -31,10 +58,37 @@ exports[`Auto Imports > eslintrc > "enabled: 9" should output a flat config file ".wxt/eslint-auto-imports.mjs ---------------------------------------- const globals = { + "AutoMount": true, + "AutoMountOptions": true, + "ContentScriptAnchoredOptions": true, + "ContentScriptAppendMode": true, "ContentScriptContext": true, + "ContentScriptInlinePositioningOptions": true, + "ContentScriptModalPositioningOptions": true, + "ContentScriptOverlayAlignment": true, + "ContentScriptOverlayPositioningOptions": true, + "ContentScriptPositioningOptions": true, + "ContentScriptUi": true, + "ContentScriptUiOptions": true, + "IframeContentScriptUi": true, + "IframeContentScriptUiOptions": true, + "InjectScriptOptions": true, + "IntegratedContentScriptUi": true, + "IntegratedContentScriptUiOptions": true, "InvalidMatchPattern": true, "MatchPattern": true, "MigrationError": true, + "ScriptPublicPath": true, + "ShadowRootContentScriptUi": true, + "ShadowRootContentScriptUiOptions": true, + "StopAutoMount": true, + "StorageArea": true, + "StorageAreaChanges": true, + "StorageItemKey": true, + "WxtAppConfig": true, + "WxtStorage": true, + "WxtStorageItem": true, + "WxtWindowEventMap": true, "browser": true, "createIframeUi": true, "createIntegratedUi": true, @@ -65,10 +119,37 @@ exports[`Auto Imports > eslintrc > "enabled: true" should output a JSON config f ---------------------------------------- { "globals": { + "AutoMount": true, + "AutoMountOptions": true, + "ContentScriptAnchoredOptions": true, + "ContentScriptAppendMode": true, "ContentScriptContext": true, + "ContentScriptInlinePositioningOptions": true, + "ContentScriptModalPositioningOptions": true, + "ContentScriptOverlayAlignment": true, + "ContentScriptOverlayPositioningOptions": true, + "ContentScriptPositioningOptions": true, + "ContentScriptUi": true, + "ContentScriptUiOptions": true, + "IframeContentScriptUi": true, + "IframeContentScriptUiOptions": true, + "InjectScriptOptions": true, + "IntegratedContentScriptUi": true, + "IntegratedContentScriptUiOptions": true, "InvalidMatchPattern": true, "MatchPattern": true, "MigrationError": true, + "ScriptPublicPath": true, + "ShadowRootContentScriptUi": true, + "ShadowRootContentScriptUiOptions": true, + "StopAutoMount": true, + "StorageArea": true, + "StorageAreaChanges": true, + "StorageItemKey": true, + "WxtAppConfig": true, + "WxtStorage": true, + "WxtStorageItem": true, + "WxtWindowEventMap": true, "browser": true, "createIframeUi": true, "createIntegratedUi": true, @@ -92,10 +173,37 @@ exports[`Auto Imports > eslintrc > should allow customizing the output 1`] = ` ---------------------------------------- { "globals": { + "AutoMount": "readonly", + "AutoMountOptions": "readonly", + "ContentScriptAnchoredOptions": "readonly", + "ContentScriptAppendMode": "readonly", "ContentScriptContext": "readonly", + "ContentScriptInlinePositioningOptions": "readonly", + "ContentScriptModalPositioningOptions": "readonly", + "ContentScriptOverlayAlignment": "readonly", + "ContentScriptOverlayPositioningOptions": "readonly", + "ContentScriptPositioningOptions": "readonly", + "ContentScriptUi": "readonly", + "ContentScriptUiOptions": "readonly", + "IframeContentScriptUi": "readonly", + "IframeContentScriptUiOptions": "readonly", + "InjectScriptOptions": "readonly", + "IntegratedContentScriptUi": "readonly", + "IntegratedContentScriptUiOptions": "readonly", "InvalidMatchPattern": "readonly", "MatchPattern": "readonly", "MigrationError": "readonly", + "ScriptPublicPath": "readonly", + "ShadowRootContentScriptUi": "readonly", + "ShadowRootContentScriptUiOptions": "readonly", + "StopAutoMount": "readonly", + "StorageArea": "readonly", + "StorageAreaChanges": "readonly", + "StorageItemKey": "readonly", + "WxtAppConfig": "readonly", + "WxtStorage": "readonly", + "WxtStorageItem": "readonly", + "WxtWindowEventMap": "readonly", "browser": "readonly", "createIframeUi": "readonly", "createIntegratedUi": "readonly", diff --git a/packages/wxt/e2e/tests/auto-imports.test.ts b/packages/wxt/e2e/tests/auto-imports.test.ts index b69cda42..024a3f55 100644 --- a/packages/wxt/e2e/tests/auto-imports.test.ts +++ b/packages/wxt/e2e/tests/auto-imports.test.ts @@ -20,7 +20,6 @@ describe('Auto Imports', () => { const ContentScriptContext: typeof import('wxt/utils/content-script-context')['ContentScriptContext'] const InvalidMatchPattern: typeof import('wxt/utils/match-patterns')['InvalidMatchPattern'] const MatchPattern: typeof import('wxt/utils/match-patterns')['MatchPattern'] - const MigrationError: typeof import('wxt/utils/storage')['MigrationError'] const browser: typeof import('wxt/browser')['browser'] const createIframeUi: typeof import('wxt/utils/content-script-ui/iframe')['createIframeUi'] const createIntegratedUi: typeof import('wxt/utils/content-script-ui/integrated')['createIntegratedUi'] @@ -35,6 +34,33 @@ describe('Auto Imports', () => { const storage: typeof import('wxt/utils/storage')['storage'] const useAppConfig: typeof import('wxt/utils/app-config')['useAppConfig'] } + // for type re-export + declare global { + // @ts-ignore + export type { StorageArea, WxtStorage, WxtStorageItem, StorageItemKey, StorageAreaChanges, MigrationError } from 'wxt/utils/storage' + import('wxt/utils/storage') + // @ts-ignore + export type { WxtWindowEventMap } from 'wxt/utils/content-script-context' + import('wxt/utils/content-script-context') + // @ts-ignore + export type { IframeContentScriptUi, IframeContentScriptUiOptions } from 'wxt/utils/content-script-ui/iframe' + import('wxt/utils/content-script-ui/iframe') + // @ts-ignore + export type { IntegratedContentScriptUi, IntegratedContentScriptUiOptions } from 'wxt/utils/content-script-ui/integrated' + import('wxt/utils/content-script-ui/integrated') + // @ts-ignore + export type { ShadowRootContentScriptUi, ShadowRootContentScriptUiOptions } from 'wxt/utils/content-script-ui/shadow-root' + import('wxt/utils/content-script-ui/shadow-root') + // @ts-ignore + export type { ContentScriptUi, ContentScriptUiOptions, ContentScriptOverlayAlignment, ContentScriptAppendMode, ContentScriptInlinePositioningOptions, ContentScriptOverlayPositioningOptions, ContentScriptModalPositioningOptions, ContentScriptPositioningOptions, ContentScriptAnchoredOptions, AutoMountOptions, StopAutoMount, AutoMount } from 'wxt/utils/content-script-ui/types' + import('wxt/utils/content-script-ui/types') + // @ts-ignore + export type { WxtAppConfig } from 'wxt/utils/define-app-config' + import('wxt/utils/define-app-config') + // @ts-ignore + export type { ScriptPublicPath, InjectScriptOptions } from 'wxt/utils/inject-script' + import('wxt/utils/inject-script') + } " `); }); @@ -82,18 +108,19 @@ describe('Auto Imports', () => { // Types for the #import virtual module declare module '#imports' { export { browser } from 'wxt/browser'; - export { MigrationError, storage } from 'wxt/utils/storage'; + export { storage, StorageArea, WxtStorage, WxtStorageItem, StorageItemKey, StorageAreaChanges, MigrationError } from 'wxt/utils/storage'; export { useAppConfig } from 'wxt/utils/app-config'; - export { ContentScriptContext } from 'wxt/utils/content-script-context'; - export { createIframeUi } from 'wxt/utils/content-script-ui/iframe'; - export { createIntegratedUi } from 'wxt/utils/content-script-ui/integrated'; - export { createShadowRootUi } from 'wxt/utils/content-script-ui/shadow-root'; - export { defineAppConfig } from 'wxt/utils/define-app-config'; + export { ContentScriptContext, WxtWindowEventMap } from 'wxt/utils/content-script-context'; + export { createIframeUi, IframeContentScriptUi, IframeContentScriptUiOptions } from 'wxt/utils/content-script-ui/iframe'; + export { createIntegratedUi, IntegratedContentScriptUi, IntegratedContentScriptUiOptions } from 'wxt/utils/content-script-ui/integrated'; + export { createShadowRootUi, ShadowRootContentScriptUi, ShadowRootContentScriptUiOptions } from 'wxt/utils/content-script-ui/shadow-root'; + export { ContentScriptUi, ContentScriptUiOptions, ContentScriptOverlayAlignment, ContentScriptAppendMode, ContentScriptInlinePositioningOptions, ContentScriptOverlayPositioningOptions, ContentScriptModalPositioningOptions, ContentScriptPositioningOptions, ContentScriptAnchoredOptions, AutoMountOptions, StopAutoMount, AutoMount } from 'wxt/utils/content-script-ui/types'; + export { defineAppConfig, WxtAppConfig } from 'wxt/utils/define-app-config'; export { defineBackground } from 'wxt/utils/define-background'; export { defineContentScript } from 'wxt/utils/define-content-script'; export { defineUnlistedScript } from 'wxt/utils/define-unlisted-script'; export { defineWxtPlugin } from 'wxt/utils/define-wxt-plugin'; - export { injectScript } from 'wxt/utils/inject-script'; + export { injectScript, ScriptPublicPath, InjectScriptOptions } from 'wxt/utils/inject-script'; export { InvalidMatchPattern, MatchPattern } from 'wxt/utils/match-patterns'; export { fakeBrowser } from 'wxt/testing'; export { startOfDay } from '../utils/time'; @@ -167,18 +194,19 @@ describe('Auto Imports', () => { // Types for the #import virtual module declare module '#imports' { export { browser } from 'wxt/browser'; - export { MigrationError, storage } from 'wxt/utils/storage'; + export { storage, StorageArea, WxtStorage, WxtStorageItem, StorageItemKey, StorageAreaChanges, MigrationError } from 'wxt/utils/storage'; export { useAppConfig } from 'wxt/utils/app-config'; - export { ContentScriptContext } from 'wxt/utils/content-script-context'; - export { createIframeUi } from 'wxt/utils/content-script-ui/iframe'; - export { createIntegratedUi } from 'wxt/utils/content-script-ui/integrated'; - export { createShadowRootUi } from 'wxt/utils/content-script-ui/shadow-root'; - export { defineAppConfig } from 'wxt/utils/define-app-config'; + export { ContentScriptContext, WxtWindowEventMap } from 'wxt/utils/content-script-context'; + export { createIframeUi, IframeContentScriptUi, IframeContentScriptUiOptions } from 'wxt/utils/content-script-ui/iframe'; + export { createIntegratedUi, IntegratedContentScriptUi, IntegratedContentScriptUiOptions } from 'wxt/utils/content-script-ui/integrated'; + export { createShadowRootUi, ShadowRootContentScriptUi, ShadowRootContentScriptUiOptions } from 'wxt/utils/content-script-ui/shadow-root'; + export { ContentScriptUi, ContentScriptUiOptions, ContentScriptOverlayAlignment, ContentScriptAppendMode, ContentScriptInlinePositioningOptions, ContentScriptOverlayPositioningOptions, ContentScriptModalPositioningOptions, ContentScriptPositioningOptions, ContentScriptAnchoredOptions, AutoMountOptions, StopAutoMount, AutoMount } from 'wxt/utils/content-script-ui/types'; + export { defineAppConfig, WxtAppConfig } from 'wxt/utils/define-app-config'; export { defineBackground } from 'wxt/utils/define-background'; export { defineContentScript } from 'wxt/utils/define-content-script'; export { defineUnlistedScript } from 'wxt/utils/define-unlisted-script'; export { defineWxtPlugin } from 'wxt/utils/define-wxt-plugin'; - export { injectScript } from 'wxt/utils/inject-script'; + export { injectScript, ScriptPublicPath, InjectScriptOptions } from 'wxt/utils/inject-script'; export { InvalidMatchPattern, MatchPattern } from 'wxt/utils/match-patterns'; export { fakeBrowser } from 'wxt/testing'; } diff --git a/packages/wxt/src/builtin-modules/unimport.ts b/packages/wxt/src/builtin-modules/unimport.ts index a2d7dd58..c8e45b21 100644 --- a/packages/wxt/src/builtin-modules/unimport.ts +++ b/packages/wxt/src/builtin-modules/unimport.ts @@ -129,7 +129,7 @@ async function getImportsModuleEntry( '// Generated by wxt', '// Types for the #import virtual module', "declare module '#imports' {", - ' ' + toExports(imports, wxt.config.wxtDir).replaceAll('\n', '\n '), + ` ${toExports(imports, wxt.config.wxtDir, true).replaceAll('\n', '\n ')}`, '}', '', ].join('\n'), diff --git a/packages/wxt/src/core/resolve-config.ts b/packages/wxt/src/core/resolve-config.ts index 90ccf5b6..8b9aa740 100644 --- a/packages/wxt/src/core/resolve-config.ts +++ b/packages/wxt/src/core/resolve-config.ts @@ -334,25 +334,124 @@ async function getUnimportOptions( const eslintrc = await getUnimportEslintOptions(wxtDir, config.imports); // mlly sometimes picks up things as exports that aren't. That's what this array contains. const invalidExports = ['options']; + + const defineImportsAndTypes = (imports: string[], typeImports: string[]) => [ + ...imports, + ...typeImports.map((name) => ({ name, type: true })), + ]; + const defaultOptions: WxtResolvedUnimportOptions = { imports: [{ name: 'fakeBrowser', from: 'wxt/testing' }], - // prettier-ignore presets: [ - { package: 'wxt/browser' }, - { package: 'wxt/utils/storage' }, - { package: 'wxt/utils/app-config' }, - { package: 'wxt/utils/content-script-context' }, - { package: 'wxt/utils/content-script-ui/iframe', ignore: invalidExports }, - { package: 'wxt/utils/content-script-ui/integrated', ignore: invalidExports }, - { package: 'wxt/utils/content-script-ui/shadow-root', ignore: invalidExports }, - { package: 'wxt/utils/content-script-ui/types' }, - { package: 'wxt/utils/define-app-config' }, - { package: 'wxt/utils/define-background' }, - { package: 'wxt/utils/define-content-script' }, - { package: 'wxt/utils/define-unlisted-script' }, - { package: 'wxt/utils/define-wxt-plugin' }, - { package: 'wxt/utils/inject-script', ignore: invalidExports }, - { package: 'wxt/utils/match-patterns' }, + { + from: 'wxt/browser', + imports: defineImportsAndTypes(['browser'], []), + }, + { + from: 'wxt/utils/storage', + imports: defineImportsAndTypes( + ['storage'], + [ + 'StorageArea', + 'WxtStorage', + 'WxtStorageItem', + 'StorageArea', + 'StorageItemKey', + 'StorageAreaChanges', + 'MigrationError', + ], + ), + }, + { + from: 'wxt/utils/app-config', + imports: defineImportsAndTypes(['useAppConfig'], []), + }, + { + from: 'wxt/utils/content-script-context', + imports: defineImportsAndTypes( + ['ContentScriptContext'], + ['WxtWindowEventMap'], + ), + }, + { + from: 'wxt/utils/content-script-ui/iframe', + imports: defineImportsAndTypes( + ['createIframeUi'], + ['IframeContentScriptUi', 'IframeContentScriptUiOptions'], + ), + ignore: invalidExports, + }, + { + from: 'wxt/utils/content-script-ui/integrated', + imports: defineImportsAndTypes( + ['createIntegratedUi'], + ['IntegratedContentScriptUi', 'IntegratedContentScriptUiOptions'], + ), + ignore: invalidExports, + }, + { + from: 'wxt/utils/content-script-ui/shadow-root', + imports: defineImportsAndTypes( + ['createShadowRootUi'], + ['ShadowRootContentScriptUi', 'ShadowRootContentScriptUiOptions'], + ), + ignore: invalidExports, + }, + { + from: 'wxt/utils/content-script-ui/types', + imports: defineImportsAndTypes( + [], + [ + 'ContentScriptUi', + 'ContentScriptUiOptions', + 'ContentScriptOverlayAlignment', + 'ContentScriptAppendMode', + 'ContentScriptInlinePositioningOptions', + 'ContentScriptOverlayPositioningOptions', + 'ContentScriptModalPositioningOptions', + 'ContentScriptPositioningOptions', + 'ContentScriptAnchoredOptions', + 'AutoMountOptions', + 'StopAutoMount', + 'AutoMount', + ], + ), + }, + { + from: 'wxt/utils/define-app-config', + imports: defineImportsAndTypes(['defineAppConfig'], ['WxtAppConfig']), + }, + { + from: 'wxt/utils/define-background', + imports: defineImportsAndTypes(['defineBackground'], []), + }, + { + from: 'wxt/utils/define-content-script', + imports: defineImportsAndTypes(['defineContentScript'], []), + }, + { + from: 'wxt/utils/define-unlisted-script', + imports: defineImportsAndTypes(['defineUnlistedScript'], []), + }, + { + from: 'wxt/utils/define-wxt-plugin', + imports: defineImportsAndTypes(['defineWxtPlugin'], []), + }, + { + from: 'wxt/utils/inject-script', + imports: defineImportsAndTypes( + ['injectScript'], + ['ScriptPublicPath', 'InjectScriptOptions'], + ), + ignore: invalidExports, + }, + { + from: 'wxt/utils/match-patterns', + imports: defineImportsAndTypes( + ['InvalidMatchPattern', 'MatchPattern'], + [], + ), + }, ], virtualImports: ['#imports'], debugLog: logger.debug, diff --git a/packages/wxt/src/utils/content-script-ui/iframe.ts b/packages/wxt/src/utils/content-script-ui/iframe.ts index f667f135..65968ea3 100644 --- a/packages/wxt/src/utils/content-script-ui/iframe.ts +++ b/packages/wxt/src/utils/content-script-ui/iframe.ts @@ -1,10 +1,7 @@ /** @module wxt/utils/content-script-ui/iframe */ import { browser } from 'wxt/browser'; import { ContentScriptContext } from '../content-script-context'; -import type { - IframeContentScriptUi, - IframeContentScriptUiOptions, -} from './types'; +import type { ContentScriptUi, ContentScriptUiOptions } from './types'; import { applyPosition, createMountFunctions, mountUi } from './shared'; /** @@ -48,3 +45,32 @@ export function createIframeUi( ...mountFunctions, }; } + +export interface IframeContentScriptUi + extends ContentScriptUi { + /** + * The iframe added to the DOM. + */ + iframe: HTMLIFrameElement; + /** + * A wrapper div that assists in positioning. + */ + wrapper: HTMLDivElement; +} + +export type IframeContentScriptUiOptions = + ContentScriptUiOptions & { + /** + * The path to the HTML page that will be shown in the iframe. This string is passed into + * `browser.runtime.getURL`. + */ + // @ts-expect-error: HtmlPublicPath is generated per-project + page: import('wxt/browser').HtmlPublicPath; + /** + * Callback executed when mounting the UI. Use this function to customize the iframe or wrapper + * element's appearance. It is called every time `ui.mount()` is called. + * + * Optionally return a value that can be accessed at `ui.mounted` or in the `onRemove` callback. + */ + onMount?: (wrapper: HTMLElement, iframe: HTMLIFrameElement) => TMounted; + }; diff --git a/packages/wxt/src/utils/content-script-ui/integrated.ts b/packages/wxt/src/utils/content-script-ui/integrated.ts index 78d53db1..85574d63 100644 --- a/packages/wxt/src/utils/content-script-ui/integrated.ts +++ b/packages/wxt/src/utils/content-script-ui/integrated.ts @@ -1,9 +1,6 @@ /** @module wxt/utils/content-script-ui/integrated */ import { ContentScriptContext } from '../content-script-context'; -import type { - IntegratedContentScriptUi, - IntegratedContentScriptUiOptions, -} from './types'; +import type { ContentScriptUi, ContentScriptUiOptions } from './types'; import { applyPosition, createMountFunctions, mountUi } from './shared'; /** @@ -49,3 +46,32 @@ export function createIntegratedUi( ...mountFunctions, }; } + +/** + * Shared types for the different `wxt/utils/content-script-ui/*` modules. + * @module wxt/utils/content-script-ui/types + */ +export interface IntegratedContentScriptUi + extends ContentScriptUi { + /** + * A wrapper div that assists in positioning. + */ + wrapper: HTMLElement; +} + +export type IntegratedContentScriptUiOptions = + ContentScriptUiOptions & { + /** + * Tag used to create the wrapper element. + * + * @default "div" + */ + tag?: string; + /** + * Callback executed when mounting the UI. This function should create and append the UI to the + * `wrapper` element. It is called every time `ui.mount()` is called. + * + * Optionally return a value that can be accessed at `ui.mounted` or in the `onRemove` callback. + */ + onMount: (wrapper: HTMLElement) => TMounted; + }; diff --git a/packages/wxt/src/utils/content-script-ui/shadow-root.ts b/packages/wxt/src/utils/content-script-ui/shadow-root.ts index de0cf88e..c4c5471d 100644 --- a/packages/wxt/src/utils/content-script-ui/shadow-root.ts +++ b/packages/wxt/src/utils/content-script-ui/shadow-root.ts @@ -1,9 +1,6 @@ /** @module wxt/utils/content-script-ui/shadow-root */ import { ContentScriptContext } from '../content-script-context'; -import type { - ShadowRootContentScriptUi, - ShadowRootContentScriptUiOptions, -} from './types'; +import type { ContentScriptUi, ContentScriptUiOptions } from './types'; import { createIsolatedElement } from '@webext-core/isolated-element'; import { applyPosition, createMountFunctions, mountUi } from './shared'; import { logger } from '../internal/logger'; @@ -109,3 +106,77 @@ async function loadCss(): Promise { return ''; } } + +export interface ShadowRootContentScriptUi + extends ContentScriptUi { + /** + * The `HTMLElement` hosting the shadow root used to isolate the UI's styles. This is the element + * that get's added to the DOM. This element's style is not isolated from the webpage. + */ + shadowHost: HTMLElement; + /** + * The container element inside the `ShadowRoot` whose styles are isolated. The UI is mounted + * inside this `HTMLElement`. + */ + uiContainer: HTMLElement; + /** + * The shadow root performing the isolation. + */ + shadow: ShadowRoot; +} + +export type ShadowRootContentScriptUiOptions = + ContentScriptUiOptions & { + /** + * The name of the custom component used to host the ShadowRoot. Must be kebab-case. + */ + name: string; + /** + * Custom CSS text to apply to the UI. If your content script imports/generates CSS and you've + * set `cssInjectionMode: "ui"`, the imported CSS will be included automatically. You do not need + * to pass those styles in here. This is for any additional styles not in the imported CSS. + */ + css?: string; + /** + * ShadowRoot's mode. + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/mode + * @default "open" + */ + mode?: 'open' | 'closed'; + /** + * When enabled, `event.stopPropagation` will be called on events trying to bubble out of the + * shadow root. + * + * - Set to `true` to stop the propagation of a default set of events, + * `["keyup", "keydown", "keypress"]` + * - Set to an array of event names to stop the propagation of a custom list of events + */ + isolateEvents?: boolean | string[]; + /** + * By default, WXT adds `all: initial` to the shadow root before the rest of + * your CSS. This resets any inheritable CSS styles that + * [normally pierce the Shadow DOM](https://open-wc.org/guides/knowledge/styling/styles-piercing-shadow-dom/). + * + * WXT resets everything but: + * - **`rem` Units**: they continue to scale based off the webpage's HTML `font-size`. + * - **CSS Variables/Custom Properties**: CSS variables defined outside the shadow root can be accessed inside it. + * - **`@font-face` Definitions**: Fonts defined outside the shadow root can be used inside it. + * + * To disable this behavior and inherit styles from the webpage, set `inheritStyles: true`. + * + * @default false + */ + inheritStyles?: boolean; + /** + * Callback executed when mounting the UI. This function should create and append the UI to the + * `uiContainer` element. It is called every time `ui.mount()` is called. + * + * Optionally return a value that can be accessed at `ui.mounted` or in the `onRemove` callback. + */ + onMount: ( + uiContainer: HTMLElement, + shadow: ShadowRoot, + shadowHost: HTMLElement, + ) => TMounted; + }; diff --git a/packages/wxt/src/utils/content-script-ui/types.ts b/packages/wxt/src/utils/content-script-ui/types.ts index a34b9a3a..b242eadc 100644 --- a/packages/wxt/src/utils/content-script-ui/types.ts +++ b/packages/wxt/src/utils/content-script-ui/types.ts @@ -1,45 +1,3 @@ -/** - * Shared types for the different `wxt/utils/content-script-ui/*` modules. - * @module wxt/utils/content-script-ui/types - */ -export interface IntegratedContentScriptUi - extends ContentScriptUi { - /** - * A wrapper div that assists in positioning. - */ - wrapper: HTMLElement; -} - -export interface IframeContentScriptUi - extends ContentScriptUi { - /** - * The iframe added to the DOM. - */ - iframe: HTMLIFrameElement; - /** - * A wrapper div that assists in positioning. - */ - wrapper: HTMLDivElement; -} - -export interface ShadowRootContentScriptUi - extends ContentScriptUi { - /** - * The `HTMLElement` hosting the shadow root used to isolate the UI's styles. This is the element - * that get's added to the DOM. This element's style is not isolated from the webpage. - */ - shadowHost: HTMLElement; - /** - * The container element inside the `ShadowRoot` whose styles are isolated. The UI is mounted - * inside this `HTMLElement`. - */ - uiContainer: HTMLElement; - /** - * The shadow root performing the isolation. - */ - shadow: ShadowRoot; -} - export interface ContentScriptUi extends MountFunctions { mounted: TMounted | undefined; } @@ -53,96 +11,6 @@ export type ContentScriptUiOptions = ContentScriptPositioningOptions & onRemove?: (mounted: TMounted | undefined) => void; }; -export type IntegratedContentScriptUiOptions = - ContentScriptUiOptions & { - /** - * Tag used to create the wrapper element. - * - * @default "div" - */ - tag?: string; - /** - * Callback executed when mounting the UI. This function should create and append the UI to the - * `wrapper` element. It is called every time `ui.mount()` is called. - * - * Optionally return a value that can be accessed at `ui.mounted` or in the `onRemove` callback. - */ - onMount: (wrapper: HTMLElement) => TMounted; - }; - -export type IframeContentScriptUiOptions = - ContentScriptUiOptions & { - /** - * The path to the HTML page that will be shown in the iframe. This string is passed into - * `browser.runtime.getURL`. - */ - // @ts-expect-error: HtmlPublicPath is generated per-project - page: import('wxt/browser').HtmlPublicPath; - /** - * Callback executed when mounting the UI. Use this function to customize the iframe or wrapper - * element's appearance. It is called every time `ui.mount()` is called. - * - * Optionally return a value that can be accessed at `ui.mounted` or in the `onRemove` callback. - */ - onMount?: (wrapper: HTMLElement, iframe: HTMLIFrameElement) => TMounted; - }; - -export type ShadowRootContentScriptUiOptions = - ContentScriptUiOptions & { - /** - * The name of the custom component used to host the ShadowRoot. Must be kebab-case. - */ - name: string; - /** - * Custom CSS text to apply to the UI. If your content script imports/generates CSS and you've - * set `cssInjectionMode: "ui"`, the imported CSS will be included automatically. You do not need - * to pass those styles in here. This is for any additional styles not in the imported CSS. - */ - css?: string; - /** - * ShadowRoot's mode. - * - * @see https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/mode - * @default "open" - */ - mode?: 'open' | 'closed'; - /** - * When enabled, `event.stopPropagation` will be called on events trying to bubble out of the - * shadow root. - * - * - Set to `true` to stop the propagation of a default set of events, - * `["keyup", "keydown", "keypress"]` - * - Set to an array of event names to stop the propagation of a custom list of events - */ - isolateEvents?: boolean | string[]; - /** - * By default, WXT adds `all: initial` to the shadow root before the rest of - * your CSS. This resets any inheritable CSS styles that - * [normally pierce the Shadow DOM](https://open-wc.org/guides/knowledge/styling/styles-piercing-shadow-dom/). - * - * WXT resets everything but: - * - **`rem` Units**: they continue to scale based off the webpage's HTML `font-size`. - * - **CSS Variables/Custom Properties**: CSS variables defined outside the shadow root can be accessed inside it. - * - **`@font-face` Definitions**: Fonts defined outside the shadow root can be used inside it. - * - * To disable this behavior and inherit styles from the webpage, set `inheritStyles: true`. - * - * @default false - */ - inheritStyles?: boolean; - /** - * Callback executed when mounting the UI. This function should create and append the UI to the - * `uiContainer` element. It is called every time `ui.mount()` is called. - * - * Optionally return a value that can be accessed at `ui.mounted` or in the `onRemove` callback. - */ - onMount: ( - uiContainer: HTMLElement, - shadow: ShadowRoot, - shadowHost: HTMLElement, - ) => TMounted; - }; - export type ContentScriptOverlayAlignment = | 'top-left' | 'top-right'