browser-extension-manager 1.3.48 → 1.4.0
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/README.md +109 -70
- package/dist/background.js +4 -0
- package/dist/build.js +3 -0
- package/dist/cli.js +1 -0
- package/dist/commands/test.js +55 -0
- package/dist/content.js +4 -0
- package/dist/defaults/CLAUDE.md +66 -6
- package/dist/gulp/tasks/defaults.js +20 -4
- package/dist/gulp/tasks/distribute.js +36 -33
- package/dist/gulp/tasks/html.js +82 -79
- package/dist/gulp/tasks/utils/template-transform.js +35 -35
- package/dist/offscreen.js +4 -0
- package/dist/options.js +4 -0
- package/dist/page.js +4 -0
- package/dist/popup.js +4 -0
- package/dist/sidepanel.js +4 -0
- package/dist/test/assert.js +120 -0
- package/dist/test/fixtures/consumer-extension/dist/background.js +15 -0
- package/dist/test/fixtures/consumer-extension/dist/manifest.json +20 -0
- package/dist/test/fixtures/consumer-extension/dist/options.html +10 -0
- package/dist/test/fixtures/consumer-extension/dist/popup.html +11 -0
- package/dist/test/harness/extension/background.js +26 -0
- package/dist/test/harness/extension/manifest.json +27 -0
- package/dist/test/harness/extension/options.html +12 -0
- package/dist/test/harness/extension/popup.html +12 -0
- package/dist/test/harness/extension/sidepanel.html +12 -0
- package/dist/test/index.js +63 -0
- package/dist/test/runner.js +407 -0
- package/dist/test/runners/boot.js +201 -0
- package/dist/test/runners/chromium.js +399 -0
- package/dist/test/suites/background/messaging.test.js +42 -0
- package/dist/test/suites/background/storage.test.js +44 -0
- package/dist/test/suites/background/sw-context.test.js +47 -0
- package/dist/test/suites/boot/extension-loads.test.js +56 -0
- package/dist/test/suites/build/affiliatizer.test.js +63 -0
- package/dist/test/suites/build/cli.test.js +123 -0
- package/dist/test/suites/build/expect.test.js +47 -0
- package/dist/test/suites/build/exports.test.js +52 -0
- package/dist/test/suites/build/extension-fallback.test.js +41 -0
- package/dist/test/suites/build/logger-lite.test.js +59 -0
- package/dist/test/suites/build/manager.test.js +162 -0
- package/dist/test/suites/build/mode-helpers.test.js +96 -0
- package/dist/test/suites/view/options-and-sidepanel.test.js +14 -0
- package/dist/test/suites/view/popup-context.test.js +51 -0
- package/dist/test/suites/view/sidepanel.test.js +14 -0
- package/dist/utils/mode-helpers.js +92 -0
- package/package.json +16 -13
package/README.md
CHANGED
|
@@ -5,129 +5,168 @@
|
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<img src="https://img.shields.io/github/package-json/v/itw-creative-works/
|
|
8
|
+
<img src="https://img.shields.io/github/package-json/v/itw-creative-works/browser-extension-manager.svg">
|
|
9
9
|
<br>
|
|
10
|
-
<img src="https://img.shields.io/librariesio/release/npm/
|
|
11
|
-
<img src="https://img.shields.io/bundlephobia/min/
|
|
12
|
-
<img src="https://img.shields.io/
|
|
13
|
-
<img src="https://img.shields.io/
|
|
14
|
-
<img src="https://img.shields.io/
|
|
15
|
-
<img src="https://img.shields.io/
|
|
16
|
-
<img src="https://img.shields.io/github/
|
|
17
|
-
<img src="https://img.shields.io/github/contributors/itw-creative-works/ultimate-browser-extension.svg">
|
|
18
|
-
<img src="https://img.shields.io/github/last-commit/itw-creative-works/ultimate-browser-extension.svg">
|
|
10
|
+
<img src="https://img.shields.io/librariesio/release/npm/browser-extension-manager.svg">
|
|
11
|
+
<img src="https://img.shields.io/bundlephobia/min/browser-extension-manager.svg">
|
|
12
|
+
<img src="https://img.shields.io/npm/dm/browser-extension-manager.svg">
|
|
13
|
+
<img src="https://img.shields.io/node/v/browser-extension-manager.svg">
|
|
14
|
+
<img src="https://img.shields.io/github/license/itw-creative-works/browser-extension-manager.svg">
|
|
15
|
+
<img src="https://img.shields.io/github/contributors/itw-creative-works/browser-extension-manager.svg">
|
|
16
|
+
<img src="https://img.shields.io/github/last-commit/itw-creative-works/browser-extension-manager.svg">
|
|
19
17
|
<br>
|
|
20
18
|
<br>
|
|
21
|
-
<a href="https://itwcreativeworks.com">Site</a> | <a href="https://www.npmjs.com/package/
|
|
19
|
+
<a href="https://itwcreativeworks.com">Site</a> | <a href="https://www.npmjs.com/package/browser-extension-manager">NPM Module</a> | <a href="https://github.com/itw-creative-works/browser-extension-manager">GitHub Repo</a>
|
|
22
20
|
<br>
|
|
23
21
|
<br>
|
|
24
|
-
<strong>
|
|
22
|
+
<strong>Browser Extension Manager</strong> is a framework for building modern cross-browser extensions. One-line bootstrap per context, component-based architecture, multi-browser build pipeline, cross-context auth, auto-translation across 16 languages, and a four-layer test framework.
|
|
25
23
|
</p>
|
|
26
24
|
|
|
27
25
|
## 🦄 Features
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
|
|
27
|
+
- **Build for any browser**: Chrome, Firefox, Edge, Opera, Brave
|
|
28
|
+
- **Component architecture**: seven contexts (background / popup / options / sidepanel / content / pages / offscreen) each with view + styles + script
|
|
29
|
+
- **One-line bootstrap per context** with cross-browser API wrapper
|
|
30
|
+
- **Cross-context auth sync**: sign-in in one tab is reflected in all open contexts (no `chrome.storage` needed)
|
|
31
|
+
- **Auto-translation** to 16 languages via Claude CLI on every build
|
|
32
|
+
- **Four-layer test framework**: build / background / view / boot — real Chromium, real MV3 service worker, real consumer extensions
|
|
33
|
+
- **Multi-browser packaging + auto-publish** to Chrome / Firefox / Edge stores from one command
|
|
34
|
+
- **Theme system**: Bootstrap 5 + Classy (custom design system), or roll your own
|
|
35
|
+
- **SCSS load paths**: `@use 'browser-extension-manager'` / `@use 'theme'` Just Work — no relative-path hell
|
|
30
36
|
|
|
31
37
|
## 🚀 Getting started
|
|
32
|
-
|
|
38
|
+
|
|
39
|
+
1. [Create a repo](https://github.com/itw-creative-works/ultimate-browser-extension/generate) from the **Ultimate Browser Extension** template (or `npm i browser-extension-manager` in an existing project).
|
|
33
40
|
2. Clone the repo to your local machine.
|
|
34
|
-
3.
|
|
41
|
+
3. Set up + run:
|
|
42
|
+
```bash
|
|
43
|
+
npm install
|
|
44
|
+
npx bxm setup
|
|
45
|
+
npm start
|
|
46
|
+
```
|
|
47
|
+
4. Open Chrome and navigate to `chrome://extensions`.
|
|
48
|
+
5. Enable **Developer mode**.
|
|
49
|
+
6. Click **Load unpacked** and select the `packaged/chromium/raw` folder in your project.
|
|
50
|
+
7. Your extension is loaded and live-reloads on source changes.
|
|
51
|
+
|
|
52
|
+
## 📦 Sync with the template
|
|
53
|
+
|
|
54
|
+
Run `npx bxm setup` again to pull the latest framework defaults. Files you've edited are preserved; only missing or framework-owned files update.
|
|
55
|
+
|
|
56
|
+
## 🧪 Testing
|
|
57
|
+
|
|
58
|
+
BXM ships a built-in four-layer test framework. Write tests under `test/<layer>/*.test.js` and run with:
|
|
59
|
+
|
|
35
60
|
```bash
|
|
36
|
-
|
|
37
|
-
npx bxm
|
|
38
|
-
|
|
61
|
+
npx bxm test # all layers
|
|
62
|
+
npx bxm test --layer build # build layer only (plain Node, fast)
|
|
63
|
+
npx bxm test --layer boot # real-Chromium end-to-end test
|
|
39
64
|
```
|
|
40
|
-
4. Open your browser and navigate to `chrome://extensions` (or the equivalent for your browser).
|
|
41
|
-
5. Enable **Developer mode**.
|
|
42
|
-
6. Click on **Load unpacked** and select the `dist` folder in your project directory.
|
|
43
|
-
7. Your extension should now be loaded and ready to use!
|
|
44
65
|
|
|
45
|
-
|
|
46
|
-
|
|
66
|
+
Test files use Jest-compatible matchers:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
// test/build/manifest.test.js
|
|
70
|
+
const Manager = require('browser-extension-manager/build');
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
layer: 'build',
|
|
74
|
+
description: 'manifest is valid MV3',
|
|
75
|
+
run: (ctx) => {
|
|
76
|
+
const m = Manager.getManifest();
|
|
77
|
+
ctx.expect(m.manifest_version).toBe(3);
|
|
78
|
+
ctx.expect(m.permissions).toContain('storage');
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Full guide: [docs/test-framework.md](docs/test-framework.md). End-to-end "did my packaged extension actually boot in Chrome?" tests: [docs/test-boot-layer.md](docs/test-boot-layer.md).
|
|
84
|
+
|
|
85
|
+
## 🌐 Auto-translation
|
|
86
|
+
|
|
87
|
+
When you run `npm run build`, BXM auto-translates `src/_locales/en/messages.json` to 16 languages via Claude CLI:
|
|
47
88
|
|
|
48
|
-
## 🌐 Automatic Translation
|
|
49
|
-
When you run `npm run build`, BXM automatically translates your `src/_locales/en/messages.json` to 16 languages using Claude CLI:
|
|
50
89
|
`zh`, `es`, `hi`, `ar`, `pt`, `ru`, `ja`, `de`, `fr`, `ko`, `ur`, `id`, `bn`, `tl`, `vi`, `it`
|
|
51
90
|
|
|
52
|
-
Only missing translations are generated
|
|
91
|
+
Only missing translations are generated — existing translations are preserved. Full guide: [docs/translations.md](docs/translations.md).
|
|
53
92
|
|
|
54
93
|
## 🌎 Publishing your extension
|
|
55
94
|
|
|
56
|
-
### Manual
|
|
57
|
-
|
|
58
|
-
|
|
95
|
+
### Manual upload
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm run build
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Upload the `.zip` files under `packaged/<browser>/` to each browser's extension store.
|
|
59
102
|
|
|
60
|
-
### Automatic
|
|
61
|
-
BXM can automatically publish to Chrome, Firefox, and Edge stores when `BXM_IS_PUBLISH=true`:
|
|
103
|
+
### Automatic publishing
|
|
62
104
|
|
|
63
105
|
```bash
|
|
64
106
|
BXM_IS_PUBLISH=true npm run build
|
|
65
107
|
```
|
|
66
108
|
|
|
67
|
-
|
|
109
|
+
Add store credentials to your `.env`:
|
|
68
110
|
|
|
69
111
|
```bash
|
|
70
112
|
# Chrome Web Store
|
|
71
|
-
CHROME_EXTENSION_ID="
|
|
72
|
-
CHROME_CLIENT_ID="
|
|
73
|
-
CHROME_CLIENT_SECRET="
|
|
74
|
-
CHROME_REFRESH_TOKEN="
|
|
113
|
+
CHROME_EXTENSION_ID="..."
|
|
114
|
+
CHROME_CLIENT_ID="..."
|
|
115
|
+
CHROME_CLIENT_SECRET="..."
|
|
116
|
+
CHROME_REFRESH_TOKEN="..."
|
|
75
117
|
|
|
76
118
|
# Firefox Add-ons
|
|
77
|
-
FIREFOX_EXTENSION_ID="
|
|
78
|
-
FIREFOX_API_KEY="
|
|
79
|
-
FIREFOX_API_SECRET="
|
|
119
|
+
FIREFOX_EXTENSION_ID="..."
|
|
120
|
+
FIREFOX_API_KEY="..."
|
|
121
|
+
FIREFOX_API_SECRET="..."
|
|
80
122
|
|
|
81
123
|
# Microsoft Edge Add-ons
|
|
82
|
-
EDGE_PRODUCT_ID="
|
|
83
|
-
EDGE_CLIENT_ID="
|
|
84
|
-
EDGE_API_KEY="
|
|
124
|
+
EDGE_PRODUCT_ID="..."
|
|
125
|
+
EDGE_CLIENT_ID="..."
|
|
126
|
+
EDGE_API_KEY="..."
|
|
85
127
|
```
|
|
86
128
|
|
|
87
|
-
Only stores with configured credentials
|
|
129
|
+
Only stores with configured credentials get published to. Full guide: [docs/publishing.md](docs/publishing.md).
|
|
88
130
|
|
|
89
131
|
## 🔐 Authentication
|
|
90
132
|
|
|
91
|
-
BXM provides built-in authentication that syncs across all extension contexts (popup, options,
|
|
92
|
-
|
|
93
|
-
### How It Works
|
|
133
|
+
BXM provides built-in cross-context authentication that syncs across all extension contexts (popup, options, sidepanel, pages, background) without using `chrome.storage`.
|
|
94
134
|
|
|
95
|
-
**Background.js is the source of truth.** Auth syncs via messaging
|
|
135
|
+
**Background.js is the source of truth.** Auth syncs via messaging — sign-in / sign-out events propagate across all open contexts, and new contexts handshake with background on load.
|
|
96
136
|
|
|
97
|
-
|
|
98
|
-
- **Context load**: Each context compares its UID with background's UID on load; syncs if different
|
|
99
|
-
- **Sign-out**: User clicks `.auth-signout-btn` → context signs out → notifies background → background broadcasts sign-out to all contexts
|
|
100
|
-
|
|
101
|
-
### Required Setup
|
|
137
|
+
### Setup
|
|
102
138
|
|
|
103
139
|
1. Add `authDomain` to your Firebase config in `config/browser-extension-manager.json`
|
|
104
|
-
2. Add `tabs` permission to `src/manifest.json`
|
|
140
|
+
2. Add `tabs` permission to `src/manifest.json`
|
|
141
|
+
|
|
142
|
+
### Auth button classes
|
|
105
143
|
|
|
106
|
-
|
|
144
|
+
Add these CSS classes to HTML elements for declarative auth UI:
|
|
107
145
|
|
|
108
146
|
| Class | Action |
|
|
109
|
-
|
|
110
|
-
| `.auth-signin-btn` | Opens `/token` page on website |
|
|
111
|
-
| `.auth-signout-btn` | Signs out via Web Manager |
|
|
112
|
-
| `.auth-account-btn` | Opens `/account` page on website |
|
|
147
|
+
|---|---|
|
|
148
|
+
| `.auth-signin-btn` | Opens `/token` page on your website |
|
|
149
|
+
| `.auth-signout-btn` | Signs out via Web Manager (broadcasts to all contexts) |
|
|
150
|
+
| `.auth-account-btn` | Opens `/account` page on your website |
|
|
113
151
|
|
|
114
|
-
### Example
|
|
115
152
|
```html
|
|
116
153
|
<button class="btn auth-signin-btn" data-wm-bind="@show !auth.user">Sign In</button>
|
|
117
154
|
|
|
118
155
|
<div data-wm-bind="@show auth.user" hidden>
|
|
156
|
+
<img data-wm-bind="@attr src auth.user.photoURL">
|
|
119
157
|
<span data-wm-bind="@text auth.user.displayName">User</span>
|
|
120
158
|
<button class="auth-signout-btn">Sign Out</button>
|
|
121
159
|
</div>
|
|
122
160
|
```
|
|
123
161
|
|
|
124
|
-
|
|
125
|
-
- `@show auth.user` / `@show !auth.user` - Show/hide based on auth state
|
|
126
|
-
- `@text auth.user.displayName` / `@text auth.user.email` - Display user info
|
|
127
|
-
- `@attr src auth.user.photoURL` - Set avatar image
|
|
162
|
+
Full guide: [docs/auth.md](docs/auth.md).
|
|
128
163
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
164
|
+
## 📚 Documentation
|
|
165
|
+
|
|
166
|
+
In-depth docs for every subsystem live in [docs/](docs/). See [CLAUDE.md](CLAUDE.md) for the architecture overview + table of contents.
|
|
167
|
+
|
|
168
|
+
## 🧰 Sister projects
|
|
169
|
+
|
|
170
|
+
- [Electron Manager (EM)](https://github.com/itw-creative-works/electron-manager) — same patterns, but for Electron desktop apps
|
|
171
|
+
- [Ultimate Jekyll Manager (UJM)](https://github.com/itw-creative-works/ultimate-jekyll-manager) — Jekyll static-site framework
|
|
172
|
+
- [Backend Manager (BEM)](https://github.com/itw-creative-works/backend-manager) — Firebase Functions backend framework
|
package/dist/background.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Libraries
|
|
2
2
|
import extension from './lib/extension.js';
|
|
3
3
|
import LoggerLite from './lib/logger-lite.js';
|
|
4
|
+
import { attachTo as attachModeHelpers } from './utils/mode-helpers.js';
|
|
4
5
|
|
|
5
6
|
// Firebase (static imports - dynamic import() doesn't work in service workers with webpack chunking)
|
|
6
7
|
import { initializeApp, getApp } from 'firebase/app';
|
|
@@ -660,5 +661,8 @@ function setupGlobalHandlers() {
|
|
|
660
661
|
});
|
|
661
662
|
}
|
|
662
663
|
|
|
664
|
+
// Cross-context helpers — Manager.isTesting() / isDevelopment() / etc.
|
|
665
|
+
attachModeHelpers(Manager);
|
|
666
|
+
|
|
663
667
|
// Export
|
|
664
668
|
export default Manager;
|
package/dist/build.js
CHANGED
|
@@ -196,5 +196,8 @@ Manager.logMemory = function (logger, label) {
|
|
|
196
196
|
};
|
|
197
197
|
Manager.prototype.logMemory = Manager.logMemory;
|
|
198
198
|
|
|
199
|
+
// Cross-context helpers — Manager.isTesting() / isDevelopment() / etc.
|
|
200
|
+
require('./utils/mode-helpers.js').attachTo(Manager);
|
|
201
|
+
|
|
199
202
|
// Export
|
|
200
203
|
module.exports = Manager;
|
package/dist/cli.js
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Libraries
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const Manager = new (require('../build.js'));
|
|
5
|
+
const logger = Manager.logger('test');
|
|
6
|
+
const { run } = require('../test/runner.js');
|
|
7
|
+
|
|
8
|
+
module.exports = async function (options) {
|
|
9
|
+
const layer = options.layer || 'all';
|
|
10
|
+
const filter = options.filter || null;
|
|
11
|
+
const reporter = options.reporter || 'pretty';
|
|
12
|
+
const integration = options.integration === true || options.integration === 'true';
|
|
13
|
+
|
|
14
|
+
if (integration) {
|
|
15
|
+
process.env.BXM_TEST_INTEGRATION = '1';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Canonical signal — every Manager picks this up via isTesting().
|
|
19
|
+
process.env.BXM_TEST_MODE = 'true';
|
|
20
|
+
|
|
21
|
+
// When BXM itself runs its own boot-layer tests (the cwd's package.json is
|
|
22
|
+
// BXM's package.json), there's no real consumer extension to target. Point
|
|
23
|
+
// the boot runner at the fixture under dist/test/fixtures/consumer-extension
|
|
24
|
+
// unless the caller has already set BXM_TEST_BOOT_PROJECT explicitly.
|
|
25
|
+
if (!process.env.BXM_TEST_BOOT_PROJECT) {
|
|
26
|
+
try {
|
|
27
|
+
const cwdPkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'));
|
|
28
|
+
if (cwdPkg.name === 'browser-extension-manager') {
|
|
29
|
+
process.env.BXM_TEST_BOOT_PROJECT = path.join(__dirname, '..', 'test', 'fixtures', 'consumer-extension');
|
|
30
|
+
}
|
|
31
|
+
} catch (_) { /* no package.json — leave unset */ }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (reporter !== 'json') {
|
|
35
|
+
logger.log(`Running tests (layer=${layer}${filter ? ` filter="${filter}"` : ''}${integration ? ' +integration' : ''})`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const result = await run({ layer, filter, reporter });
|
|
39
|
+
|
|
40
|
+
if (reporter === 'json') {
|
|
41
|
+
// Final machine-readable summary.
|
|
42
|
+
process.stdout.write(JSON.stringify({
|
|
43
|
+
event: 'summary',
|
|
44
|
+
passed: result.passed,
|
|
45
|
+
failed: result.failed,
|
|
46
|
+
skipped: result.skipped,
|
|
47
|
+
total: result.passed + result.failed + result.skipped,
|
|
48
|
+
}) + '\n');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (result.failed > 0) {
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
throw new Error(`${result.failed} test(s) failed`);
|
|
54
|
+
}
|
|
55
|
+
};
|
package/dist/content.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import extension from './lib/extension.js';
|
|
3
3
|
import LoggerLite from './lib/logger-lite.js';
|
|
4
4
|
import Affiliatizer from './lib/affiliatizer.js';
|
|
5
|
+
import { attachTo as attachModeHelpers } from './utils/mode-helpers.js';
|
|
5
6
|
|
|
6
7
|
// Class
|
|
7
8
|
class Manager {
|
|
@@ -28,5 +29,8 @@ class Manager {
|
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
// Cross-context helpers — Manager.isTesting() / isDevelopment() / etc.
|
|
33
|
+
attachModeHelpers(Manager);
|
|
34
|
+
|
|
31
35
|
// Export
|
|
32
36
|
export default Manager;
|
package/dist/defaults/CLAUDE.md
CHANGED
|
@@ -1,8 +1,68 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# ========== Default Values ==========
|
|
2
|
+
# Browser Extension Manager (BXM) — consumer project
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
You should have a full understanding of Browser Extension Manager before editing this project, which can be found at: node_modules/browser-extension-manager/CLAUDE.md
|
|
4
|
+
> **Auto-managed file.** Everything between `# ========== Default Values ==========` and `# ========== Custom Values ==========` is owned by `browser-extension-manager` and rewritten on every `npx mgr setup`. Put your own project-specific notes BELOW the `Custom Values` marker — that section is preserved verbatim across setups.
|
|
6
5
|
|
|
7
|
-
##
|
|
8
|
-
|
|
6
|
+
## Framework
|
|
7
|
+
|
|
8
|
+
This project consumes **Browser Extension Manager** (BXM) — a comprehensive framework for building modern cross-browser extensions (Chrome, Firefox, Edge, Opera, Brave). BXM provides one-line bootstrap per extension context, a component-based architecture (view + styles + script per context), a multi-browser build/release pipeline that produces store-uploadable zips, cross-context auth synchronization, and a built-in four-layer test framework.
|
|
9
|
+
|
|
10
|
+
**Framework's own docs** (read these for deep-dives; both paths point to the same files, the absolute path works regardless of working directory):
|
|
11
|
+
- Top-level overview: `/Users/ian/Developer/Repositories/ITW-Creative-Works/browser-extension-manager/CLAUDE.md` (or `node_modules/browser-extension-manager/CLAUDE.md`)
|
|
12
|
+
- Subsystem references: `/Users/ian/Developer/Repositories/ITW-Creative-Works/browser-extension-manager/docs/` (or `node_modules/browser-extension-manager/docs/`)
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm start # dev with live reload (gulp → webpack → serve)
|
|
18
|
+
npm run build # production build → dist/ + packaged/<browser>/raw/ + .zip per browser
|
|
19
|
+
BXM_IS_PUBLISH=true npm run build # build + auto-upload to Chrome / Firefox / Edge stores
|
|
20
|
+
npx mgr test # run framework + project test suites
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Load the unpacked extension in Chrome: point chrome://extensions → "Load unpacked" at `packaged/chromium/raw/`.
|
|
24
|
+
|
|
25
|
+
## Where things live
|
|
26
|
+
|
|
27
|
+
- `config/browser-extension-manager.json` — JSON5 config: brand, manifest overrides, build settings, theme. `Manager.getConfig()` reads this.
|
|
28
|
+
- `config/messages.json` — i18n source. Auto-translated to 16 languages at build time via the Claude CLI (only missing keys regenerated).
|
|
29
|
+
- `config/description.md` — store-listing description (used by the publish step).
|
|
30
|
+
- `src/manifest.json` — extension manifest. BXM merges its defaults in at build time; you only need to declare what's specific to your extension.
|
|
31
|
+
- `src/views/<context>/index.html` — per-context HTML (popup / options / sidepanel / pages).
|
|
32
|
+
- `src/assets/js/components/<context>/index.js` — per-context script entry. One-line bootstrap of `browser-extension-manager/<context>`.
|
|
33
|
+
- `src/assets/css/components/<context>/index.scss` — per-context styles.
|
|
34
|
+
- `src/assets/js/components/background.js` — MV3 service worker entry. Source of truth for auth + messaging.
|
|
35
|
+
- `src/_locales/en/messages.json` — Chrome `__MSG_*__` placeholders (auto-translated to 16 langs at build).
|
|
36
|
+
- `hooks/build/{pre,post}.js` — optional lifecycle hooks.
|
|
37
|
+
- `test/**/*.js` — your project test suites (framework auto-runs them alongside its own).
|
|
38
|
+
|
|
39
|
+
## Per-context imports
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
// src/assets/js/components/popup/index.js
|
|
43
|
+
import Manager from 'browser-extension-manager/popup';
|
|
44
|
+
await new Manager().initialize();
|
|
45
|
+
|
|
46
|
+
// src/assets/js/components/background.js (service worker)
|
|
47
|
+
import Manager from 'browser-extension-manager/background';
|
|
48
|
+
await new Manager().initialize();
|
|
49
|
+
|
|
50
|
+
// Same shape for options / sidepanel / content / page / offscreen
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Available APIs at runtime
|
|
54
|
+
|
|
55
|
+
After `initialize()`, every Manager exposes:
|
|
56
|
+
- `manager.extension` — cross-browser `chrome.*` / `browser.*` / `window.*` wrapper
|
|
57
|
+
- `manager.logger` — timestamped per-context logger
|
|
58
|
+
- `manager.webManager` — Web Manager singleton (Firebase, auth, analytics, reactive `data-wm-bind` directives)
|
|
59
|
+
- `manager.messenger` — `chrome.runtime.onMessage` listener wired automatically
|
|
60
|
+
- `manager.isDevelopment()` / `isProduction()` / `isTesting()` / `getVersion()` — cross-context helpers
|
|
61
|
+
|
|
62
|
+
Auth UI is declarative — add `.auth-signin-btn` / `.auth-signout-btn` / `.auth-account-btn` to buttons; BXM wires them. Show/hide based on auth state via `data-wm-bind="@show auth.user"`.
|
|
63
|
+
|
|
64
|
+
# ========== Custom Values ==========
|
|
65
|
+
|
|
66
|
+
## Project-specific notes
|
|
67
|
+
|
|
68
|
+
Add anything specific to THIS project here. Edits below this line are preserved across `npx mgr setup` runs.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const Manager = new (require('../../build.js'));
|
|
3
3
|
const logger = Manager.logger('defaults');
|
|
4
4
|
const { src, dest, watch, series } = require('gulp');
|
|
5
|
-
const
|
|
5
|
+
const { Transform } = require('node:stream');
|
|
6
6
|
const jetpack = require('fs-jetpack');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const { minimatch } = require('minimatch');
|
|
@@ -66,6 +66,15 @@ const FILE_MAP = {
|
|
|
66
66
|
mergeLines: true,
|
|
67
67
|
},
|
|
68
68
|
|
|
69
|
+
// Consumer CLAUDE.md uses the same marker-based merge as .env/.gitignore.
|
|
70
|
+
// Framework owns the Default section; consumer owns everything below the Custom marker.
|
|
71
|
+
// Must come AFTER `**/*.md` (which sets overwrite: false) — `getFileOptions` does
|
|
72
|
+
// last-match-wins, so this rule's `mergeLines: true` activates the merge path even though
|
|
73
|
+
// the catch-all would otherwise skip.
|
|
74
|
+
'CLAUDE.md': {
|
|
75
|
+
mergeLines: true,
|
|
76
|
+
},
|
|
77
|
+
|
|
69
78
|
// Config files
|
|
70
79
|
'config/browser-extension-manager.json': {
|
|
71
80
|
overwrite: true,
|
|
@@ -270,8 +279,12 @@ function mergeLineBasedFiles(existingContent, newContent, fileName) {
|
|
|
270
279
|
result.push(DEFAULT_SECTION_MARKER);
|
|
271
280
|
result.push(...mergedDefaultSection);
|
|
272
281
|
|
|
273
|
-
// Add custom section
|
|
274
|
-
|
|
282
|
+
// Add custom section. Insert a single blank line before the marker, but only if the
|
|
283
|
+
// merged default doesn't already end with one — otherwise we'd accumulate an extra blank
|
|
284
|
+
// line on every merge, breaking idempotency after the first re-run.
|
|
285
|
+
if (mergedDefaultSection.length === 0 || mergedDefaultSection[mergedDefaultSection.length - 1].trim() !== '') {
|
|
286
|
+
result.push('');
|
|
287
|
+
}
|
|
275
288
|
result.push(CUSTOM_SECTION_MARKER);
|
|
276
289
|
|
|
277
290
|
// First add any user lines that were in default section (moved to custom)
|
|
@@ -347,7 +360,9 @@ function defaults(complete, changedFile) {
|
|
|
347
360
|
}
|
|
348
361
|
|
|
349
362
|
function customTransform() {
|
|
350
|
-
return
|
|
363
|
+
return new Transform({
|
|
364
|
+
objectMode: true,
|
|
365
|
+
transform(file, _, callback) {
|
|
351
366
|
// Skip if it's a directory
|
|
352
367
|
if (file.isDirectory()) {
|
|
353
368
|
return callback(null, file);
|
|
@@ -477,6 +492,7 @@ function customTransform() {
|
|
|
477
492
|
|
|
478
493
|
// Complete
|
|
479
494
|
return callback();
|
|
495
|
+
},
|
|
480
496
|
});
|
|
481
497
|
}
|
|
482
498
|
function defaultsWatcher(complete) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const Manager = new (require('../../build.js'));
|
|
3
3
|
const logger = Manager.logger('distribute');
|
|
4
4
|
const { src, dest, watch, series } = require('gulp');
|
|
5
|
-
const
|
|
5
|
+
const { Transform } = require('node:stream');
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const jetpack = require('fs-jetpack');
|
|
8
8
|
const createTemplateTransform = require('./utils/template-transform');
|
|
@@ -101,38 +101,41 @@ function distribute() {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
function customTransform() {
|
|
104
|
-
return
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
104
|
+
return new Transform({
|
|
105
|
+
objectMode: true,
|
|
106
|
+
transform(file, _, callback) {
|
|
107
|
+
// Skip if it's a directory
|
|
108
|
+
if (file.isDirectory()) {
|
|
109
|
+
return callback(null, file);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Get relative path from src base
|
|
113
|
+
const relativePath = path.relative(file.base, file.path).replace(/\\/g, '/');
|
|
114
|
+
|
|
115
|
+
// Log
|
|
116
|
+
if (LOUD) {
|
|
117
|
+
logger.log(`Processing file: ${relativePath}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Change path if it starts with 'pages/'
|
|
121
|
+
// if (relativePath.startsWith('pages/')) {
|
|
122
|
+
// // Remove 'pages/' prefix
|
|
123
|
+
// const newRelativePath = relativePath.replace(/^pages\//, '');
|
|
124
|
+
|
|
125
|
+
// // Update file path to remove pages directory
|
|
126
|
+
// // This will make src/pages/index.html -> dist/index.html
|
|
127
|
+
// file.path = path.join(file.base, newRelativePath);
|
|
128
|
+
|
|
129
|
+
// // Log
|
|
130
|
+
// logger.log(` -> Moving from pages/ to root: ${newRelativePath}`);
|
|
131
|
+
// }
|
|
132
|
+
|
|
133
|
+
// Push the file
|
|
134
|
+
this.push(file);
|
|
135
|
+
|
|
136
|
+
// Continue
|
|
137
|
+
callback();
|
|
138
|
+
},
|
|
136
139
|
});
|
|
137
140
|
}
|
|
138
141
|
|