extension 0.0.0-next-20250825144602

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Cezar Augusto <boss@cezaraugusto.net> (https://cezaraugusto.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ [npm-version-image]: https://img.shields.io/npm/v/extension.svg?color=0971fe
2
+ [npm-version-url]: https://www.npmjs.com/package/extension
3
+ [downloads-image]: https://img.shields.io/npm/dm/extension.svg?color=2ecc40
4
+ [downloads-url]: https://npmjs.org/package/extension
5
+ [action-image]: https://github.com/extension-js/extension.js/actions/workflows/ci.yml/badge.svg?branch=main&color=2ecc40
6
+ [action-url]: https://github.com/extension-js/extension.js/actions
7
+ [coverage-image]: https://img.shields.io/codecov/c/github/extension-js/extension.js?color=2ecc40
8
+ [coverage-url]: https://codecov.io/github/extension-js/extension.js
9
+ [discord-image]: https://img.shields.io/discord/1253608412890271755?label=Discord&logo=discord&style=flat&color=2ecc40
10
+ [discord-url]: https://discord.gg/v9h2RgeTSN
11
+ [snyk-image]: https://snyk.io/test/github/extension-js/extension/badge.svg?color=2ecc40
12
+ [snyk-url]: https://snyk.io/test/github/extension-js/extension
13
+
14
+ > The cross-browser extension framework
15
+
16
+ # Extension.js [![Version][npm-version-image]][npm-version-url] [![Downloads][downloads-image]][downloads-url] [![workflow][action-image]][action-url] [![coverage][coverage-image]][coverage-url] [![discord][discord-image]][discord-url]
17
+
18
+ <img alt="Logo" align="right" src="https://avatars.githubusercontent.com/u/172809806" width="20%" />
19
+
20
+ - [Create A New Extension](#create-a-new-extension) — How to create a new extension.
21
+ - [Get Started Immediately](#get-started-immediately) — Get work done in no time.
22
+ - [Start From An Example](https://github.com/extension-js/extension.js/tree/main/examples) — Start with your favorite tool.
23
+ - [I have An Extension](#i-have-an-extension) — Use only specific parts of Extension.js.
24
+
25
+ Extension.js makes it very easy to develop cross-browser extensions.<br />Developers prefer it for its fast builds, unified interface, and zero configuration setup.
26
+
27
+ ## Create A New Extension
28
+
29
+ Use the `create` command to generate a new extension. Also works with pnpm, yarn, and bun.
30
+
31
+ ```bash
32
+ npx extension@latest create my-extension
33
+ cd my-extension
34
+ npm run dev
35
+ ```
36
+
37
+ ### Watch Demo
38
+
39
+ https://github.com/cezaraugusto/extension/assets/4672033/7263d368-99c4-434f-a60a-72c489672586
40
+
41
+ ## Web Standards and Framework Support
42
+
43
+ <!-- For a preview of extensions running these technologies, see the [templates](https://templates.extension.land) website. -->
44
+
45
+ | <img src="https://github.com/cezaraugusto/extension.js/assets/4672033/a9e2541a-96f0-4caa-9fc9-5fc5c3e901c8" width="70"> | <img src="https://github.com/cezaraugusto/extension.js/assets/4672033/b42c5330-9e2a-4045-99c3-1f7d264dfaf4" width="70"> | <img src="https://github.com/cezaraugusto/extension.js/assets/4672033/f19edff3-9005-4f50-b05c-fba615896a7f" width="70"> | <img src="https://github.com/cezaraugusto/extension.js/assets/4672033/ff64721d-d145-4213-930d-e70193f8d57e" width="70"> | <img src="https://github.com/cezaraugusto/extension.js/assets/4672033/15f1314a-aa65-4ce2-a3f3-cf53c4f730cf" width="70"> | <img src="https://github.com/cezaraugusto/extension.js/assets/4672033/de1082fd-7cf6-4202-8c12-a5c3cd3e5b42" width="70"> | <img src="https://github.com/cezaraugusto/extension.js/assets/4672033/8807efd9-93e5-4db5-a1d2-9ac524f7ecc2" width="70"> | <img src="https://github.com/cezaraugusto/extension.js/assets/4672033/c5f8a127-3c2a-4ceb-bb46-948cf2c8bd89" width="70"> | <img src="https://github.com/cezaraugusto/extension.js/assets/4672033/78e5fe3d-dc79-4aa2-954e-1a5973d1d9db" width="70"> |
46
+ | :---------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------: |
47
+ | ESNext<br>latest | TypeScript<br>latest | WASM<br>latest | React<br>18+ | Vue<br>3+ | Svelte<br>5+ | Preact<br>10+ | Angular<br>👋 | Solid<br>👋 |
48
+
49
+ 👋 = PR Welcome!
50
+
51
+ ## Get Started Immediately
52
+
53
+ Start developing an extension using a sample from Chrome Extension Samples
54
+
55
+ See the example below where we request the sample [page-redder](https://github.com/GoogleChrome/chrome-extensions-samples/tree/main/functional-samples/sample.page-redder) from [Google Chrome Extension Samples](https://github.com/GoogleChrome/chrome-extensions-samples).
56
+
57
+ ### Watch Demo
58
+
59
+ https://github.com/cezaraugusto/extension/assets/4672033/ee221a94-6ec7-4e04-8553-8812288927f1
60
+
61
+ ### Try Yourself
62
+
63
+ ```bash
64
+ npx extension@latest dev https://github.com/GoogleChrome/chrome-extensions-samples/tree/main/functional-samples/sample.page-redder --browser=edge
65
+ ```
66
+
67
+ </details>
68
+
69
+ ## I have An Extension
70
+
71
+ If you have an existing extension which is using a package manager, you can install the Extension.js package and manually create the scripts used to run your extension.
72
+
73
+ ### See How It Works
74
+
75
+ https://github.com/cezaraugusto/extension/assets/4672033/48694a23-b7f1-4098-9c5d-eff49983739c
76
+
77
+ **Step 1 - Install extension as a `devDependency`**
78
+
79
+ ```bash
80
+ npm install extension@latest --save-dev
81
+ ```
82
+
83
+ **Step 2 - Link your npm scripts with the executable Extension.js commands**
84
+
85
+ ```json
86
+ {
87
+ "scripts": {
88
+ "build": "extension build",
89
+ "dev": "extension dev",
90
+ "preview": "extension preview"
91
+ },
92
+ "devDependencies": {
93
+ // ...other dependencies
94
+ "extension": "latest"
95
+ }
96
+ }
97
+ ```
98
+
99
+ Done. You are all set!
100
+
101
+ - To develop the extension, run `npm run dev`.
102
+ - To build the extension in production mode, run `npm run build`.
103
+ - To visualize the extension in production mode, run `npm run build` and `npm run preview`.
104
+
105
+ ## Using a specific browser for development
106
+
107
+ | <img src="https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome.svg" width="70"> | <img src="https://raw.githubusercontent.com/alrra/browser-logos/main/src/edge/edge.svg" width="70"> | <img src="https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox.svg" width="70"> | <img src="https://raw.githubusercontent.com/alrra/browser-logos/main/src/opera/opera.svg" width="70"> | <img src="https://raw.githubusercontent.com/alrra/browser-logos/main/src/safari/safari.svg" width="70"> | <img src="https://raw.githubusercontent.com/alrra/browser-logos/main/src/chromium/chromium.svg" width="70"> | <img src="https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox.svg" width="70"> | <img src="https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox.svg" width="70"> | <img src="https://raw.githubusercontent.com/alrra/browser-logos/main/src/safari-ios/safari-ios.svg" width="70"> |
108
+ | :-----------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------: |
109
+ | Chrome browser<br>✅ | Edge browser<br>✅ | Firefox browser<br>✅ | Opera browser<br>☑️ | Safari browser<br>❌ | Chromium-based<br>☑️ | Gecko-based<br>☑️ | Firefox (Android)<br>❌ | Safari (iOS)<br>❌ |
110
+
111
+ ### Browser flags and custom binaries
112
+
113
+ Use these flags with `extension dev`, `extension start`, or `extension preview`:
114
+
115
+ - Select a browser: `--browser <chrome | edge | firefox>`
116
+ - Custom Chromium binary: `--chromium-binary <path-to-binary>`
117
+ - Custom Gecko (Firefox) binary: `--gecko-binary <path-to-binary>`
118
+
119
+ Examples:
120
+
121
+ ```bash
122
+ # Chrome (system default)
123
+ npx extension@latest dev --browser=chrome
124
+
125
+ # Edge
126
+ npx extension@latest dev --browser=edge
127
+
128
+ # Custom Chrome/Chromium path
129
+ npx extension@latest dev --chromium-binary "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
130
+
131
+ # Custom Firefox path
132
+ npx extension@latest dev --gecko-binary "/Applications/Firefox.app/Contents/MacOS/firefox"
133
+ ```
134
+
135
+ ## License
136
+
137
+ MIT (c) Cezar Augusto and the Extension.js Authors.
@@ -0,0 +1 @@
1
+ export default function checkUpdates(packageJson: Record<string, any>): Promise<void>;
@@ -0,0 +1,10 @@
1
+ export declare function updateFailed(err: any): string;
2
+ export declare function checkUpdates(packageJson: Record<string, any>, update: {
3
+ latest: string;
4
+ }): string;
5
+ export declare function unsupportedNodeVersion(): string;
6
+ export declare function noURLWithoutStart(argument: string): string;
7
+ export declare function notImplemented(argument: string): string;
8
+ export declare function programUserHelp(): string;
9
+ export declare function unsupportedBrowserFlag(value: string, supported: string[]): string;
10
+ export declare function programAIHelp(): string;
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export type { FileConfig, Manifest } from 'extension-develop';
package/dist/cli.js ADDED
@@ -0,0 +1,345 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __webpack_modules__ = {
4
+ "extension-develop": function(module) {
5
+ module.exports = import("extension-develop").then(function(module) {
6
+ return module;
7
+ });
8
+ }
9
+ };
10
+ var __webpack_module_cache__ = {};
11
+ function __webpack_require__(moduleId) {
12
+ var cachedModule = __webpack_module_cache__[moduleId];
13
+ if (void 0 !== cachedModule) return cachedModule.exports;
14
+ var module = __webpack_module_cache__[moduleId] = {
15
+ exports: {}
16
+ };
17
+ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
18
+ return module.exports;
19
+ }
20
+ (()=>{
21
+ __webpack_require__.n = (module)=>{
22
+ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
23
+ __webpack_require__.d(getter, {
24
+ a: getter
25
+ });
26
+ return getter;
27
+ };
28
+ })();
29
+ (()=>{
30
+ __webpack_require__.d = (exports1, definition)=>{
31
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
32
+ enumerable: true,
33
+ get: definition[key]
34
+ });
35
+ };
36
+ })();
37
+ (()=>{
38
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
39
+ })();
40
+ var __webpack_exports__ = {};
41
+ (()=>{
42
+ const external_commander_namespaceObject = require("commander");
43
+ const external_extension_create_namespaceObject = require("extension-create");
44
+ const external_pintor_namespaceObject = require("pintor");
45
+ var external_pintor_default = /*#__PURE__*/ __webpack_require__.n(external_pintor_namespaceObject);
46
+ function getLoggingPrefix(type) {
47
+ if ('error' === type) return external_pintor_default().red('ERROR');
48
+ if ('warn' === type) return external_pintor_default().brightYellow("\u25BA\u25BA\u25BA");
49
+ if ('info' === type) return external_pintor_default().blue("\u25BA\u25BA\u25BA");
50
+ return external_pintor_default().green("\u25BA\u25BA\u25BA");
51
+ }
52
+ const code = (text)=>external_pintor_default().blue(text);
53
+ const arg = (text)=>external_pintor_default().gray(text);
54
+ function updateFailed(err) {
55
+ return `${getLoggingPrefix('error')} Failed to check for updates.\n${external_pintor_default().red(String((null == err ? void 0 : err.message) || err))}`;
56
+ }
57
+ function checkUpdates(packageJson, update) {
58
+ return `${external_pintor_default().blue('Extension.js')} update available.\nYou are currently using version ${external_pintor_default().gray(String(packageJson.version))}. Latest stable is ${external_pintor_default().gray(String(update.latest))}. Please update to enjoy new features and improvements.`;
59
+ }
60
+ function programUserHelp() {
61
+ return `\n${getLoggingPrefix('info')} ${external_pintor_default().underline('Help center for the Extension.js program')}
62
+
63
+ Usage: extension [command] [options]
64
+
65
+ Notes
66
+ - All high-level commands offer their own \`--help\` with usage and flag lists.
67
+
68
+ Example
69
+ - ${code('extension create --help')} outputs information about the "create" command.
70
+
71
+ Available Commands
72
+ - ${code('extension create ' + arg('<project-name|project-path>'))}
73
+ Creates a new extension from a template (React, TypeScript, Vue, Svelte, etc.)
74
+
75
+ - ${code('extension dev ' + arg('[project-path|remote-url]'))}
76
+ Starts a development server with hot reloading
77
+
78
+ - ${code('extension start ' + arg('[project-path|remote-url]'))}
79
+ Builds and starts the extension in production mode
80
+
81
+ - ${code('extension preview ' + arg('[project-path|remote-url]'))}
82
+ Previews the extension in production mode without building
83
+
84
+ - ${code('extension build ' + arg('[project-path|remote-url]'))}
85
+ Builds the extension for packaging/distribution
86
+
87
+ - ${code('extension cleanup')}
88
+ Cleans up orphaned instances and frees unused ports
89
+
90
+ Common Options
91
+ - ${code('--browser')} ${arg('<chrome|edge|firefox>')} Target browser (default: chrome)
92
+ - ${code('--profile')} ${arg('<path|boolean>')} Browser profile configuration
93
+ - ${code('--polyfill')} ${arg('[boolean]')} Enable/disable cross-browser polyfill
94
+ - ${code('--port')} ${arg('<number>')} Development server port (default: 8080)
95
+ - ${code('--starting-url')} ${arg('<url>')} Initial URL to load in browser
96
+ - ${code('--silent')} ${arg('[boolean]')} Suppress console output during build
97
+
98
+ Source Inspection
99
+ - ${code('--source')} ${arg('<url>')} Open URL and print HTML after content scripts inject
100
+ - ${code('--watch-source')} Monitor rebuild events and print HTML on reloads
101
+
102
+ Browser-Specific Options
103
+ - ${code('--chromium-binary')} ${arg('<path>')} Custom Chromium binary path
104
+ - ${code('--gecko-binary')} ${arg('<path>')} Custom Firefox/Gecko binary path
105
+
106
+ Build Options
107
+ - ${code('--zip')} ${arg('[boolean]')} Create ZIP archive of built extension
108
+ - ${code('--zip-source')} ${arg('[boolean]')} Include source files in ZIP
109
+ - ${code('--zip-filename')} ${arg('<name>')} Custom ZIP filename
110
+
111
+ ${code('extension --help')}
112
+ This command outputs a help file with key command options.
113
+
114
+ AI Assistants
115
+ - For AI-oriented guidance and deep-dive tips, run ${code('extension --ai-help')}
116
+
117
+ Report issues
118
+ - ${external_pintor_default().underline('https://github.com/cezaraugusto/extension/issues/new')}`;
119
+ }
120
+ function unsupportedBrowserFlag(value, supported) {
121
+ return `${getLoggingPrefix('error')} Unsupported --browser value: ${value}. Supported: ${supported.join(', ')}.`;
122
+ }
123
+ function programAIHelp() {
124
+ return `\n${getLoggingPrefix('info')} ${external_pintor_default().gray('Development tips for extension developers and AI assistants')}
125
+
126
+ Browser-Specific Configuration
127
+ - Use browser prefixes in manifest.json for browser-specific fields:
128
+ ${code('{"firefox:manifest": 2, "chrome:manifest": 3}')}
129
+ This applies manifest v2 to Firefox only, v3 to Chrome/Edge.
130
+
131
+ Special Folders for Entrypoints
132
+ - Use special folders to handle entrypoints and assets not declared in manifest.json:
133
+ - ${external_pintor_default().underline(code('public/'))} - Static assets automatically copied to build (resolves to output root)
134
+ - ${external_pintor_default().underline(code('pages/'))} - HTML files not declared in manifest (e.g., welcome pages)
135
+ - ${external_pintor_default().underline(code("scripts/"))} - JavaScript files not declared in manifest (e.g., executable scripts)
136
+
137
+ Shadow DOM for Content Scripts
138
+ - Add ${code('use shadow-dom')} directive to content scripts for style isolation
139
+ - Automatically creates ${code('#extension-root')} element with shadow DOM
140
+ - All CSS imports are automatically injected into shadow DOM
141
+ - Prevents style conflicts with host page
142
+
143
+ Environment Variables
144
+ - Use ${code(arg('EXTENSION_PUBLIC_*'))} prefix for variables accessible in extension code
145
+ - Supported in both ${code('process.env')} and ${code('import.meta.env')}
146
+ - Environment file priority: ${external_pintor_default().underline(code(arg('.env.{browser}.{mode}')))} > ${external_pintor_default().underline(code(arg('.env.{browser}')))} > ${external_pintor_default().underline(code(arg('.env.{mode}')))} > ${external_pintor_default().underline(code(arg('.env')))}
147
+ - Example: ${code(arg('EXTENSION_PUBLIC_API_KEY=your_key'))}
148
+
149
+ Available Templates
150
+ - ${external_pintor_default().green('Frameworks')}: ${code(arg('react'))}, ${code(arg('preact'))}, ${code(arg('vue'))}, ${code(arg('svelte'))}
151
+ - ${external_pintor_default().green('Languages')}: ${code(arg("javascript"))}, ${code(arg("typescript"))}
152
+ - ${external_pintor_default().green('Contexts')}: ${code(arg('content'))} (content scripts), ${code(arg('new'))} (new tab), ${code(arg('action'))} (popup)
153
+ - ${external_pintor_default().green('Styling')}: ${code(arg('tailwind'))}, ${code(arg('sass'))}, ${code(arg('less'))}
154
+ - ${external_pintor_default().green('Configs')}: ${code(arg('eslint'))}, ${code(arg('prettier'))}, ${code(arg('stylelint'))}
155
+
156
+ Webpack/Rspack Configuration
157
+ - Create ${external_pintor_default().underline(code(arg('extension.config.js')))} for custom webpack configuration
158
+ - Function receives base config, return modified config
159
+ - Supports all webpack/rspack loaders and plugins
160
+ - Example:
161
+ ${code('export default {')}
162
+ ${code(' config: (config) => {')}
163
+ ${code(" config.module.rules.push({ test: /\\.svg$/, use: ['@svgr/webpack'] })")}
164
+ ${code(' return config')}
165
+ ${code(' }')}
166
+ ${code('}')}
167
+
168
+ Managed Dependencies (Important)
169
+ - ${external_pintor_default().green('Do not add')} packages that ${external_pintor_default().blue('Extension.js')} already ships in its own toolchain.
170
+ - The guard only triggers when a managed package is declared in your ${code('package.json')} ${external_pintor_default().gray('and')} is referenced in your ${external_pintor_default().underline(code('extension.config.js'))}.
171
+ - In that case, the program will ${external_pintor_default().red('print an error and abort')} to avoid version conflicts.
172
+ - Remove the duplicate from your project ${code('package.json')} or avoid referencing it in ${external_pintor_default().underline(code('extension.config.js'))} and rely on the built-in version instead.
173
+ - If you truly need a different version, open an issue so we can evaluate a safe upgrade.
174
+
175
+ Framework-Specific Configuration
176
+ - Create ${external_pintor_default().underline(code(arg('vue.loader.js')))} for Vue-specific loader configuration
177
+ - Create ${external_pintor_default().underline(code(arg('svelte.loader.js')))} for Svelte-specific loader configuration
178
+ - Automatically detected and used by Extension.js
179
+ - Example svelte.loader.js:
180
+ ${code('module.exports = {')}
181
+ ${code(' preprocess: require("svelte-preprocess")({')}
182
+ ${code(" typescript: true")}
183
+ ${code(' })')}
184
+ ${code('}')}
185
+
186
+ Hot Module Replacement (HMR)
187
+ - Automatically enabled in development mode
188
+ - CSS changes trigger automatic style updates
189
+ - React/Preact/Vue/Svelte components hot reload
190
+ - Content scripts automatically re-inject on changes
191
+ - Service workers, _locales and manifest changes reload the extension
192
+
193
+ Source Inspection & Real-Time Monitoring
194
+ - Use ${code('--source')} ${arg('<url>')} to inspect page HTML after content script injection
195
+ - Use ${code('--watch-source')} to monitor real-time changes in stdout
196
+ - Automatically enables Chrome remote debugging (port 9222) when source inspection is active
197
+ - Extracts Shadow DOM content from ${code('#extension-root')} elements
198
+ - Perfect for debugging content script behavior and style injection
199
+ - Example: ${code('extension dev --source=' + arg('https://example.com') + ' --watch-source')}
200
+
201
+ Non-Destructive Testing in CI
202
+ - Prefer ${code('EXTENSION_ENV=development')} to copy local templates and avoid network.
203
+ - Reuse Playwright's Chromium via ${code('--chromium-binary')} path when available.
204
+ - Set ${code(arg('EXTENSION_AUTO_EXIT_MS'))} and ${code(arg('EXTENSION_FORCE_KILL_MS'))} for non-interactive dev sessions.
205
+
206
+ File Watching & HMR Examples
207
+ - Content script JS/TS changes trigger reinjection; CSS changes update styles live.
208
+ - For watch-source HTML prints, update a visible string in ${code("content/scripts.*")} and assert it appears in stdout.
209
+
210
+ Troubleshooting
211
+ - If HTML is not printed, ensure ${code('--source')} is provided and browser launched with debugging port.
212
+ - Use ${code('--silent true')} during builds to reduce noise; logs still surface errors.
213
+ - When ports conflict, pass ${code('--port 0')} to auto-select an available port.
214
+
215
+ Non-Interactive / Auto Mode (CI)
216
+ - Set ${code(arg('EXTENSION_AUTO_EXIT_MS'))} to enable self-termination after N milliseconds.
217
+ Useful when ${code('pnpm extension dev')} would otherwise hang under Rspack watch.
218
+ Example: ${code(arg('EXTENSION_AUTO_EXIT_MS=6000'))} pnpm extension dev ./templates/react --browser chrome --source ${arg('https://example.com')}
219
+ - Optional: ${code(arg('EXTENSION_FORCE_KILL_MS'))} to hard-exit after N ms as a fallback (defaults to auto-exit + 4000).
220
+
221
+ Cross-Browser Compatibility
222
+ - Use ${code('--polyfill')} flag to enable webextension-polyfill
223
+ - Automatically handles browser API differences
224
+ - Supports Chrome, Edge, Firefox with single codebase`;
225
+ }
226
+ const external_update_check_namespaceObject = require("update-check");
227
+ var external_update_check_default = /*#__PURE__*/ __webpack_require__.n(external_update_check_namespaceObject);
228
+ function isStableVersion(version) {
229
+ return !/[a-zA-Z]/.test(version);
230
+ }
231
+ async function check_updates_checkUpdates(packageJson) {
232
+ let update = null;
233
+ try {
234
+ update = await external_update_check_default()(packageJson);
235
+ } catch (err) {
236
+ if ('development' === process.env.EXTENSION_ENV) console.error(updateFailed(err));
237
+ }
238
+ if (update && isStableVersion(update.latest)) console.log(checkUpdates(packageJson, update));
239
+ }
240
+ var package_namespaceObject = JSON.parse('{"license":"MIT","repository":{"type":"git","url":"https://github.com/extension-js/extension.js.git","directory":"programs/cli"},"engines":{"node":">=18"},"exports":{".":{"types":"./dist/cli.d.ts","import":"./dist/cli.js","require":"./dist/cli.js"}},"main":"./dist/cli.js","types":"./dist/cli.d.ts","files":["dist","types"],"bin":{"extension":"./dist/cli.js"},"name":"extension","version":"2.0.4","description":"Create cross-browser extensions with no build configuration.","author":{"name":"Cezar Augusto","email":"boss@cezaraugusto.net","url":"https://cezaraugusto.com"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"scripts":{"watch":"rslib build --watch","compile":"rslib build","clean":"rm -rf dist","test":"echo \\"Note: no test specified\\" && exit 0","test:cli":"vitest run"},"keywords":["zero-config","build","develop","browser","extension","chrome extension","edge extension","firefox extension","safari extension","web","react","typescript","webextension","browser-extension","chrome-extension","firefox-addon","edge-extension","safari-web-extension","manifest-v3","mv3","cross-browser","content-script","background-script","devtools","create-extension","scaffold","starter-template","boilerplate","cli"],"dependencies":{"@types/chrome":"^0.0.287","@types/node":"^22.10.1","@types/react":"^19.0.1","@types/react-dom":"^19.0.1","@types/webextension-polyfill":"0.12.3","commander":"^12.1.0","extension-create":"workspace:*","extension-develop":"workspace:*","pintor":"0.3.0","semver":"^7.6.3","update-check":"^1.5.4","webextension-polyfill":"^0.12.0"},"devDependencies":{"@rslib/core":"^0.6.9","@types/mock-fs":"^4.13.4","@types/semver":"^7.5.8","mock-fs":"^5.4.1","tsconfig":"*","typescript":"5.7.2","vitest":"3.2.2"}}');
241
+ function parseOptionalBoolean(value) {
242
+ if (void 0 === value) return true;
243
+ const normalized = String(value).trim().toLowerCase();
244
+ return ![
245
+ 'false',
246
+ '0',
247
+ 'no',
248
+ 'off'
249
+ ].includes(normalized);
250
+ }
251
+ check_updates_checkUpdates(package_namespaceObject);
252
+ const extensionJs = external_commander_namespaceObject.program;
253
+ const vendors = (browser)=>'all' === browser ? 'chrome,edge,firefox'.split(',') : browser.split(',');
254
+ function validateVendorsOrExit(vendorsList) {
255
+ const supported = [
256
+ 'chrome',
257
+ 'edge',
258
+ 'firefox'
259
+ ];
260
+ for (const v of vendorsList)if (!supported.includes(v)) {
261
+ console.error(unsupportedBrowserFlag(v, supported));
262
+ process.exit(1);
263
+ }
264
+ }
265
+ extensionJs.name(package_namespaceObject.name).description(package_namespaceObject.description).version(package_namespaceObject.version).option('--ai-help', 'show AI-assistant oriented help and tips').addHelpText('after', programUserHelp());
266
+ extensionJs.command('create').arguments('<project-name|project-path>').usage('create <project-name|project-path> [options]').description('Creates a new extension.').option('-t, --template <template-name>', 'specify a template for the created project').option('--install [boolean]', 'whether or not to install the dependencies after creating the project (disabled by default)', parseOptionalBoolean, false).action(async function(pathOrRemoteUrl, { template, install }) {
267
+ await (0, external_extension_create_namespaceObject.extensionCreate)(pathOrRemoteUrl, {
268
+ template,
269
+ install,
270
+ cliVersion: package_namespaceObject.version
271
+ });
272
+ });
273
+ extensionJs.command('dev').arguments('[project-path|remote-url]').usage('dev [project-path|remote-url] [options]').description('Starts the development server (development mode)').option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | edge | firefox>', 'specify a browser to preview your extension in production mode. Defaults to `chrome`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`').option('--open [boolean]', 'whether or not to open the browser automatically. Defaults to `true`').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').option('--source [url]', "opens the provided URL in Chrome and prints the full, live HTML of the page after content scripts are injected").option('--watch-source', 'continuously monitors rebuild events and prints updated HTML whenever the extension reloads and reinjects into the page').action(async function(pathOrRemoteUrl, { browser = 'chrome', ...devOptions }) {
274
+ const list = vendors(browser);
275
+ validateVendorsOrExit(list);
276
+ const { extensionDev } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "extension-develop"));
277
+ for (const vendor of list){
278
+ var _devOptions_polyfill;
279
+ await extensionDev(pathOrRemoteUrl, {
280
+ ...devOptions,
281
+ profile: devOptions.profile,
282
+ browser: vendor,
283
+ chromiumBinary: devOptions.chromiumBinary,
284
+ geckoBinary: devOptions.geckoBinary,
285
+ polyfill: (null == (_devOptions_polyfill = devOptions.polyfill) ? void 0 : _devOptions_polyfill.toString()) !== 'false',
286
+ open: devOptions.open,
287
+ startingUrl: devOptions.startingUrl,
288
+ source: devOptions.source,
289
+ watchSource: devOptions.watchSource
290
+ });
291
+ }
292
+ });
293
+ extensionJs.command('start').arguments('[project-path|remote-url]').usage('start [project-path|remote-url] [options]').description('Starts the development server (production mode)').option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | edge | firefox>', 'specify a browser to preview your extension in production mode. Defaults to `chrome`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `true`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').action(async function(pathOrRemoteUrl, { browser = 'chrome', ...startOptions }) {
294
+ const list = vendors(browser);
295
+ validateVendorsOrExit(list);
296
+ const { extensionStart } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "extension-develop"));
297
+ for (const vendor of list)await extensionStart(pathOrRemoteUrl, {
298
+ mode: 'production',
299
+ profile: startOptions.profile,
300
+ browser: vendor,
301
+ chromiumBinary: startOptions.chromiumBinary,
302
+ geckoBinary: startOptions.geckoBinary,
303
+ startingUrl: startOptions.startingUrl
304
+ });
305
+ });
306
+ extensionJs.command('preview').arguments('[project-name]').usage('preview [path-to-remote-extension] [options]').description('Preview the extension in production mode').option('--profile <path-to-file | boolean>', 'what path to use for the browser profile. A boolean value of false sets the profile to the default user profile. Defaults to a fresh profile').option('--browser <chrome | edge | firefox>', 'specify a browser to preview your extension in production mode. Defaults to `chrome`').option('--chromium-binary <path-to-binary>', 'specify a path to the Chromium binary. This option overrides the --browser setting. Defaults to the system default').option('--gecko-binary <path-to-binary>', 'specify a path to the Gecko binary. This option overrides the --browser setting. Defaults to the system default').option('--starting-url <url>', 'specify the starting URL for the browser. Defaults to `undefined`').option('--port <port>', 'specify the port to use for the development server. Defaults to `8080`').action(async function(pathOrRemoteUrl, { browser = 'chrome', ...previewOptions }) {
307
+ const list = vendors(browser);
308
+ validateVendorsOrExit(list);
309
+ const { extensionPreview } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "extension-develop"));
310
+ for (const vendor of list)await extensionPreview(pathOrRemoteUrl, {
311
+ mode: 'production',
312
+ profile: previewOptions.profile,
313
+ browser: vendor,
314
+ chromiumBinary: previewOptions.chromiumBinary,
315
+ geckoBinary: previewOptions.geckoBinary,
316
+ startingUrl: previewOptions.startingUrl
317
+ });
318
+ });
319
+ extensionJs.command('build').arguments('[project-name]').usage('build [path-to-remote-extension] [options]').description('Builds the extension for production').option('--browser <chrome | edge | firefox>', 'specify a browser to preview your extension in production mode. Defaults to `chrome`').option('--polyfill [boolean]', 'whether or not to apply the cross-browser polyfill. Defaults to `false`').option('--zip [boolean]', 'whether or not to compress the extension into a ZIP file. Defaults to `false`').option('--zip-source [boolean]', 'whether or not to include the source files in the ZIP file. Defaults to `false`').option('--zip-filename <string>', 'specify the name of the ZIP file. Defaults to the extension name and version').option('--silent [boolean]', 'whether or not to open the browser automatically. Defaults to `false`').action(async function(pathOrRemoteUrl, { browser = 'chrome', ...buildOptions }) {
320
+ const list = vendors(browser);
321
+ validateVendorsOrExit(list);
322
+ const { extensionBuild } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "extension-develop"));
323
+ for (const vendor of list)await extensionBuild(pathOrRemoteUrl, {
324
+ browser: vendor,
325
+ polyfill: buildOptions.polyfill,
326
+ zip: buildOptions.zip,
327
+ zipSource: buildOptions.zipSource,
328
+ zipFilename: buildOptions.zipFilename,
329
+ silent: buildOptions.silent
330
+ });
331
+ });
332
+ extensionJs.command('cleanup').description('Clean up orphaned instances and free unused ports').action(async function() {
333
+ const { cleanupCommand } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "extension-develop"));
334
+ await cleanupCommand();
335
+ });
336
+ extensionJs.on('option:ai-help', function() {
337
+ console.log(programAIHelp());
338
+ process.exit(0);
339
+ });
340
+ extensionJs.parse();
341
+ })();
342
+ for(var __webpack_i__ in __webpack_exports__)exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
343
+ Object.defineProperty(exports, '__esModule', {
344
+ value: true
345
+ });
@@ -0,0 +1,3 @@
1
+ import type { RslibConfig } from '@rslib/core';
2
+ declare const _default: RslibConfig;
3
+ export default _default;
File without changes
@@ -0,0 +1,2 @@
1
+ declare const _default: import("vite").UserConfig;
2
+ export default _default;
package/package.json ADDED
@@ -0,0 +1,100 @@
1
+ {
2
+ "license": "MIT",
3
+ "repository": {
4
+ "type": "git",
5
+ "url": "https://github.com/extension-js/extension.js.git",
6
+ "directory": "programs/cli"
7
+ },
8
+ "engines": {
9
+ "node": ">=18"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/cli.d.ts",
14
+ "import": "./dist/cli.js",
15
+ "require": "./dist/cli.js"
16
+ }
17
+ },
18
+ "main": "./dist/cli.js",
19
+ "types": "./dist/cli.d.ts",
20
+ "files": [
21
+ "dist",
22
+ "types"
23
+ ],
24
+ "bin": {
25
+ "extension": "./dist/cli.js"
26
+ },
27
+ "name": "extension",
28
+ "version": "0.0.0-next-20250825144602",
29
+ "description": "Create cross-browser extensions with no build configuration.",
30
+ "author": {
31
+ "name": "Cezar Augusto",
32
+ "email": "boss@cezaraugusto.net",
33
+ "url": "https://cezaraugusto.com"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public",
37
+ "registry": "https://registry.npmjs.org"
38
+ },
39
+ "keywords": [
40
+ "zero-config",
41
+ "build",
42
+ "develop",
43
+ "browser",
44
+ "extension",
45
+ "chrome extension",
46
+ "edge extension",
47
+ "firefox extension",
48
+ "safari extension",
49
+ "web",
50
+ "react",
51
+ "typescript",
52
+ "webextension",
53
+ "browser-extension",
54
+ "chrome-extension",
55
+ "firefox-addon",
56
+ "edge-extension",
57
+ "safari-web-extension",
58
+ "manifest-v3",
59
+ "mv3",
60
+ "cross-browser",
61
+ "content-script",
62
+ "background-script",
63
+ "devtools",
64
+ "create-extension",
65
+ "scaffold",
66
+ "starter-template",
67
+ "boilerplate",
68
+ "cli"
69
+ ],
70
+ "dependencies": {
71
+ "@types/chrome": "^0.0.287",
72
+ "@types/node": "^22.10.1",
73
+ "@types/react": "^19.0.1",
74
+ "@types/react-dom": "^19.0.1",
75
+ "@types/webextension-polyfill": "0.12.3",
76
+ "commander": "^12.1.0",
77
+ "pintor": "0.3.0",
78
+ "semver": "^7.6.3",
79
+ "update-check": "^1.5.4",
80
+ "webextension-polyfill": "^0.12.0",
81
+ "extension-create": "2.0.1",
82
+ "extension-develop": "0.0.0-next-20250825144602"
83
+ },
84
+ "devDependencies": {
85
+ "@rslib/core": "^0.6.9",
86
+ "@types/mock-fs": "^4.13.4",
87
+ "@types/semver": "^7.5.8",
88
+ "mock-fs": "^5.4.1",
89
+ "tsconfig": "*",
90
+ "typescript": "5.7.2",
91
+ "vitest": "3.2.2"
92
+ },
93
+ "scripts": {
94
+ "watch": "rslib build --watch",
95
+ "compile": "rslib build",
96
+ "clean": "rm -rf dist",
97
+ "test": "echo \"Note: no test specified\" && exit 0",
98
+ "test:cli": "vitest run"
99
+ }
100
+ }
@@ -0,0 +1,7 @@
1
+ type CSSContentData = Readonly<Record<string, string>>
2
+
3
+ declare module '*.css' {
4
+ const content: CSSContentData
5
+
6
+ export default content
7
+ }
@@ -0,0 +1,19 @@
1
+ type CSSModuleData = Readonly<Record<string, string>>
2
+
3
+ declare module '*.module.css' {
4
+ const content: CSSModuleData
5
+
6
+ export default content
7
+ }
8
+
9
+ declare module '*.module.scss' {
10
+ const content: CSSModuleData
11
+
12
+ export default content
13
+ }
14
+
15
+ declare module '*.module.sass' {
16
+ const content: CSSModuleData
17
+
18
+ export default content
19
+ }
@@ -0,0 +1,58 @@
1
+ declare module '*.png' {
2
+ const content: string
3
+
4
+ export default content
5
+ }
6
+
7
+ declare module '*.jpg' {
8
+ const content: string
9
+
10
+ export default content
11
+ }
12
+
13
+ declare module '*.jpeg' {
14
+ const content: string
15
+
16
+ export default content
17
+ }
18
+
19
+ declare module '*.gif' {
20
+ const content: string
21
+
22
+ export default content
23
+ }
24
+
25
+ declare module '*.webp' {
26
+ const content: string
27
+
28
+ export default content
29
+ }
30
+
31
+ declare module '*.avif' {
32
+ const content: string
33
+
34
+ export default content
35
+ }
36
+
37
+ declare module '*.ico' {
38
+ const content: string
39
+
40
+ export default content
41
+ }
42
+
43
+ declare module '*.bmp' {
44
+ const content: string
45
+
46
+ export default content
47
+ }
48
+
49
+ declare module '*.svg' {
50
+ /**
51
+ * Use `any` to avoid conflicts with
52
+ * `@svgr/webpack` plugin or
53
+ * `babel-plugin-inline-react-svg` plugin.
54
+ */
55
+ const content: any
56
+
57
+ export default content
58
+ }
@@ -0,0 +1,60 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="chrome" />
3
+ /// <reference types="./js-frameworks.d.ts" />
4
+ /// <reference path="./css-content.d.ts" />
5
+ /// <reference path="./css-modules.d.ts" />
6
+ /// <reference path="./images.d.ts" />
7
+
8
+ type ExtensionBrowser =
9
+ | 'chrome'
10
+ | 'edge'
11
+ | 'firefox'
12
+ | 'chromium-based'
13
+ | 'gecko-based'
14
+
15
+ type ExtensionMode = 'development' | 'production'
16
+
17
+ interface ExtensionEnv {
18
+ EXTENSION_BROWSER: ExtensionBrowser
19
+ EXTENSION_MODE: ExtensionMode
20
+ EXTENSION_PUBLIC_BROWSER: ExtensionBrowser
21
+ EXTENSION_PUBLIC_MODE: ExtensionMode
22
+ EXTENSION_PUBLIC_DESCRIPTION_TEXT: string
23
+ EXTENSION_PUBLIC_OPENAI_API_KEY: string
24
+ EXTENSION_ENV: ExtensionMode
25
+ }
26
+
27
+ // Global augmentations
28
+ declare global {
29
+ namespace NodeJS {
30
+ interface ProcessEnv extends ExtensionEnv {
31
+ [key: string]: string | undefined
32
+ }
33
+ }
34
+
35
+ interface ImportMetaEnv extends ExtensionEnv {
36
+ [key: string]: string | undefined
37
+ }
38
+
39
+ interface ImportMeta {
40
+ readonly env: ImportMetaEnv
41
+ readonly webpackHot?: {
42
+ accept: (module?: string | string[], callback?: () => void) => void
43
+ dispose: (callback: () => void) => void
44
+ }
45
+ url: string
46
+ }
47
+
48
+ interface Window {
49
+ /**
50
+ * @deprecated
51
+ * @description
52
+ * This is how Extension.js used to inject the shadow root into the window object.
53
+ * Use the shadowRoot reference from the content script instead.
54
+ */
55
+ __EXTENSION_SHADOW_ROOT__: ShadowRoot
56
+ }
57
+ }
58
+
59
+ // This export is needed for TypeScript to treat this file as a module
60
+ export {}
package/types/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="react" />
3
+ /// <reference types="react-dom" />
4
+ /// <reference types="chrome" />
5
+ /// <reference path="./css-content.d.ts" />
6
+ /// <reference path="./css-modules.d.ts" />
7
+ /// <reference path="./images.d.ts" />
@@ -0,0 +1,3 @@
1
+ /// <reference types="react" />
2
+ /// <reference types="react-dom" />
3
+ /// <reference types="svelte" />
@@ -0,0 +1 @@
1
+ /// <reference types="webextension-polyfill" />