firefox-location2 2.0.4 → 2.1.1

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 CHANGED
@@ -169,6 +169,7 @@ Note: On Linux, the module first tries to resolve binaries on <code>$PATH</code>
169
169
 
170
170
  ```js
171
171
  import firefoxLocation from 'firefox-location2'
172
+ import {getFirefoxVersion} from 'firefox-location2'
172
173
 
173
174
  // Strict (Stable only)
174
175
  console.log(firefoxLocation())
@@ -183,6 +184,14 @@ import {locateFirefoxOrExplain, getInstallGuidance} from 'firefox-location2'
183
184
  try {
184
185
  const path = locateFirefoxOrExplain({allowFallback: true})
185
186
  console.log(path)
187
+
188
+ // Cross-platform version (no exec by default)
189
+ const v = getFirefoxVersion(path)
190
+ console.log(v) // e.g. "130.0.1" or null
191
+
192
+ // Opt-in: allow executing the binary to fetch version on platforms without metadata (e.g. Linux)
193
+ const v2 = getFirefoxVersion(path, {allowExec: true})
194
+ console.log(v2)
186
195
  } catch (e) {
187
196
  console.error(String(e))
188
197
  // Or print getInstallGuidance() explicitly
@@ -244,6 +253,22 @@ Notes:
244
253
  - Output is colorized when printed to a TTY (green success, red error)
245
254
  - After you run `npx @puppeteer/browsers install firefox@stable` once, we auto-detect Firefox from Puppeteer's cache on all platforms. No env vars needed.
246
255
 
256
+ ## API
257
+
258
+ - `default export locateFirefox(allowFallback?: boolean): string | null`
259
+ - Returns the first existing path among the selected channels or `null`.
260
+ - When `allowFallback` is `true`, checks Stable → ESR → Developer → Nightly.
261
+
262
+ - `locateFirefoxOrExplain(options?: boolean | { allowFallback?: boolean }): string`
263
+ - Returns a path if found, otherwise throws an `Error` with a friendly installation guide.
264
+ - Path resolution never executes the browser.
265
+
266
+ - `getFirefoxVersion(bin: string, opts?: { allowExec?: boolean }): string | null`
267
+ - Cross-platform version resolver that does not execute the browser by default.
268
+ - Windows: reads PE file metadata via PowerShell (no GUI spawn).
269
+ - macOS: reads `Info.plist` (no GUI spawn).
270
+ - Linux/other: returns `null` unless `allowExec` is `true`, then tries `--version`.
271
+
247
272
  ## Planned enhancements
248
273
 
249
274
  - Flatpak detection on Linux: detect installed Flatpak app <code>org.mozilla.firefox</code> and expose the appropriate invocation.
package/bin.js CHANGED
@@ -3,23 +3,39 @@
3
3
  import locateFirefox, {
4
4
  locateFirefoxOrExplain,
5
5
  getInstallGuidance,
6
+ getFirefoxVersion,
6
7
  } from './dist/index.js';
7
8
 
8
9
  const argv = process.argv.slice(2);
9
10
  const allowFallback = argv.includes('--fallback') || argv.includes('-f');
11
+ const printBrowserVersion =
12
+ argv.includes('--firefox-version') || argv.includes('--browser-version');
13
+ const allowExec = argv.includes('--allow-exec');
10
14
 
11
15
  try {
12
- const result =
13
- typeof locateFirefoxOrExplain === 'function'
14
- ? locateFirefoxOrExplain({ allowFallback })
15
- : locateFirefox(allowFallback);
16
+ const location =
17
+ (typeof locateFirefoxOrExplain === 'function' &&
18
+ locateFirefoxOrExplain({ allowFallback })) ||
19
+ (typeof locateFirefox === 'function' && locateFirefox(allowFallback)) ||
20
+ null;
16
21
 
17
- if (!result)
22
+ if (!location)
18
23
  throw new Error(
19
24
  (typeof getInstallGuidance === 'function' && getInstallGuidance()) ||
20
25
  'No suitable Firefox binary found.',
21
26
  );
22
- console.log(String(result));
27
+
28
+ if (printBrowserVersion && typeof getFirefoxVersion === 'function') {
29
+ const v = getFirefoxVersion(location, { allowExec });
30
+ if (!v) {
31
+ console.log('');
32
+ process.exit(2);
33
+ }
34
+ console.log(String(v));
35
+ process.exit(0);
36
+ }
37
+
38
+ console.log(String(location));
23
39
  } catch (e) {
24
40
  console.error(String(e));
25
41
  process.exit(1);
package/dist/index.cjs CHANGED
@@ -33,8 +33,9 @@ var __webpack_require__ = {};
33
33
  var __webpack_exports__ = {};
34
34
  __webpack_require__.r(__webpack_exports__);
35
35
  __webpack_require__.d(__webpack_exports__, {
36
- getInstallGuidance: ()=>getInstallGuidance,
37
36
  default: ()=>locateFirefox,
37
+ getFirefoxVersion: ()=>getFirefoxVersion,
38
+ getInstallGuidance: ()=>getInstallGuidance,
38
39
  locateFirefoxOrExplain: ()=>locateFirefoxOrExplain
39
40
  });
40
41
  const external_fs_namespaceObject = require("fs");
@@ -276,6 +277,82 @@ function locateFirefoxOrExplain(options) {
276
277
  if ('string' == typeof found && found) return found;
277
278
  throw new Error(getInstallGuidance());
278
279
  }
280
+ function getFirefoxVersion(bin, opts) {
281
+ if ('win32' === process.platform) {
282
+ try {
283
+ const psPath = bin.replace(/'/g, "''");
284
+ const pv = (0, external_child_process_namespaceObject.execFileSync)('powershell.exe', [
285
+ '-NoProfile',
286
+ '-Command',
287
+ `(Get-Item -LiteralPath '${psPath}').VersionInfo.ProductVersion`
288
+ ], {
289
+ encoding: 'utf8',
290
+ stdio: [
291
+ 'ignore',
292
+ 'pipe',
293
+ 'ignore'
294
+ ]
295
+ }).trim();
296
+ return normalizeVersion(pv);
297
+ } catch {}
298
+ if (null == opts ? void 0 : opts.allowExec) {
299
+ const v = tryExec(bin, [
300
+ '--version'
301
+ ]);
302
+ return normalizeVersion(v);
303
+ }
304
+ return null;
305
+ }
306
+ if ('darwin' === process.platform) {
307
+ try {
308
+ const contentsDir = external_path_default().dirname(external_path_default().dirname(bin));
309
+ const infoPlist = external_path_default().join(contentsDir, 'Info.plist');
310
+ if (external_fs_default().existsSync(infoPlist)) {
311
+ const xml = external_fs_default().readFileSync(infoPlist, 'utf8');
312
+ const v = parsePlistString(xml, 'CFBundleShortVersionString') || parsePlistString(xml, 'CFBundleVersion') || '';
313
+ return normalizeVersion(v);
314
+ }
315
+ } catch {}
316
+ if (null == opts ? void 0 : opts.allowExec) {
317
+ const v = tryExec(bin, [
318
+ '--version'
319
+ ]);
320
+ return normalizeVersion(v);
321
+ }
322
+ return null;
323
+ }
324
+ if (null == opts ? void 0 : opts.allowExec) {
325
+ const v = tryExec(bin, [
326
+ '--version'
327
+ ]);
328
+ return normalizeVersion(v);
329
+ }
330
+ return null;
331
+ }
332
+ function normalizeVersion(s) {
333
+ if (!s) return null;
334
+ const m = String(s).match(/(\d+(?:\.\d+){1,3})/);
335
+ return m ? m[1] : null;
336
+ }
337
+ function parsePlistString(xml, key) {
338
+ const re = new RegExp(`<key>${key}<\\/key>\\s*<string>([^<]+)<\\/string>`);
339
+ const m = xml.match(re);
340
+ return m ? m[1].trim() : null;
341
+ }
342
+ function tryExec(bin, args) {
343
+ try {
344
+ return (0, external_child_process_namespaceObject.execFileSync)(bin, args, {
345
+ encoding: 'utf8',
346
+ stdio: [
347
+ 'ignore',
348
+ 'pipe',
349
+ 'ignore'
350
+ ]
351
+ }).trim();
352
+ } catch {
353
+ return null;
354
+ }
355
+ }
279
356
  function resolveFromPuppeteerBrowsersCLI() {
280
357
  try {
281
358
  const attempts = [
@@ -331,10 +408,12 @@ function resolveFromPuppeteerBrowsersCLI() {
331
408
  return null;
332
409
  }
333
410
  exports["default"] = __webpack_exports__["default"];
411
+ exports.getFirefoxVersion = __webpack_exports__.getFirefoxVersion;
334
412
  exports.getInstallGuidance = __webpack_exports__.getInstallGuidance;
335
413
  exports.locateFirefoxOrExplain = __webpack_exports__.locateFirefoxOrExplain;
336
414
  for(var __webpack_i__ in __webpack_exports__)if (-1 === [
337
415
  "default",
416
+ "getFirefoxVersion",
338
417
  "getInstallGuidance",
339
418
  "locateFirefoxOrExplain"
340
419
  ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
package/dist/index.d.ts CHANGED
@@ -21,3 +21,13 @@ export declare function getInstallGuidance(): string;
21
21
  export declare function locateFirefoxOrExplain(options?: boolean | {
22
22
  allowFallback?: boolean;
23
23
  }): string;
24
+ /**
25
+ * Cross-platform Firefox version resolver.
26
+ * - Never executes the browser by default.
27
+ * - On Windows: reads PE metadata via PowerShell.
28
+ * - On macOS: reads Info.plist next to the binary.
29
+ * - On Linux/others: returns null unless opts.allowExec is true, then tries --version.
30
+ */
31
+ export declare function getFirefoxVersion(bin: string, opts?: {
32
+ allowExec?: boolean;
33
+ }): string | null;
package/dist/index.js CHANGED
@@ -231,6 +231,82 @@ function locateFirefoxOrExplain(options) {
231
231
  if ('string' == typeof found && found) return found;
232
232
  throw new Error(getInstallGuidance());
233
233
  }
234
+ function getFirefoxVersion(bin, opts) {
235
+ if ('win32' === process.platform) {
236
+ try {
237
+ const psPath = bin.replace(/'/g, "''");
238
+ const pv = (0, __WEBPACK_EXTERNAL_MODULE_child_process__.execFileSync)('powershell.exe', [
239
+ '-NoProfile',
240
+ '-Command',
241
+ `(Get-Item -LiteralPath '${psPath}').VersionInfo.ProductVersion`
242
+ ], {
243
+ encoding: 'utf8',
244
+ stdio: [
245
+ 'ignore',
246
+ 'pipe',
247
+ 'ignore'
248
+ ]
249
+ }).trim();
250
+ return normalizeVersion(pv);
251
+ } catch {}
252
+ if (null == opts ? void 0 : opts.allowExec) {
253
+ const v = tryExec(bin, [
254
+ '--version'
255
+ ]);
256
+ return normalizeVersion(v);
257
+ }
258
+ return null;
259
+ }
260
+ if ('darwin' === process.platform) {
261
+ try {
262
+ const contentsDir = __WEBPACK_EXTERNAL_MODULE_path__["default"].dirname(__WEBPACK_EXTERNAL_MODULE_path__["default"].dirname(bin));
263
+ const infoPlist = __WEBPACK_EXTERNAL_MODULE_path__["default"].join(contentsDir, 'Info.plist');
264
+ if (__WEBPACK_EXTERNAL_MODULE_fs__["default"].existsSync(infoPlist)) {
265
+ const xml = __WEBPACK_EXTERNAL_MODULE_fs__["default"].readFileSync(infoPlist, 'utf8');
266
+ const v = parsePlistString(xml, 'CFBundleShortVersionString') || parsePlistString(xml, 'CFBundleVersion') || '';
267
+ return normalizeVersion(v);
268
+ }
269
+ } catch {}
270
+ if (null == opts ? void 0 : opts.allowExec) {
271
+ const v = tryExec(bin, [
272
+ '--version'
273
+ ]);
274
+ return normalizeVersion(v);
275
+ }
276
+ return null;
277
+ }
278
+ if (null == opts ? void 0 : opts.allowExec) {
279
+ const v = tryExec(bin, [
280
+ '--version'
281
+ ]);
282
+ return normalizeVersion(v);
283
+ }
284
+ return null;
285
+ }
286
+ function normalizeVersion(s) {
287
+ if (!s) return null;
288
+ const m = String(s).match(/(\d+(?:\.\d+){1,3})/);
289
+ return m ? m[1] : null;
290
+ }
291
+ function parsePlistString(xml, key) {
292
+ const re = new RegExp(`<key>${key}<\\/key>\\s*<string>([^<]+)<\\/string>`);
293
+ const m = xml.match(re);
294
+ return m ? m[1].trim() : null;
295
+ }
296
+ function tryExec(bin, args) {
297
+ try {
298
+ return (0, __WEBPACK_EXTERNAL_MODULE_child_process__.execFileSync)(bin, args, {
299
+ encoding: 'utf8',
300
+ stdio: [
301
+ 'ignore',
302
+ 'pipe',
303
+ 'ignore'
304
+ ]
305
+ }).trim();
306
+ } catch {
307
+ return null;
308
+ }
309
+ }
234
310
  function resolveFromPuppeteerBrowsersCLI() {
235
311
  try {
236
312
  const attempts = [
@@ -285,4 +361,4 @@ function resolveFromPuppeteerBrowsersCLI() {
285
361
  } catch {}
286
362
  return null;
287
363
  }
288
- export { locateFirefox as default, getInstallGuidance, locateFirefoxOrExplain };
364
+ export { locateFirefox as default, getFirefoxVersion, getInstallGuidance, locateFirefoxOrExplain };
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "https://github.com/cezaraugusto/firefox-location2.git"
6
6
  },
7
7
  "name": "firefox-location2",
8
- "version": "2.0.4",
8
+ "version": "2.1.1",
9
9
  "description": "Approximates the current location of the Firefox browser across platforms.",
10
10
  "homepage": "https://www.npmjs.com/package/firefox-location2",
11
11
  "type": "module",
@@ -29,14 +29,15 @@
29
29
  "check": "pnpm dlx @biomejs/biome@1.9.4 check --write",
30
30
  "dev": "rslib build --watch",
31
31
  "format": "pnpm dlx @biomejs/biome@1.9.4 format --write",
32
+ "pretest": "npm run build",
32
33
  "test": "vitest run --pool vmForks --poolOptions.vmForks.singleFork",
33
34
  "prepublishOnly": "npm run build",
34
35
  "publish:provenance": "np patch --no-tests --any-branch --yolo --message 'release: %s' --provenance"
35
36
  },
36
37
  "engines": {
37
- "node": ">=18 <23"
38
+ "node": ">=16"
38
39
  },
39
- "engineStrict": true,
40
+ "engineStrict": false,
40
41
  "publishConfig": {
41
42
  "provenance": true
42
43
  },