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 Firefox with automatic manifest adjustments
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
- - Edge loading instructions in BUILD_INSTRUCTIONS.md
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
 
@@ -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="your-extension-id"
5
- CHROME_CLIENT_ID="your-client-id"
6
- CHROME_CLIENT_SECRET="your-client-secret"
7
- CHROME_REFRESH_TOKEN="your-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="your-extension-id"
12
- FIREFOX_API_KEY="your-api-key"
13
- FIREFOX_API_SECRET="your-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="your-product-id"
18
- EDGE_CLIENT_ID="your-client-id"
19
- EDGE_API_KEY="your-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 = ['chromium', 'firefox'];
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
- const manifest = JSON5.parse(jetpack.read(manifestPath));
255
+ let manifest = JSON5.parse(jetpack.read(manifestPath));
167
256
  const defaultConfig = JSON5.parse(jetpack.read(configPath));
168
257
 
169
- // Apply defaults
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
- // Apply target-specific manifest adjustments
196
- if (manifest.background) {
197
- if (target === 'chromium') {
198
- // Chrome/Edge uses service_worker only
199
- delete manifest.background.scripts;
200
- } else if (target === 'firefox') {
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(manifest, null, 2));
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 both chromium and firefox builds
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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-extension-manager",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Browser Extension Manager dependency manager",
5
5
  "main": "dist/index.js",
6
6
  "exports": {