browser-extension-manager 1.3.0 → 1.3.2
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/CHANGELOG.md
CHANGED
|
@@ -18,16 +18,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
18
18
|
## [1.3.0] - 2025-12-16
|
|
19
19
|
|
|
20
20
|
### Added
|
|
21
|
-
- Multi-target builds for Chromium and
|
|
21
|
+
- Multi-target builds for Chromium, Firefox, and Opera with automatic manifest adjustments
|
|
22
22
|
- Chromium build uses `background.service_worker`, Firefox build uses `background.scripts`
|
|
23
|
-
-
|
|
23
|
+
- Opera build auto-resolves `__MSG_*__` placeholders in `short_name` (Opera enforces 12-char limit including placeholder text)
|
|
24
|
+
- Browser-specific `TARGETS` config object with `adjustManifest()` functions for each target
|
|
25
|
+
- Opera loading instructions in BUILD_INSTRUCTIONS.md
|
|
24
26
|
|
|
25
27
|
### Changed
|
|
26
|
-
- Packaged output structure changed from `packaged/raw/` to `packaged/{chromium,firefox}/raw/`
|
|
27
|
-
- Extension zip moved from `packaged/extension.zip` to `packaged/{chromium,firefox}/extension.zip`
|
|
28
|
-
- `package.js` now creates separate builds for each browser target
|
|
29
|
-
- `publish.js` uses target-specific paths (chromium zip for Chrome/Edge, firefox raw for Firefox)
|
|
28
|
+
- Packaged output structure changed from `packaged/raw/` to `packaged/{chromium,firefox,opera}/raw/`
|
|
29
|
+
- Extension zip moved from `packaged/extension.zip` to `packaged/{chromium,firefox,opera}/extension.zip`
|
|
30
|
+
- `package.js` now creates separate builds for each browser target using configurable `TARGETS` object
|
|
31
|
+
- `publish.js` uses target-specific paths (chromium zip for Chrome/Edge, firefox raw for Firefox, opera zip for Opera)
|
|
30
32
|
- `audit.js` audits chromium build (code is identical between targets)
|
|
33
|
+
- Manifest compilation now uses 3-step process: apply defaults → target adjustments → cleanup
|
|
31
34
|
|
|
32
35
|
## [1.1.13] - 2025-11-26
|
|
33
36
|
### Added
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// Basic info
|
|
3
3
|
manifest_version: 3,
|
|
4
4
|
name: '__MSG_appName__',
|
|
5
|
+
// NOTE: Opera enforces a 12-char limit on short_name INCLUDING the placeholder text
|
|
6
|
+
// '__MSG_appNameShort__' is 19 chars, so use a static value for Opera compatibility
|
|
5
7
|
short_name: '__MSG_appNameShort__',
|
|
6
8
|
description: '__MSG_appDescription__',
|
|
7
9
|
|
package/dist/defaults/_.env
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
# ========== Default Values ==========
|
|
2
|
+
# Github Token
|
|
3
|
+
# Get token at: https://github.com/settings/tokens
|
|
4
|
+
GH_TOKEN=""
|
|
5
|
+
|
|
2
6
|
# Chrome Web Store Publishing Credentials
|
|
3
7
|
# Get credentials at: https://developer.chrome.com/docs/webstore/using_webstore_api/
|
|
4
|
-
CHROME_EXTENSION_ID="
|
|
5
|
-
CHROME_CLIENT_ID="
|
|
6
|
-
CHROME_CLIENT_SECRET="
|
|
7
|
-
CHROME_REFRESH_TOKEN="
|
|
8
|
+
CHROME_EXTENSION_ID=""
|
|
9
|
+
CHROME_CLIENT_ID=""
|
|
10
|
+
CHROME_CLIENT_SECRET=""
|
|
11
|
+
CHROME_REFRESH_TOKEN=""
|
|
8
12
|
|
|
9
13
|
# Firefox Add-ons Publishing Credentials
|
|
10
14
|
# Get credentials at: https://addons.mozilla.org/developers/addon/api/key/
|
|
11
|
-
FIREFOX_EXTENSION_ID="
|
|
12
|
-
FIREFOX_API_KEY="
|
|
13
|
-
FIREFOX_API_SECRET="
|
|
15
|
+
FIREFOX_EXTENSION_ID=""
|
|
16
|
+
FIREFOX_API_KEY=""
|
|
17
|
+
FIREFOX_API_SECRET=""
|
|
14
18
|
|
|
15
19
|
# Microsoft Edge Add-ons Publishing Credentials
|
|
16
20
|
# Get credentials at: https://learn.microsoft.com/en-us/microsoft-edge/extensions-chromium/publish/api/using-addons-api
|
|
17
|
-
EDGE_PRODUCT_ID="
|
|
18
|
-
EDGE_CLIENT_ID="
|
|
19
|
-
EDGE_API_KEY="
|
|
21
|
+
EDGE_PRODUCT_ID=""
|
|
22
|
+
EDGE_CLIENT_ID=""
|
|
23
|
+
EDGE_API_KEY=""
|
|
20
24
|
|
|
21
25
|
# Opera Add-ons
|
|
22
26
|
# NOTE: Opera does not have a publishing API. Extensions must be submitted manually at:
|
|
@@ -152,8 +152,97 @@ function getPackageVersion(packageName) {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
// Build targets
|
|
156
|
-
const TARGETS =
|
|
155
|
+
// Build targets with browser-specific configurations
|
|
156
|
+
const TARGETS = {
|
|
157
|
+
chromium: {
|
|
158
|
+
// Chrome, Edge, Brave, etc. - uses service_worker
|
|
159
|
+
adjustManifest: (manifest) => {
|
|
160
|
+
if (manifest.background) {
|
|
161
|
+
delete manifest.background.scripts;
|
|
162
|
+
}
|
|
163
|
+
return manifest;
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
firefox: {
|
|
167
|
+
// Firefox - uses scripts array, no service_worker
|
|
168
|
+
adjustManifest: (manifest) => {
|
|
169
|
+
if (manifest.background) {
|
|
170
|
+
if (manifest.background.service_worker) {
|
|
171
|
+
if (!manifest.background.scripts) {
|
|
172
|
+
manifest.background.scripts = [manifest.background.service_worker];
|
|
173
|
+
}
|
|
174
|
+
delete manifest.background.service_worker;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return manifest;
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
opera: {
|
|
181
|
+
// Opera - like chromium but with stricter requirements
|
|
182
|
+
// Opera enforces 12-char limit on short_name INCLUDING placeholder text
|
|
183
|
+
adjustManifest: (manifest) => {
|
|
184
|
+
// Same as chromium for background
|
|
185
|
+
if (manifest.background) {
|
|
186
|
+
delete manifest.background.scripts;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Opera requires static short_name (12 char limit includes placeholder text)
|
|
190
|
+
// __MSG_appNameShort__ is 19 chars, so we must use actual value
|
|
191
|
+
if (manifest.short_name && manifest.short_name.startsWith('__MSG_')) {
|
|
192
|
+
// Try to get the value from default locale
|
|
193
|
+
const localesDir = path.join('dist', '_locales');
|
|
194
|
+
const defaultLocale = manifest.default_locale || 'en';
|
|
195
|
+
const messagesPath = path.join(localesDir, defaultLocale, 'messages.json');
|
|
196
|
+
|
|
197
|
+
if (jetpack.exists(messagesPath)) {
|
|
198
|
+
try {
|
|
199
|
+
const messages = JSON5.parse(jetpack.read(messagesPath));
|
|
200
|
+
// Extract key from __MSG_keyName__
|
|
201
|
+
const key = manifest.short_name.replace(/^__MSG_/, '').replace(/__$/, '');
|
|
202
|
+
if (messages[key]?.message) {
|
|
203
|
+
manifest.short_name = messages[key].message;
|
|
204
|
+
logger.log(`[opera] Resolved short_name to "${manifest.short_name}"`);
|
|
205
|
+
}
|
|
206
|
+
} catch (e) {
|
|
207
|
+
logger.warn(`[opera] Could not resolve short_name from locale: ${e.message}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return manifest;
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Recursively remove empty arrays and objects from an object
|
|
218
|
+
function removeEmptyValues(obj) {
|
|
219
|
+
if (Array.isArray(obj)) {
|
|
220
|
+
return obj;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (obj && typeof obj === 'object') {
|
|
224
|
+
const result = {};
|
|
225
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
226
|
+
// Recursively clean nested objects
|
|
227
|
+
const cleaned = removeEmptyValues(value);
|
|
228
|
+
|
|
229
|
+
// Skip empty arrays
|
|
230
|
+
if (Array.isArray(cleaned) && cleaned.length === 0) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Skip empty objects (after cleaning)
|
|
235
|
+
if (cleaned && typeof cleaned === 'object' && !Array.isArray(cleaned) && Object.keys(cleaned).length === 0) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
result[key] = cleaned;
|
|
240
|
+
}
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return obj;
|
|
245
|
+
}
|
|
157
246
|
|
|
158
247
|
// Special Compilation Task for manifest.json with default settings
|
|
159
248
|
async function compileManifest(outputDir, target) {
|
|
@@ -163,10 +252,12 @@ async function compileManifest(outputDir, target) {
|
|
|
163
252
|
const configPath = path.join(rootPathPackage, 'dist', 'config', 'manifest.json');
|
|
164
253
|
|
|
165
254
|
// Read and parse using JSON5
|
|
166
|
-
|
|
255
|
+
let manifest = JSON5.parse(jetpack.read(manifestPath));
|
|
167
256
|
const defaultConfig = JSON5.parse(jetpack.read(configPath));
|
|
168
257
|
|
|
169
|
-
//
|
|
258
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
259
|
+
// STEP 1: Apply defaults (shared across all targets)
|
|
260
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
170
261
|
getKeys(defaultConfig).forEach(key => {
|
|
171
262
|
const defaultValue = key.split('.').reduce((o, k) => (o || {})[k], defaultConfig);
|
|
172
263
|
const userValue = key.split('.').reduce((o, k) => (o || {})[k], manifest);
|
|
@@ -192,25 +283,22 @@ async function compileManifest(outputDir, target) {
|
|
|
192
283
|
// Add package version to manifest
|
|
193
284
|
manifest.version = project.version;
|
|
194
285
|
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
// Firefox uses scripts array only
|
|
202
|
-
if (manifest.background.service_worker) {
|
|
203
|
-
// Ensure scripts array exists (derive from service_worker if needed)
|
|
204
|
-
if (!manifest.background.scripts) {
|
|
205
|
-
manifest.background.scripts = [manifest.background.service_worker];
|
|
206
|
-
}
|
|
207
|
-
delete manifest.background.service_worker;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
286
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
287
|
+
// STEP 2: Apply target-specific adjustments
|
|
288
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
289
|
+
const targetConfig = TARGETS[target];
|
|
290
|
+
if (targetConfig?.adjustManifest) {
|
|
291
|
+
manifest = targetConfig.adjustManifest(manifest);
|
|
210
292
|
}
|
|
211
293
|
|
|
294
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
295
|
+
// STEP 3: Clean up (shared across all targets)
|
|
296
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
297
|
+
// Remove empty arrays and objects from manifest
|
|
298
|
+
const cleanedManifest = removeEmptyValues(manifest);
|
|
299
|
+
|
|
212
300
|
// Save as regular JSON
|
|
213
|
-
jetpack.write(outputPath, JSON.stringify(
|
|
301
|
+
jetpack.write(outputPath, JSON.stringify(cleanedManifest, null, 2));
|
|
214
302
|
|
|
215
303
|
logger.log(`[${target}] Manifest compiled with defaults`);
|
|
216
304
|
} catch (e) {
|
|
@@ -245,14 +333,14 @@ async function compileLocales(outputDir) {
|
|
|
245
333
|
}
|
|
246
334
|
}
|
|
247
335
|
|
|
248
|
-
// Package Task for raw - creates
|
|
336
|
+
// Package Task for raw - creates builds for all browser targets
|
|
249
337
|
async function packageRaw() {
|
|
250
338
|
// Log
|
|
251
339
|
logger.log(`Starting raw packaging...`);
|
|
252
340
|
|
|
253
341
|
try {
|
|
254
342
|
// Build for each target
|
|
255
|
-
for (const target of TARGETS) {
|
|
343
|
+
for (const target of Object.keys(TARGETS)) {
|
|
256
344
|
await packageRawForTarget(target);
|
|
257
345
|
}
|
|
258
346
|
|
|
@@ -320,7 +408,7 @@ async function packageZip() {
|
|
|
320
408
|
|
|
321
409
|
try {
|
|
322
410
|
// Create zip for each target
|
|
323
|
-
for (const target of TARGETS) {
|
|
411
|
+
for (const target of Object.keys(TARGETS)) {
|
|
324
412
|
const inputDir = `packaged/${target}/raw`;
|
|
325
413
|
const zipPath = `packaged/${target}/extension.zip`;
|
|
326
414
|
|
|
@@ -19,6 +19,10 @@ const PATHS = {
|
|
|
19
19
|
zip: path.join(process.cwd(), 'packaged', 'firefox', 'extension.zip'),
|
|
20
20
|
raw: path.join(process.cwd(), 'packaged', 'firefox', 'raw'),
|
|
21
21
|
},
|
|
22
|
+
opera: {
|
|
23
|
+
zip: path.join(process.cwd(), 'packaged', 'opera', 'extension.zip'),
|
|
24
|
+
raw: path.join(process.cwd(), 'packaged', 'opera', 'raw'),
|
|
25
|
+
},
|
|
22
26
|
};
|
|
23
27
|
|
|
24
28
|
// Helper to check if a credential is valid (not empty or placeholder)
|
|
@@ -26,17 +26,12 @@ npm install
|
|
|
26
26
|
npm run build
|
|
27
27
|
```
|
|
28
28
|
5. The built extensions will be in `packaged/` directory:
|
|
29
|
-
- **Firefox:** `packaged/firefox/raw/` (unpacked) and `packaged/firefox/extension.zip`
|
|
30
29
|
- **Chrome/Edge:** `packaged/chromium/raw/` (unpacked) and `packaged/chromium/extension.zip`
|
|
30
|
+
- **Firefox:** `packaged/firefox/raw/` (unpacked) and `packaged/firefox/extension.zip`
|
|
31
|
+
- **Opera:** `packaged/opera/raw/` (unpacked) and `packaged/opera/extension.zip`
|
|
31
32
|
|
|
32
33
|
## Loading the Extension
|
|
33
34
|
|
|
34
|
-
### Firefox
|
|
35
|
-
1. Go to `about:debugging`
|
|
36
|
-
2. Click "This Firefox"
|
|
37
|
-
3. Click "Load Temporary Add-on"
|
|
38
|
-
4. Select `packaged/firefox/raw/manifest.json`
|
|
39
|
-
|
|
40
35
|
### Chrome
|
|
41
36
|
1. Go to `chrome://extensions`
|
|
42
37
|
2. Enable "Developer mode"
|
|
@@ -49,6 +44,18 @@ npm run build
|
|
|
49
44
|
3. Click "Load unpacked"
|
|
50
45
|
4. Select the `packaged/chromium/raw/` directory
|
|
51
46
|
|
|
47
|
+
### Firefox
|
|
48
|
+
1. Go to `about:debugging`
|
|
49
|
+
2. Click "This Firefox"
|
|
50
|
+
3. Click "Load Temporary Add-on"
|
|
51
|
+
4. Select `packaged/firefox/raw/manifest.json`
|
|
52
|
+
|
|
53
|
+
### Opera
|
|
54
|
+
1. Go to `opera://extensions`
|
|
55
|
+
2. Enable "Developer mode"
|
|
56
|
+
3. Click "Load unpacked"
|
|
57
|
+
4. Select the `packaged/opera/raw/` directory
|
|
58
|
+
|
|
52
59
|
## Questions
|
|
53
60
|
|
|
54
61
|
If you have any questions about the build process, please contact the developer.
|