extension-develop
v4.0.8
Published
Develop, build, preview, and package Extension.js projects.
Maintainers
Readme
extension-develop

Develop, build, preview, and package Extension.js projects.
This package powers Extension.js during local development and production builds. It provides the commands and build plugins that compile your extension, run it in browsers, and produce distributable artifacts.
Installation
pnpm add extension-developUsage
import {
extensionDev,
extensionBuild,
extensionStart,
extensionPreview,
type DevOptions,
type BuildOptions,
type StartOptions,
type PreviewOptions
} from 'extension-develop'
async function run() {
// Development server
await extensionDev(undefined, {
browser: 'chrome',
open: true
} satisfies DevOptions)
// Production build + zip
await extensionBuild(undefined, {
browser: 'firefox',
zip: true
} satisfies BuildOptions)
// Build then preview from dist/<browser>
await extensionStart(undefined, {browser: 'edge'} satisfies StartOptions)
// Preview using an existing output folder or project path
await extensionPreview(undefined, {
browser: 'chrome',
mode: 'production'
} satisfies PreviewOptions)
}
run()Features
- Live reload/HMR development server with per-instance browser runners
- Cross-browser support: Chrome, Edge, Firefox, Chromium-based, Gecko-based
- Rspack-based build with opinionated plugin stack
- Clean production output in
dist/<browser> - Zipping: distribution and/or source packages (respects
.gitignore) - Auto-install of missing build + optional dependencies on first run
- Type generation for TS projects via
extension-env.d.ts - User config via
extension.config.(js|mjs)for commands, browser start, unified logger defaults, and webpack config hooks - Managed dependency guard to avoid conflicts
Commands
| Name | Summary |
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| dev | - Starts a local development server with live reload/HMR- Auto-installs build + optional deps if missing- Generates TypeScript shim types (extension-env.d.ts) when applicable- Runs a target browser with an isolated/stable profile |
| build | - Production build using the webpack/Rspack plugin stack- Cleans dist/<browser> before emitting- Optional packaging: distribution zip and/or source zip- Merges user config; excludes browser runners during compilation |
| start | - Runs a silent production build, then runs preview from dist/<browser>- Mirrors the runtime environment of shipped output |
| preview | - Runs the extension for manual testing without dev server- Uses dist/<browser> when present, otherwise uses the project directory- Preserves production settings; only browser runners are applied |
| cleanup | - Removes orphaned browser instances and temporary profiles created during development |
Command options
Options accepted by each command. Values shown are typical types or enumerations; see the tables for specifics.
Common (browser/runtime)
| Option | Type / Values | Description |
| -------------- | -------------------------------------------------- | --------------------------------------------------------------- |
| browser | chrome, edge, firefox, chromium-based, gecko-based | Target browser/runtime |
| profile | string or false | Profile path or disable profile persistence |
| startingUrl | string | Initial URL to open |
| open | boolean | Focus/open the browser window (CLI: use --no-open to disable) |
| --no-browser | boolean | Skip launching the browser |
| chromiumBinary | string | Custom Chromium-based executable path |
| geckoBinary | string | Custom Gecko-based executable path |
dev
| Option | Type / Values | Description |
| ------------------- | ----------------------------------- | --------------------------------------------------------------------------------- |
| mode | development, production, none | Build mode |
| polyfill | boolean | Include webextension-polyfill when possible |
| port | number or string | Dev server port |
| source | string | Inspect a source directory |
| watchSource | boolean | Watch the source directory |
| sourceFormat | pretty,json,ndjson | Output format for source HTML |
| sourceSummary | boolean | Output a compact summary instead of full HTML |
| sourceMeta | boolean | Output page metadata (readyState, viewport, frames) |
| sourceProbe | string[] | CSS selectors to probe for quick validation |
| sourceTree | off,root-only | Output a compact extension root tree |
| sourceConsole | boolean | Output console summary (best-effort) |
| sourceDom | boolean | Output DOM snapshots and diffs |
| sourceMaxBytes | number | Limit HTML output size in bytes (0 disables truncation) |
| sourceRedact | off,safe,strict | Redact sensitive content in HTML output |
| sourceIncludeShadow | off,open-only,all | Control Shadow DOM inclusion |
| sourceDiff | boolean | Include diff metadata on watch updates |
| logs | off,error,warn,info,debug,trace,all | Unified logger verbosity (all shows everything) |
| logContext | list or all | Comma-separated contexts (background,content,page,sidebar,popup,options,devtools) |
| logFormat | pretty,json,ndjson | Pretty text or JSON/NDJSON output |
| logTimestamps | boolean | Include timestamps in pretty output |
| logColor | boolean | Colorize pretty output |
| logUrl | string or /regex/flags | Filter by URL substring or JS-style regex literal |
| logTab | number | Filter by tabId |
build
| Option | Type / Values | Description |
| ----------- | ------------- | -------------------------------------------------------------- |
| zip | boolean | Package dist/<browser> as an artifact (e.g., .zip, .xpi) |
| zipSource | boolean | Package source files (respects .gitignore) |
| zipFilename | string | Custom base name for artifacts |
| polyfill | boolean | Include webextension-polyfill when possible |
| silent | boolean | Suppress non-error logs |
start
| Option | Type / Values | Description |
| -------- | ------------- | --------------------------------------------- |
| mode | production | Build mode |
| polyfill | boolean | Include webextension-polyfill when possible |
preview
| Option | Type / Values | Description |
| ---------- | ------------- | ------------------------------------------------------------------- |
| mode | production | Build mode |
| outputPath | string | Directory to run from (defaults to dist/<browser> when available) |
Project detection and inputs
- Path or remote: Commands accept a local path or a remote URL.
- GitHub repo URL: downloaded via
go-git-itinto a subfolder named after the repository. - Other HTTP(S) URLs: treated as zip archives and extracted locally.
- GitHub repo URL: downloaded via
- Monorepos: The nearest
manifest.jsonis resolved recursively; the nearest validpackage.jsonis then located and validated.
Root semantics (manifest vs package.json)
manifest.jsonmay live in any subdirectory of your project.- The project root for build/dev is the directory containing the nearest valid
package.json(i.e., webpackcontext). - Special folders and root-relative paths are anchored at the package root:
public/,pages/,scripts/, and URLs starting with/resolve relative to the package root (e.g.,/logo.png→<packageRoot>/public/logo.png).
- Web-only mode: if no
package.jsonis found, the manifest directory is used as a fallback project root.
User config
- Provide
extension.config.jsorextension.config.mjsin your project root. - Supported sections:
- config(config: Configuration): mutate the assembled Rspack config. Supports a function or a plain object. When an object is provided, it is deep‑merged on top of the assembled config.
- commands.dev | .build | .start | .preview: per‑command options (browser, profile, binaries, flags, preferences, unified logger defaults, packaging). These defaults are applied for all respective commands.
- browser.chrome | .firefox | .edge | .chromium-based | .gecko-based: start flags, excluded flags, preferences, binaries, profile reuse (persistProfile), and per-browser
extensions.- extensions: load-only companion extensions (unpacked dirs) loaded alongside your extension in dev/preview/start.
- Example: { dir: "./extensions" } loads every "./extensions/*" folder that contains a manifest.json.
- extensions: load-only companion extensions (unpacked dirs) loaded alongside your extension in dev/preview/start.
- Precedence when composing options: browser._ → commands._ → CLI flags. CLI flags always win over config defaults.
- Browser key aliases when resolving
browser.*fromextension.config.*:- When the runtime asks for
chromium,loadBrowserConfigprefersbrowser.chromiumand then falls back tobrowser['chromium-based']. - When the runtime asks for
chromium-based, it prefersbrowser['chromium-based']and thenbrowser.chromium. - When the runtime asks for
firefox, it prefersbrowser.firefoxand thenbrowser['gecko-based']. - When the runtime asks for
gecko-based, it prefersbrowser['gecko-based']and thenbrowser.firefox.
- When the runtime asks for
- When detected, a one‑time notice is printed to indicate config is active.
Companion extensions (load-only)
Use this when you have other unpacked extensions you want loaded alongside your main extension during dev, start, and preview.
- What it loads: directories that contain a
manifest.jsonat their root (unpacked extension roots). - How they’re loaded: they’re appended into the browser runner’s
--load-extensionlist (Chromium) / addon install list (Firefox) before your extension. Your extension is always loaded last for precedence. - Discovery:
extensions.dir: scans one level deep (e.g../extensions/*/manifest.json)- When
dirpoints to./extensions, browser folders like./extensions/chrome/*and./extensions/firefox/*are also scanned.
- When
extensions.paths: explicit directories (absolute or relative to the project root)
- Overrides: top-level
extensionsapplies to all commands, butcommands.<cmd>.extensionsoverrides it for that command. - Invalid entries: ignored. In author mode (
EXTENSION_AUTHOR_MODE=true) we print a warning ifextensionsis configured but nothing resolves. - Store URLs: entries pointing to Chrome Web Store, Edge Addons, or AMO are downloaded on-demand into
./extensions/<browser>/<id-or-slug>. - Local paths: only paths under
./extensions/are accepted for companion extensions. - CLI: use
--extensions <csv>to provide a comma-separated list of paths or store URLs.
Example:
// extension.config.mjs
export default {
// Applies to dev/start/preview unless overridden per-command
extensions: {
dir: './extensions',
paths: ['./vendor/some-unpacked-extension']
},
commands: {
dev: {
// Override only for dev
extensions: ['./extensions/debug-helper']
}
}
}Environment variables in extension.config.*
extension.config.*runs in a Node context during command startup.- Use
process.env.*to read environment variables inside the config file. import.meta.env.*is available in your extension code at bundle time (via the Env plugin), not in the Node config.
- Use
- During config loading, develop preloads environment files from the project directory into
process.envusing the following order (first match wins):.env.defaults(always merged first when present).env.development.env.local.env
- In monorepos, if none of the files above exist in the project directory, develop falls back to the nearest workspace root (directory containing
pnpm-workspace.yaml) and applies the same loading order there. - Only variables you read explicitly in the config are used there; client-side injection still requires the
EXTENSION_PUBLIC_*prefix. - Example:
// extension.config.js (Node-based)
export default {
browser: {
chrome: {
startingUrl:
process.env.EXTENSION_PUBLIC_START_URL || 'https://example.com'
}
},
commands: {
dev: {
// Unified logger defaults for `extension dev`
logLevel: 'off',
// omit or set to undefined to include all
logContexts: ['background', 'content'],
logFormat: 'pretty',
logTimestamps: true,
logColor: true,
// Optional filters
logUrl: '/example\\.com/i',
logTab: 123
}
},
// Either a function
config: (config) => config
// Or a plain object to merge
// config: { resolve: { alias: { react: 'preact/compat' } } }
}Safety and ergonomics
- Managed dependency guard: If your
extension.config.*references dependencies that are managed by Extension.js itself, the command aborts with a detailed message to prevent version conflicts. - Auto‑install: If
node_modulesis missing, the appropriate package manager is detected and dependencies are installed before running. - Type generation: For TypeScript projects,
extension-env.d.tsis generated/updated to include required types and polyfills.
Packaging outputs
- Distribution artifacts live under
dist/<browser>/and source artifacts underdist/. - File names default to a sanitized form of
manifest.nameplusmanifest.version(override withzipFilename). - Source packaging respects
.gitignore.
Example layout when both zip and zipSource are enabled:
dist/
chrome/
<name>-<version>.zip
<name>-<version>-source.zipNotes and compatibility
- Built on the same Rspack stack as
@/webpack; user config is loaded when anextension.config.*is present. - Only
EXTENSION_PUBLIC_*variables are injected into client code; avoid secrets in templated.json/.html.
Plugins
| Name | Group | Summary |
| -------------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| plugin-extension | core | - Core builder: emits pages and scripts- Validates and rewrites manifest.json- Ships icons, JSON, locales, and web resources- Ensures dev parity between local and shipped output |
| plugin-css | core | - Auto‑wires CSS for HTML and content scripts- Optional SASS/LESS/PostCSS when configs exist- Integrates Stylelint when configured |
| plugin-js-frameworks | core | - Detects React/Preact/Vue/Svelte and TypeScript- Configures SWC parsing, loaders/plugins, and safe aliases- Sets tsconfig resolution- Defers heavy work to beforeRun in production |
| plugin-static-assets | core | - Emits images, fonts, and misc files to assets/- Inlines small SVGs (≤2KB), emits larger ones- Content hashing in production; stable names in development- Respects existing custom SVG rules |
| plugin-compatibility | core | - Cross‑browser helpers- Normalizes browser‑specific manifest fields- Optional webextension-polyfill for Chromium |
| plugin-compilation | core | - Loads env and templating (EXTENSION_PUBLIC_*)- Optional dist/<browser> cleaning- Compact, de‑duplicated compilation summary |
Notes and compatibility
- Built against
@rspack/core; Webpack 5 may work for some plugins but is not officially supported here. - Only
EXTENSION_PUBLIC_*variables are injected into client code; avoid embedding secrets in templated.json/.html.
Related files in this folder
webpack/webpack-config.ts: Assembles the plugin stack and shared configuration.dev-server/: Local development server wiring and reload orchestration.webpack/webpack-types.ts: Common types for the plugin stack.
License
MIT (c) Cezar Augusto and the Extension.js authors.
