diff --git a/docs/guide/essentials/entrypoints.md b/docs/guide/essentials/entrypoints.md index 0902d75c..dda2f817 100644 --- a/docs/guide/essentials/entrypoints.md +++ b/docs/guide/essentials/entrypoints.md @@ -4,50 +4,30 @@ outline: deep # Entrypoints -WXT uses the files inside the `entrypoints/` directory as inputs when bundling your extension. They can be HTML, JS, CSS, or any variant of those file types supported by Vite (Pug, TS, JSX, SCSS, etc). +WXT uses the files inside the `entrypoints/` directory as inputs when bundling your extension. They can be HTML, JS, CSS, or any variant of those file types supported by Vite (TS, JSX, SCSS, etc). -Here's an example set of entrypoints: +## Folder Structure + +Inside the `entrypoints/` directory, an entrypoint is defined as a single file or directory (with an `index` file) inside it. + +:::code-group -```html +```html [Single File] 📂 entrypoints/ - 📂 popup/ - 📄 index.html - 📄 main.ts - 📄 style.css - 📄 background.ts - 📄 content.ts + 📄 {name}.{ext} ``` -[[toc]] + +```html [Directory] +📂 entrypoints/ + 📂 {name}/ + 📄 index.{ext} +``` -## Listed vs Unlisted - -For web extensions, there are two types of entrypoints: - -- **Listed**: Referenced in the `manifest.json` -- **Unlisted**: Not referenced in the `manifest.json` - -Throughout the rest of WXT's documentation, listed entrypoints are referred to by name. For example: - -- Popup -- Options -- Background -- Content Scripts -- Etc. - -Some examples of "unlisted" entrypoints: - -- A welcome page shown when the extension is installed -- JS files injected by content scripts into the page's main world - -:::tip -Regardless of whether an entrypoint is listed or unlisted, it will still be bundled into your extension and be available at runtime. ::: -## Adding Entrypoints - -An entrypoint can be defined as a single file or directory with an `index` file inside it. +The entrypoint's `name` dictates the type of entrypoint. For example, to add a ["Background" entrypoint](#background), either of these files would work: :::code-group @@ -66,10 +46,96 @@ An entrypoint can be defined as a single file or directory with an `index` file ::: -The entrypoint's name dictates the type of entrypoint, listed vs unlisted. In this example, "background" is the name of the ["Background" entrypoint](#background). - Refer to the [Entrypoint Types](#entrypoint-types) section for the full list of listed entrypoints and their filename patterns. +### Including Other Files + +When using an entrypoint directory, `entrypoints/{name}/index.{ext}`, you can add related files next to the `index` file. + + +```html +📂 entrypoints/ + 📂 popup/ + 📄 index.html ← This file is the entrypoint + 📄 main.ts + 📄 style.css + 📂 background/ + 📄 index.ts ← This file is the entrypoint + 📄 alarms.ts + 📄 messaging.ts + 📂 youtube.content/ + 📄 index.ts ← This file is the entrypoint + 📄 style.css +``` + +:::danger +**DO NOT** put files related to an entrypoint directly inside the `entrypoints/` directory. WXT will treat them as entrypoints and try to build them, usually resulting in an error. + +Instead, use a directory for that entrypoint: + + +```html +📂 entrypoints/ + 📄 popup.html + 📄 popup.ts + 📄 popup.css + 📂 popup/ + 📄 index.html + 📄 main.ts + 📄 style.css +``` + +::: + +### Deeply Nested Entrypoints + +While the `entrypoints/` directory might resemble the `pages/` directory of other web frameworks, like Nuxt or Next.js, **it does not support deeply nesting entrypoints** in the same way. + +Entrypoints must be zero or one levels deep for WXT to discover and build them: + + +```html +📂 entrypoints/ + 📂 youtube/ + 📂 content/ + 📄 index.ts + 📄 ... + 📂 injected/ + 📄 index.ts + 📄 ... + 📂 youtube.content/ + 📄 index.ts + 📄 ... + 📂 youtube-injected/ + 📄 index.ts + 📄 ... +``` + +## Unlisted Entrypoints + +In web extensions, there are two types of entrypoints: + +1. **Listed**: Referenced in the `manifest.json` +2. **Unlisted**: Not referenced in the `manifest.json` + +Throughout the rest of WXT's documentation, listed entrypoints are referred to by name. For example: + +- Popup +- Options +- Background +- Content Script + +However, not all entrypoints in web extensions are listed in the manifest. Some are not listed in the manifest, but are still used by extensions. For example: + +- A welcome page shown in a new tab when the extension is installed +- JS files injected by content scripts into the main world + +For more details on how to add unlisted entrypoints, see: + +- [Unlisted Pages](#unlisted-pages) +- [Unlisted Scripts](#unlisted-scripts) +- [Unlisted CSS](#unlisted-css) + ## Defining Manifest Options Most listed entrypoints have options that need to be added to the `manifest.json`. However with WXT, instead of defining the options in a separate file, _you define these options inside the entrypoint file itself_. @@ -106,8 +172,6 @@ When building your extension, WXT will look at the options defined in your entry [Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/manifest/background/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/background) -For MV2, the background is added as a script to the background page. For MV3, the background becomes a service worker. - .content.[jt]sx?', 'content-scripts/.js'], - ['.content/index.[jt]sx?', 'content-scripts/.js'], + ['{name}.content.[jt]sx?', 'content-scripts/{name}.js'], + ['{name}.content/index.[jt]sx?', 'content-scripts/{name}.js'], ]" /> @@ -213,12 +297,30 @@ export default defineContentScript({ }); ``` +When defining content script entrypoints, keep in mind that WXT will import this file in a NodeJS environment during the build process. That means you cannot place any runtime code outside the `main` function. + +```ts +browser.runtime.onMessage.addListener((message) => { + // [!code --] + // ... // [!code --] +}); // [!code --] + +export default defineBackground(() => { + browser.runtime.onMessage.addListener((message) => { + // [!code ++] + // ... // [!code ++] + }); // [!code ++] +}); +``` + +Refer to the [Entrypoint Loaders](/guide/essentials/config/entrypoint-loaders) documentation for more details. + +See [Content Script UI](/guide/essentials/content-scripts) for more info on creating UIs and including CSS in content scripts. + ### Devtools [Chrome Docs](https://developer.chrome.com/docs/extensions/mv3/devtools/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/devtools_page) -Follow the [Devtools Example](https://github.com/wxt-dev/examples/tree/main/examples/devtools-extension#readme) to add different panels and panes. - .sandbox.html', '.html'], - ['.sandbox/index.html', '.html'], + ['{name}.sandbox.html', '{name}.html'], + ['{name}.sandbox/index.html', '{name}.html'], ]" /> @@ -414,14 +522,12 @@ Firefox does not support sandboxed pages. [Chrome Docs](https://developer.chrome.com/docs/extensions/reference/sidePanel/) • [Firefox Docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/user_interface/Sidebars) -In Chrome, side panels use the `side_panel` API, while Firefox uses the `sidebar_action` API. - @@ -455,20 +561,18 @@ In Chrome, side panels use the `side_panel` API, while Firefox uses the `sidebar ``` +In Chrome, side panels use the `side_panel` API, while Firefox uses the `sidebar_action` API. + ### Unlisted CSS -Follow Vite's guide to setup your preprocessor of choice: https://vitejs.dev/guide/features.html#css-pre-processors - -CSS entrypoints are always unlisted. To add CSS to a content script, see the [Content Script](/guide/essentials/content-scripts#css) docs. - @@ -478,12 +582,16 @@ body { } ``` +Follow Vite's guide to setup your preprocessor of choice: https://vitejs.dev/guide/features.html#css-pre-processors + +CSS entrypoints are always unlisted. To add CSS to a content script, see the [Content Script](/guide/essentials/content-scripts#css) docs. + ### Unlisted Pages @@ -505,20 +613,21 @@ body { ``` -Pages are accessible at `/.html`: +At runtime, unlisted pages are accessible at `/{name}.html`: ```ts -const url = browser.runtime.getURL('/.html'); +const url = browser.runtime.getURL('/{name}.html'); -console.log(url); // "chrome-extension:///.html" +console.log(url); // "chrome-extension://{id}/{name}.html" +window.open(url); // Open the page in a new tab ``` ### Unlisted Scripts @@ -544,12 +653,30 @@ export default defineUnlistedScript({ ::: -Scripts are accessible from `/.js`: +At runtime, unlisted scripts are accessible from `/{name}.js`: ```ts -const url = browser.runtime.getURL('/.js'); +const url = browser.runtime.getURL('/{name}.js'); -console.log(url); // "chrome-extension:///.js" +console.log(url); // "chrome-extension://{id}/{name}.js" ``` You are responsible for loading/running these scripts where needed. If necessary, don't forget to add the script and/or any related assets to [`web_accessible_resources`](https://developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources). + +When defining an unlisted script, keep in mind that WXT will import this file in a NodeJS environment during the build process. That means you cannot place any runtime code outside the `main` function. + +```ts +document.querySelectorAll('a').forEach((anchor) => { + // [!code --] + // ... // [!code --] +}); // [!code --] + +export default defineUnlistedScript(() => { + document.querySelectorAll('a').forEach((anchor) => { + // [!code ++] + // ... // [!code ++] + }); // [!code ++] +}); +``` + +Refer to the [Entrypoint Loaders](/guide/essentials/config/entrypoint-loaders) documentation for more details.