firefox-location2 2.0.0 → 2.0.4

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
@@ -11,13 +11,19 @@
11
11
 
12
12
  <img alt="Firefox" align="right" src="https://cdn.jsdelivr.net/gh/extension-js/media@db5deb23fbfa85530f8146718812972998e13a4d/browser_logos/svg/firefox.svg" width="10.5%" />
13
13
 
14
- * Finds Firefox in the following channel order: `stable` / `esr` / `developer edition` / `nightly`.
15
- * Supports macOS / Windows / Linux
16
- * Works both as an ES module or CommonJS
14
+ - By default checks only `stable`. Optionally can cascade to `esr` / `developer edition` / `nightly`.
15
+ - Supports macOS / Windows / Linux
16
+ - Works both as an ES module or CommonJS
17
+
18
+ New in this version:
19
+
20
+ - Optional helper to throw with a friendly install guide when nothing is found
21
+ - CLI output is colorized (green on success, red on error)
22
+ - 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)
17
23
 
18
24
  ## Support table
19
25
 
20
- This table lists the default locations where Firefox is typically installed for each supported platform and channel. The package checks these paths (in order) and returns the first one found.
26
+ This table lists the default locations where Firefox is typically installed for each supported platform and channel. By default, only the Stable channel is checked. When fallback is enabled, the package checks these paths (in order) and returns the first one found.
21
27
 
22
28
  <table>
23
29
  <thead>
@@ -153,28 +159,91 @@ This table lists the default locations where Firefox is typically installed for
153
159
  </tbody>
154
160
  </table>
155
161
 
156
- Returns the first existing path found, or <code>null</code> if none are found.
162
+ Returns the first existing path found (given selected channels), or <code>null</code> if none are found.
157
163
 
158
164
  Note: On Linux, the module first tries to resolve binaries on <code>$PATH</code> (using <code>which</code>) for the common channels listed above, then falls back to checking common filesystem locations like <code>/usr/bin/firefox</code>, <code>/usr/local/bin/firefox</code>, <code>/snap/bin/firefox</code>, and <code>/opt/firefox/firefox</code>.
159
165
 
160
166
  ## Usage
161
167
 
162
- **Via Node.js:**
168
+ **Via Node.js (strict by default):**
163
169
 
164
170
  ```js
165
- import firefoxLocation from "firefox-location2";
171
+ import firefoxLocation from 'firefox-location2'
172
+
173
+ // Strict (Stable only)
174
+ console.log(firefoxLocation())
175
+ // => "/Applications/Firefox.app/Contents/MacOS/firefox" or null
176
+
177
+ // Enable fallback (Stable / ESR / Developer Edition / Nightly)
178
+ console.log(firefoxLocation(true))
179
+ // => first found among Stable/ESR/Developer/Nightly or null
180
+
181
+ // Throw with a friendly, copy-pasteable guide when not found
182
+ import {locateFirefoxOrExplain, getInstallGuidance} from 'firefox-location2'
183
+ try {
184
+ const path = locateFirefoxOrExplain({allowFallback: true})
185
+ console.log(path)
186
+ } catch (e) {
187
+ console.error(String(e))
188
+ // Or print getInstallGuidance() explicitly
189
+ }
190
+ ```
191
+
192
+ **CommonJS:**
193
+
194
+ ```js
195
+ const api = require('firefox-location2')
196
+ const locateFirefox = api.default || api
197
+
198
+ // Strict (Stable only)
199
+ console.log(locateFirefox())
200
+
201
+ // With fallback enabled
202
+ console.log(locateFirefox(true))
166
203
 
167
- console.log(firefoxLocation());
168
- // /Applications/Firefox.app/Contents/MacOS/firefox
204
+ // Helper that throws with guidance
205
+ try {
206
+ const p = (
207
+ api.locateFirefoxOrExplain || ((o) => locateFirefox(o?.allowFallback))
208
+ )({allowFallback: true})
209
+ console.log(p)
210
+ } catch (e) {
211
+ console.error(String(e))
212
+ }
169
213
  ```
170
214
 
171
215
  **Via CLI:**
172
216
 
173
217
  ```bash
174
218
  npx firefox-location2
175
- # /Applications/Firefox.app/Contents/MacOS/firefox
219
+ # Strict (Stable only)
220
+
221
+ npx firefox-location2 --fallback
222
+ # Enable cascade (Stable / ESR / Developer / Nightly)
223
+
224
+ # Output is colorized when printed to a TTY
225
+
226
+ # Respect Puppeteer cache (after you install once):
227
+ npx @puppeteer/browsers install firefox@stable
228
+ npx firefox-location2
176
229
  ```
177
230
 
231
+ ### Environment overrides
232
+
233
+ If this environment variable is set and points to an existing binary, it takes precedence:
234
+
235
+ - `FIREFOX_BINARY`
236
+
237
+ Exit behavior:
238
+
239
+ - Prints the resolved path on success
240
+ - Exits with code 1 and prints a guidance message if nothing suitable is found
241
+
242
+ Notes:
243
+
244
+ - Output is colorized when printed to a TTY (green success, red error)
245
+ - 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
+
178
247
  ## Planned enhancements
179
248
 
180
249
  - Flatpak detection on Linux: detect installed Flatpak app <code>org.mozilla.firefox</code> and expose the appropriate invocation.
@@ -184,12 +253,12 @@ npx firefox-location2
184
253
 
185
254
  ## Related projects
186
255
 
187
- * [chrome-location2](https://github.com/hughsk/chrome-location2)
188
- * [edge-location](https://github.com/cezaraugusto/edge-location)
189
- * [firefox-location](https://github.com/hughsk/firefox-location)
190
- * [brave-location](https://github.com/cezaraugusto/brave-location)
191
- * [vivaldi-location](https://github.com/jandrey/vivaldi-location)
192
- * [opera-location](https://github.com/jandrey/opera-location)
256
+ - [brave-location](https://github.com/cezaraugusto/brave-location)
257
+ - [chrome-location2](https://github.com/cezaraugusto/chrome-location2)
258
+ - [edge-location](https://github.com/cezaraugusto/edge-location)
259
+ - [opera-location2](https://github.com/cezaraugusto/opera-location2)
260
+ - [vivaldi-location2](https://github.com/cezaraugusto/vivaldi-location2)
261
+ - [yandex-location2](https://github.com/cezaraugusto/yandex-location2)
193
262
 
194
263
  ## License
195
264
 
package/bin.js CHANGED
@@ -1,6 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const locateFirefox =
4
- require('./dist/index.cjs').default || require('./dist/index.cjs');
3
+ import locateFirefox, {
4
+ locateFirefoxOrExplain,
5
+ getInstallGuidance,
6
+ } from './dist/index.js';
5
7
 
6
- console.log(locateFirefox());
8
+ const argv = process.argv.slice(2);
9
+ const allowFallback = argv.includes('--fallback') || argv.includes('-f');
10
+
11
+ try {
12
+ const result =
13
+ typeof locateFirefoxOrExplain === 'function'
14
+ ? locateFirefoxOrExplain({ allowFallback })
15
+ : locateFirefox(allowFallback);
16
+
17
+ if (!result)
18
+ throw new Error(
19
+ (typeof getInstallGuidance === 'function' && getInstallGuidance()) ||
20
+ 'No suitable Firefox binary found.',
21
+ );
22
+ console.log(String(result));
23
+ } catch (e) {
24
+ console.error(String(e));
25
+ process.exit(1);
26
+ }
package/dist/index.cjs CHANGED
@@ -33,7 +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
- default: ()=>locateFirefox
36
+ getInstallGuidance: ()=>getInstallGuidance,
37
+ default: ()=>locateFirefox,
38
+ locateFirefoxOrExplain: ()=>locateFirefoxOrExplain
37
39
  });
38
40
  const external_fs_namespaceObject = require("fs");
39
41
  var external_fs_default = /*#__PURE__*/ __webpack_require__.n(external_fs_namespaceObject);
@@ -43,45 +45,143 @@ const external_os_namespaceObject = require("os");
43
45
  var external_os_default = /*#__PURE__*/ __webpack_require__.n(external_os_namespaceObject);
44
46
  const external_which_namespaceObject = require("which");
45
47
  var external_which_default = /*#__PURE__*/ __webpack_require__.n(external_which_namespaceObject);
46
- function locateFirefox(deps) {
48
+ const external_child_process_namespaceObject = require("child_process");
49
+ const external_node_fs_namespaceObject = require("node:fs");
50
+ var external_node_fs_default = /*#__PURE__*/ __webpack_require__.n(external_node_fs_namespaceObject);
51
+ const external_node_path_namespaceObject = require("node:path");
52
+ var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
53
+ function resolveFromPuppeteerCache(deps) {
54
+ const f = (null == deps ? void 0 : deps.fs) ?? external_node_fs_default();
55
+ const env = (null == deps ? void 0 : deps.env) ?? process.env;
56
+ const platform = (null == deps ? void 0 : deps.platform) ?? process.platform;
57
+ try {
58
+ if ('darwin' === platform) {
59
+ const home = (null == deps ? void 0 : deps.homeDir) ?? env.HOME ?? '';
60
+ if (!home) return null;
61
+ const base = external_node_path_default().join(home, 'Library', 'Caches', 'puppeteer', 'firefox');
62
+ const dirs = listDirs(f, base).filter((d)=>d.startsWith('mac-') || d.startsWith('mac_arm-'));
63
+ const candidates = [];
64
+ for (const d of dirs){
65
+ candidates.push(external_node_path_default().join(base, d, 'Firefox.app', 'Contents', 'MacOS', 'firefox'));
66
+ candidates.push(external_node_path_default().join(base, d, 'Firefox Nightly.app', 'Contents', 'MacOS', 'firefox'));
67
+ }
68
+ return firstExisting(f, candidates);
69
+ }
70
+ if ('win32' === platform) {
71
+ const lad = (null == deps ? void 0 : deps.localAppData) ?? env.LOCALAPPDATA;
72
+ if (!lad) return null;
73
+ const base = external_node_path_default().join(lad, 'puppeteer', 'firefox');
74
+ const dirs = listDirs(f, base);
75
+ const preferred = [
76
+ ...dirs.filter((d)=>d.startsWith('win64-')),
77
+ ...dirs.filter((d)=>d.startsWith('win32-'))
78
+ ];
79
+ const candidates = [];
80
+ for (const d of preferred){
81
+ candidates.push(external_node_path_default().join(base, d, 'firefox.exe'));
82
+ candidates.push(external_node_path_default().join(base, d, 'firefox', 'firefox.exe'));
83
+ }
84
+ return firstExisting(f, candidates);
85
+ }
86
+ const xdg = env.XDG_CACHE_HOME;
87
+ const home = (null == deps ? void 0 : deps.homeDir) ?? env.HOME ?? '';
88
+ const cacheBase = xdg || (home ? external_node_path_default().join(home, '.cache') : void 0);
89
+ if (!cacheBase) return null;
90
+ const base = external_node_path_default().join(cacheBase, 'puppeteer', 'firefox');
91
+ const dirs = listDirs(f, base).filter((d)=>d.startsWith('linux-'));
92
+ const candidates = [];
93
+ for (const d of dirs){
94
+ candidates.push(external_node_path_default().join(base, d, 'firefox'));
95
+ candidates.push(external_node_path_default().join(base, d, 'firefox', 'firefox'));
96
+ }
97
+ return firstExisting(f, candidates);
98
+ } catch {
99
+ return null;
100
+ }
101
+ }
102
+ function listDirs(f, dir) {
103
+ try {
104
+ return f.readdirSync(dir, {
105
+ withFileTypes: true
106
+ }).filter((e)=>{
107
+ if (!e) return false;
108
+ const v = e.isDirectory;
109
+ return 'function' == typeof v ? v.call(e) : Boolean(v);
110
+ }).map((e)=>e.name || String(e));
111
+ } catch {
112
+ return [];
113
+ }
114
+ }
115
+ function firstExisting(f, candidates) {
116
+ for (const c of candidates)try {
117
+ if (c && f.existsSync(c)) return c;
118
+ } catch {}
119
+ return null;
120
+ }
121
+ function locateFirefox(allowFallbackOrDeps, depsMaybe) {
122
+ const isBoolean = 'boolean' == typeof allowFallbackOrDeps;
123
+ const allowFallback = isBoolean ? allowFallbackOrDeps : false;
124
+ const deps = isBoolean ? depsMaybe : allowFallbackOrDeps;
47
125
  const f = (null == deps ? void 0 : deps.fs) ?? external_fs_default();
48
126
  const w = (null == deps ? void 0 : deps.which) ?? external_which_default();
49
127
  const o = (null == deps ? void 0 : deps.os) ?? external_os_default();
50
128
  const p = (null == deps ? void 0 : deps.path) ?? external_path_default();
51
129
  const env = (null == deps ? void 0 : deps.env) ?? process.env;
52
130
  const platform = (null == deps ? void 0 : deps.platform) ?? process.platform;
131
+ const override = env.FIREFOX_BINARY;
132
+ if (override && f.existsSync(override)) return override;
53
133
  const osx = 'darwin' === platform;
54
134
  const win = 'win32' === platform;
55
135
  const other = !osx && !win;
56
136
  if (other) {
57
- const candidates = [
58
- 'firefox',
137
+ var _process_env, _process_env1;
138
+ const stable = [
139
+ 'firefox'
140
+ ];
141
+ const fallbacks = [
59
142
  'firefox-esr',
60
143
  'firefox-developer-edition',
61
144
  'firefox-devedition',
62
145
  'firefox-nightly'
63
146
  ];
147
+ const candidates = allowFallback ? [
148
+ ...stable,
149
+ ...fallbacks
150
+ ] : stable;
64
151
  for (const cmd of candidates)try {
65
152
  const resolved = w.sync(cmd);
66
153
  if (resolved) return resolved;
67
154
  } catch (_) {}
68
- const linuxPaths = [
69
- '/usr/bin/firefox',
70
- '/usr/local/bin/firefox',
71
- '/usr/lib/firefox/firefox',
72
- '/snap/bin/firefox',
73
- '/opt/firefox/firefox',
74
- '/usr/local/firefox/firefox',
75
- p.join(o.homedir(), 'bin', 'firefox'),
76
- p.join(o.homedir(), 'Downloads', 'firefox', 'firefox'),
77
- p.join(o.homedir(), '.local', 'share', 'flatpak', 'exports', 'bin', 'org.mozilla.firefox'),
78
- '/var/lib/flatpak/exports/bin/org.mozilla.firefox'
79
- ];
80
- for (const linuxPath of linuxPaths)if (f.existsSync(linuxPath)) return linuxPath;
155
+ if (allowFallback) {
156
+ const linuxPaths = [
157
+ '/usr/bin/firefox',
158
+ '/usr/local/bin/firefox',
159
+ '/usr/lib/firefox/firefox',
160
+ '/snap/bin/firefox',
161
+ '/opt/firefox/firefox',
162
+ '/usr/local/firefox/firefox',
163
+ p.join(o.homedir(), 'bin', 'firefox'),
164
+ p.join(o.homedir(), 'Downloads', 'firefox', 'firefox'),
165
+ p.join(o.homedir(), '.local', 'share', 'flatpak', 'exports', 'bin', 'org.mozilla.firefox'),
166
+ '/var/lib/flatpak/exports/bin/org.mozilla.firefox'
167
+ ];
168
+ for (const linuxPath of linuxPaths)if (f.existsSync(linuxPath)) return linuxPath;
169
+ }
170
+ if (!deps) {
171
+ const viaCache = resolveFromPuppeteerCache();
172
+ if (viaCache) return viaCache;
173
+ }
174
+ const isTestEnv = 'test' === process.env.NODE_ENV || void 0 !== (null == (_process_env = process.env) ? void 0 : _process_env.VITEST) || void 0 !== (null == (_process_env1 = process.env) ? void 0 : _process_env1.JEST_WORKER_ID);
175
+ const skipCliProbe = isTestEnv && 'darwin' === process.platform;
176
+ if (allowFallback && !deps && !skipCliProbe) {
177
+ const viaCLI = resolveFromPuppeteerBrowsersCLI();
178
+ if (viaCLI) return viaCLI;
179
+ }
81
180
  return null;
82
181
  }
83
182
  if (osx) {
84
- const apps = [
183
+ var _process_env2, _process_env3;
184
+ const appsAll = [
85
185
  {
86
186
  app: 'Firefox.app',
87
187
  exec: 'firefox'
@@ -99,6 +199,9 @@ function locateFirefox(deps) {
99
199
  exec: 'firefox'
100
200
  }
101
201
  ];
202
+ const apps = allowFallback ? appsAll : [
203
+ appsAll[0]
204
+ ];
102
205
  const systemBase = '/Applications';
103
206
  const userBase = p.join(o.homedir(), 'Applications');
104
207
  for (const { app, exec } of apps){
@@ -107,25 +210,39 @@ function locateFirefox(deps) {
107
210
  const userPath = `${userBase}/${app}/Contents/MacOS/${exec}`;
108
211
  if (f.existsSync(userPath)) return userPath;
109
212
  }
213
+ if (!deps) {
214
+ const viaCache = resolveFromPuppeteerCache();
215
+ if (viaCache) return viaCache;
216
+ }
217
+ const isTestEnv = 'test' === process.env.NODE_ENV || void 0 !== (null == (_process_env2 = process.env) ? void 0 : _process_env2.VITEST) || void 0 !== (null == (_process_env3 = process.env) ? void 0 : _process_env3.JEST_WORKER_ID);
218
+ const skipCliProbe = isTestEnv && 'darwin' === process.platform;
219
+ if (allowFallback && !deps && !skipCliProbe) {
220
+ const viaCLI = resolveFromPuppeteerBrowsersCLI();
221
+ if (viaCLI) return viaCLI;
222
+ }
110
223
  return null;
111
224
  }
112
225
  {
226
+ var _process_env4, _process_env5;
113
227
  const prefixes = [
114
228
  env.LOCALAPPDATA,
115
229
  env.PROGRAMFILES,
116
230
  env['PROGRAMFILES(X86)']
117
231
  ].filter(Boolean);
118
- const suffixes = [
232
+ const suffixesAll = [
119
233
  p.join('Mozilla Firefox', 'firefox.exe'),
120
234
  p.join('Mozilla Firefox ESR', 'firefox.exe'),
121
235
  p.join('Mozilla Firefox Developer Edition', 'firefox.exe'),
122
236
  p.join('Firefox Nightly', 'firefox.exe')
123
237
  ];
238
+ const suffixes = allowFallback ? suffixesAll : [
239
+ suffixesAll[0]
240
+ ];
124
241
  for (const prefix of prefixes)for (const suffix of suffixes){
125
242
  const exePath = p.join(prefix, suffix);
126
243
  if (f.existsSync(exePath)) return exePath;
127
244
  }
128
- const defaultPaths = [
245
+ const defaultPathsAll = [
129
246
  'C:\\Program Files\\Mozilla Firefox\\firefox.exe',
130
247
  'C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe',
131
248
  'C:\\Program Files\\Mozilla Firefox ESR\\firefox.exe',
@@ -135,13 +252,91 @@ function locateFirefox(deps) {
135
252
  'C:\\Program Files\\Firefox Nightly\\firefox.exe',
136
253
  'C:\\Program Files (x86)\\Firefox Nightly\\firefox.exe'
137
254
  ];
255
+ const defaultPaths = allowFallback ? defaultPathsAll : defaultPathsAll.slice(0, 2);
138
256
  for (const defaultPath of defaultPaths)if (f.existsSync(defaultPath)) return defaultPath;
257
+ if (!deps) {
258
+ const viaCache = resolveFromPuppeteerCache();
259
+ if (viaCache) return viaCache;
260
+ }
261
+ const isTestEnv = 'test' === process.env.NODE_ENV || void 0 !== (null == (_process_env4 = process.env) ? void 0 : _process_env4.VITEST) || void 0 !== (null == (_process_env5 = process.env) ? void 0 : _process_env5.JEST_WORKER_ID);
262
+ const skipCliProbe = isTestEnv && 'darwin' === process.platform;
263
+ if (allowFallback && !deps && !skipCliProbe) {
264
+ const viaCLI = resolveFromPuppeteerBrowsersCLI();
265
+ if (viaCLI) return viaCLI;
266
+ }
139
267
  return null;
140
268
  }
141
269
  }
270
+ function getInstallGuidance() {
271
+ return "We couldn't find a Firefox browser on this machine.\n\nHere's the fastest way to get set up:\n\n1) Install Firefox via Puppeteer Browsers (recommended for CI/dev)\n npx @puppeteer/browsers install firefox@stable\n\nThen re-run your command — we will detect it automatically.\n\nAlternatively, install Firefox using your OS package manager and re-run.";
272
+ }
273
+ function locateFirefoxOrExplain(options) {
274
+ const allowFallback = 'boolean' == typeof options ? options : Boolean(null == options ? void 0 : options.allowFallback);
275
+ const found = locateFirefox(allowFallback) || locateFirefox(true);
276
+ if ('string' == typeof found && found) return found;
277
+ throw new Error(getInstallGuidance());
278
+ }
279
+ function resolveFromPuppeteerBrowsersCLI() {
280
+ try {
281
+ const attempts = [
282
+ {
283
+ cmd: 'npx',
284
+ args: [
285
+ '-y',
286
+ '@puppeteer/browsers',
287
+ 'path',
288
+ 'firefox@stable'
289
+ ]
290
+ },
291
+ {
292
+ cmd: 'pnpm',
293
+ args: [
294
+ 'dlx',
295
+ '@puppeteer/browsers',
296
+ 'path',
297
+ 'firefox@stable'
298
+ ]
299
+ },
300
+ {
301
+ cmd: 'yarn',
302
+ args: [
303
+ 'dlx',
304
+ '@puppeteer/browsers',
305
+ 'path',
306
+ 'firefox@stable'
307
+ ]
308
+ },
309
+ {
310
+ cmd: 'bunx',
311
+ args: [
312
+ '@puppeteer/browsers',
313
+ 'path',
314
+ 'firefox@stable'
315
+ ]
316
+ }
317
+ ];
318
+ for (const { cmd, args } of attempts)try {
319
+ const out = (0, external_child_process_namespaceObject.execFileSync)(cmd, args, {
320
+ encoding: 'utf8',
321
+ stdio: [
322
+ 'ignore',
323
+ 'pipe',
324
+ 'ignore'
325
+ ],
326
+ timeout: 2000
327
+ }).trim();
328
+ if (out && external_fs_default().existsSync(out)) return out;
329
+ } catch {}
330
+ } catch {}
331
+ return null;
332
+ }
142
333
  exports["default"] = __webpack_exports__["default"];
334
+ exports.getInstallGuidance = __webpack_exports__.getInstallGuidance;
335
+ exports.locateFirefoxOrExplain = __webpack_exports__.locateFirefoxOrExplain;
143
336
  for(var __webpack_i__ in __webpack_exports__)if (-1 === [
144
- "default"
337
+ "default",
338
+ "getInstallGuidance",
339
+ "locateFirefoxOrExplain"
145
340
  ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
146
341
  Object.defineProperty(exports, '__esModule', {
147
342
  value: true
package/dist/index.d.ts CHANGED
@@ -16,4 +16,8 @@ export type Deps = {
16
16
  env?: NodeJS.ProcessEnv;
17
17
  platform?: NodeJS.Platform;
18
18
  };
19
- export default function locateFirefox(deps?: Deps): string | null;
19
+ export default function locateFirefox(allowFallbackOrDeps?: boolean | Deps, depsMaybe?: Deps): string | null;
20
+ export declare function getInstallGuidance(): string;
21
+ export declare function locateFirefoxOrExplain(options?: boolean | {
22
+ allowFallback?: boolean;
23
+ }): string;
package/dist/index.js CHANGED
@@ -2,45 +2,141 @@ import * as __WEBPACK_EXTERNAL_MODULE_fs__ from "fs";
2
2
  import * as __WEBPACK_EXTERNAL_MODULE_path__ from "path";
3
3
  import * as __WEBPACK_EXTERNAL_MODULE_os__ from "os";
4
4
  import * as __WEBPACK_EXTERNAL_MODULE_which__ from "which";
5
- function locateFirefox(deps) {
5
+ import * as __WEBPACK_EXTERNAL_MODULE_child_process__ from "child_process";
6
+ import * as __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__ from "node:fs";
7
+ import * as __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__ from "node:path";
8
+ function resolveFromPuppeteerCache(deps) {
9
+ const f = (null == deps ? void 0 : deps.fs) ?? __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"];
10
+ const env = (null == deps ? void 0 : deps.env) ?? process.env;
11
+ const platform = (null == deps ? void 0 : deps.platform) ?? process.platform;
12
+ try {
13
+ if ('darwin' === platform) {
14
+ const home = (null == deps ? void 0 : deps.homeDir) ?? env.HOME ?? '';
15
+ if (!home) return null;
16
+ const base = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(home, 'Library', 'Caches', 'puppeteer', 'firefox');
17
+ const dirs = listDirs(f, base).filter((d)=>d.startsWith('mac-') || d.startsWith('mac_arm-'));
18
+ const candidates = [];
19
+ for (const d of dirs){
20
+ candidates.push(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(base, d, 'Firefox.app', 'Contents', 'MacOS', 'firefox'));
21
+ candidates.push(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(base, d, 'Firefox Nightly.app', 'Contents', 'MacOS', 'firefox'));
22
+ }
23
+ return firstExisting(f, candidates);
24
+ }
25
+ if ('win32' === platform) {
26
+ const lad = (null == deps ? void 0 : deps.localAppData) ?? env.LOCALAPPDATA;
27
+ if (!lad) return null;
28
+ const base = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(lad, 'puppeteer', 'firefox');
29
+ const dirs = listDirs(f, base);
30
+ const preferred = [
31
+ ...dirs.filter((d)=>d.startsWith('win64-')),
32
+ ...dirs.filter((d)=>d.startsWith('win32-'))
33
+ ];
34
+ const candidates = [];
35
+ for (const d of preferred){
36
+ candidates.push(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(base, d, 'firefox.exe'));
37
+ candidates.push(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(base, d, 'firefox', 'firefox.exe'));
38
+ }
39
+ return firstExisting(f, candidates);
40
+ }
41
+ const xdg = env.XDG_CACHE_HOME;
42
+ const home = (null == deps ? void 0 : deps.homeDir) ?? env.HOME ?? '';
43
+ const cacheBase = xdg || (home ? __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(home, '.cache') : void 0);
44
+ if (!cacheBase) return null;
45
+ const base = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(cacheBase, 'puppeteer', 'firefox');
46
+ const dirs = listDirs(f, base).filter((d)=>d.startsWith('linux-'));
47
+ const candidates = [];
48
+ for (const d of dirs){
49
+ candidates.push(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(base, d, 'firefox'));
50
+ candidates.push(__WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(base, d, 'firefox', 'firefox'));
51
+ }
52
+ return firstExisting(f, candidates);
53
+ } catch {
54
+ return null;
55
+ }
56
+ }
57
+ function listDirs(f, dir) {
58
+ try {
59
+ return f.readdirSync(dir, {
60
+ withFileTypes: true
61
+ }).filter((e)=>{
62
+ if (!e) return false;
63
+ const v = e.isDirectory;
64
+ return 'function' == typeof v ? v.call(e) : Boolean(v);
65
+ }).map((e)=>e.name || String(e));
66
+ } catch {
67
+ return [];
68
+ }
69
+ }
70
+ function firstExisting(f, candidates) {
71
+ for (const c of candidates)try {
72
+ if (c && f.existsSync(c)) return c;
73
+ } catch {}
74
+ return null;
75
+ }
76
+ function locateFirefox(allowFallbackOrDeps, depsMaybe) {
77
+ const isBoolean = 'boolean' == typeof allowFallbackOrDeps;
78
+ const allowFallback = isBoolean ? allowFallbackOrDeps : false;
79
+ const deps = isBoolean ? depsMaybe : allowFallbackOrDeps;
6
80
  const f = (null == deps ? void 0 : deps.fs) ?? __WEBPACK_EXTERNAL_MODULE_fs__["default"];
7
81
  const w = (null == deps ? void 0 : deps.which) ?? __WEBPACK_EXTERNAL_MODULE_which__["default"];
8
82
  const o = (null == deps ? void 0 : deps.os) ?? __WEBPACK_EXTERNAL_MODULE_os__["default"];
9
83
  const p = (null == deps ? void 0 : deps.path) ?? __WEBPACK_EXTERNAL_MODULE_path__["default"];
10
84
  const env = (null == deps ? void 0 : deps.env) ?? process.env;
11
85
  const platform = (null == deps ? void 0 : deps.platform) ?? process.platform;
86
+ const override = env.FIREFOX_BINARY;
87
+ if (override && f.existsSync(override)) return override;
12
88
  const osx = 'darwin' === platform;
13
89
  const win = 'win32' === platform;
14
90
  const other = !osx && !win;
15
91
  if (other) {
16
- const candidates = [
17
- 'firefox',
92
+ var _process_env, _process_env1;
93
+ const stable = [
94
+ 'firefox'
95
+ ];
96
+ const fallbacks = [
18
97
  'firefox-esr',
19
98
  'firefox-developer-edition',
20
99
  'firefox-devedition',
21
100
  'firefox-nightly'
22
101
  ];
102
+ const candidates = allowFallback ? [
103
+ ...stable,
104
+ ...fallbacks
105
+ ] : stable;
23
106
  for (const cmd of candidates)try {
24
107
  const resolved = w.sync(cmd);
25
108
  if (resolved) return resolved;
26
109
  } catch (_) {}
27
- const linuxPaths = [
28
- '/usr/bin/firefox',
29
- '/usr/local/bin/firefox',
30
- '/usr/lib/firefox/firefox',
31
- '/snap/bin/firefox',
32
- '/opt/firefox/firefox',
33
- '/usr/local/firefox/firefox',
34
- p.join(o.homedir(), 'bin', 'firefox'),
35
- p.join(o.homedir(), 'Downloads', 'firefox', 'firefox'),
36
- p.join(o.homedir(), '.local', 'share', 'flatpak', 'exports', 'bin', 'org.mozilla.firefox'),
37
- '/var/lib/flatpak/exports/bin/org.mozilla.firefox'
38
- ];
39
- for (const linuxPath of linuxPaths)if (f.existsSync(linuxPath)) return linuxPath;
110
+ if (allowFallback) {
111
+ const linuxPaths = [
112
+ '/usr/bin/firefox',
113
+ '/usr/local/bin/firefox',
114
+ '/usr/lib/firefox/firefox',
115
+ '/snap/bin/firefox',
116
+ '/opt/firefox/firefox',
117
+ '/usr/local/firefox/firefox',
118
+ p.join(o.homedir(), 'bin', 'firefox'),
119
+ p.join(o.homedir(), 'Downloads', 'firefox', 'firefox'),
120
+ p.join(o.homedir(), '.local', 'share', 'flatpak', 'exports', 'bin', 'org.mozilla.firefox'),
121
+ '/var/lib/flatpak/exports/bin/org.mozilla.firefox'
122
+ ];
123
+ for (const linuxPath of linuxPaths)if (f.existsSync(linuxPath)) return linuxPath;
124
+ }
125
+ if (!deps) {
126
+ const viaCache = resolveFromPuppeteerCache();
127
+ if (viaCache) return viaCache;
128
+ }
129
+ const isTestEnv = 'test' === process.env.NODE_ENV || void 0 !== (null == (_process_env = process.env) ? void 0 : _process_env.VITEST) || void 0 !== (null == (_process_env1 = process.env) ? void 0 : _process_env1.JEST_WORKER_ID);
130
+ const skipCliProbe = isTestEnv && 'darwin' === process.platform;
131
+ if (allowFallback && !deps && !skipCliProbe) {
132
+ const viaCLI = resolveFromPuppeteerBrowsersCLI();
133
+ if (viaCLI) return viaCLI;
134
+ }
40
135
  return null;
41
136
  }
42
137
  if (osx) {
43
- const apps = [
138
+ var _process_env2, _process_env3;
139
+ const appsAll = [
44
140
  {
45
141
  app: 'Firefox.app',
46
142
  exec: 'firefox'
@@ -58,6 +154,9 @@ function locateFirefox(deps) {
58
154
  exec: 'firefox'
59
155
  }
60
156
  ];
157
+ const apps = allowFallback ? appsAll : [
158
+ appsAll[0]
159
+ ];
61
160
  const systemBase = '/Applications';
62
161
  const userBase = p.join(o.homedir(), 'Applications');
63
162
  for (const { app, exec } of apps){
@@ -66,25 +165,39 @@ function locateFirefox(deps) {
66
165
  const userPath = `${userBase}/${app}/Contents/MacOS/${exec}`;
67
166
  if (f.existsSync(userPath)) return userPath;
68
167
  }
168
+ if (!deps) {
169
+ const viaCache = resolveFromPuppeteerCache();
170
+ if (viaCache) return viaCache;
171
+ }
172
+ const isTestEnv = 'test' === process.env.NODE_ENV || void 0 !== (null == (_process_env2 = process.env) ? void 0 : _process_env2.VITEST) || void 0 !== (null == (_process_env3 = process.env) ? void 0 : _process_env3.JEST_WORKER_ID);
173
+ const skipCliProbe = isTestEnv && 'darwin' === process.platform;
174
+ if (allowFallback && !deps && !skipCliProbe) {
175
+ const viaCLI = resolveFromPuppeteerBrowsersCLI();
176
+ if (viaCLI) return viaCLI;
177
+ }
69
178
  return null;
70
179
  }
71
180
  {
181
+ var _process_env4, _process_env5;
72
182
  const prefixes = [
73
183
  env.LOCALAPPDATA,
74
184
  env.PROGRAMFILES,
75
185
  env['PROGRAMFILES(X86)']
76
186
  ].filter(Boolean);
77
- const suffixes = [
187
+ const suffixesAll = [
78
188
  p.join('Mozilla Firefox', 'firefox.exe'),
79
189
  p.join('Mozilla Firefox ESR', 'firefox.exe'),
80
190
  p.join('Mozilla Firefox Developer Edition', 'firefox.exe'),
81
191
  p.join('Firefox Nightly', 'firefox.exe')
82
192
  ];
193
+ const suffixes = allowFallback ? suffixesAll : [
194
+ suffixesAll[0]
195
+ ];
83
196
  for (const prefix of prefixes)for (const suffix of suffixes){
84
197
  const exePath = p.join(prefix, suffix);
85
198
  if (f.existsSync(exePath)) return exePath;
86
199
  }
87
- const defaultPaths = [
200
+ const defaultPathsAll = [
88
201
  'C:\\Program Files\\Mozilla Firefox\\firefox.exe',
89
202
  'C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe',
90
203
  'C:\\Program Files\\Mozilla Firefox ESR\\firefox.exe',
@@ -94,8 +207,82 @@ function locateFirefox(deps) {
94
207
  'C:\\Program Files\\Firefox Nightly\\firefox.exe',
95
208
  'C:\\Program Files (x86)\\Firefox Nightly\\firefox.exe'
96
209
  ];
210
+ const defaultPaths = allowFallback ? defaultPathsAll : defaultPathsAll.slice(0, 2);
97
211
  for (const defaultPath of defaultPaths)if (f.existsSync(defaultPath)) return defaultPath;
212
+ if (!deps) {
213
+ const viaCache = resolveFromPuppeteerCache();
214
+ if (viaCache) return viaCache;
215
+ }
216
+ const isTestEnv = 'test' === process.env.NODE_ENV || void 0 !== (null == (_process_env4 = process.env) ? void 0 : _process_env4.VITEST) || void 0 !== (null == (_process_env5 = process.env) ? void 0 : _process_env5.JEST_WORKER_ID);
217
+ const skipCliProbe = isTestEnv && 'darwin' === process.platform;
218
+ if (allowFallback && !deps && !skipCliProbe) {
219
+ const viaCLI = resolveFromPuppeteerBrowsersCLI();
220
+ if (viaCLI) return viaCLI;
221
+ }
98
222
  return null;
99
223
  }
100
224
  }
101
- export { locateFirefox as default };
225
+ function getInstallGuidance() {
226
+ return "We couldn't find a Firefox browser on this machine.\n\nHere's the fastest way to get set up:\n\n1) Install Firefox via Puppeteer Browsers (recommended for CI/dev)\n npx @puppeteer/browsers install firefox@stable\n\nThen re-run your command — we will detect it automatically.\n\nAlternatively, install Firefox using your OS package manager and re-run.";
227
+ }
228
+ function locateFirefoxOrExplain(options) {
229
+ const allowFallback = 'boolean' == typeof options ? options : Boolean(null == options ? void 0 : options.allowFallback);
230
+ const found = locateFirefox(allowFallback) || locateFirefox(true);
231
+ if ('string' == typeof found && found) return found;
232
+ throw new Error(getInstallGuidance());
233
+ }
234
+ function resolveFromPuppeteerBrowsersCLI() {
235
+ try {
236
+ const attempts = [
237
+ {
238
+ cmd: 'npx',
239
+ args: [
240
+ '-y',
241
+ '@puppeteer/browsers',
242
+ 'path',
243
+ 'firefox@stable'
244
+ ]
245
+ },
246
+ {
247
+ cmd: 'pnpm',
248
+ args: [
249
+ 'dlx',
250
+ '@puppeteer/browsers',
251
+ 'path',
252
+ 'firefox@stable'
253
+ ]
254
+ },
255
+ {
256
+ cmd: 'yarn',
257
+ args: [
258
+ 'dlx',
259
+ '@puppeteer/browsers',
260
+ 'path',
261
+ 'firefox@stable'
262
+ ]
263
+ },
264
+ {
265
+ cmd: 'bunx',
266
+ args: [
267
+ '@puppeteer/browsers',
268
+ 'path',
269
+ 'firefox@stable'
270
+ ]
271
+ }
272
+ ];
273
+ for (const { cmd, args } of attempts)try {
274
+ const out = (0, __WEBPACK_EXTERNAL_MODULE_child_process__.execFileSync)(cmd, args, {
275
+ encoding: 'utf8',
276
+ stdio: [
277
+ 'ignore',
278
+ 'pipe',
279
+ 'ignore'
280
+ ],
281
+ timeout: 2000
282
+ }).trim();
283
+ if (out && __WEBPACK_EXTERNAL_MODULE_fs__["default"].existsSync(out)) return out;
284
+ } catch {}
285
+ } catch {}
286
+ return null;
287
+ }
288
+ export { locateFirefox as default, getInstallGuidance, locateFirefoxOrExplain };
@@ -0,0 +1,11 @@
1
+ import fs from 'node:fs';
2
+ type FsLike = Pick<typeof fs, 'existsSync' | 'readdirSync'>;
3
+ type EnvLike = NodeJS.ProcessEnv;
4
+ export declare function resolveFromPuppeteerCache(deps?: {
5
+ fs?: FsLike;
6
+ env?: EnvLike;
7
+ platform?: NodeJS.Platform;
8
+ homeDir?: string;
9
+ localAppData?: string;
10
+ }): string | null;
11
+ export {};
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.0",
8
+ "version": "2.0.4",
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",
@@ -26,13 +26,17 @@
26
26
  },
27
27
  "scripts": {
28
28
  "build": "rslib build",
29
- "check": "biome check --write",
29
+ "check": "pnpm dlx @biomejs/biome@1.9.4 check --write",
30
30
  "dev": "rslib build --watch",
31
- "format": "biome format --write",
32
- "test": "vitest run",
31
+ "format": "pnpm dlx @biomejs/biome@1.9.4 format --write",
32
+ "test": "vitest run --pool vmForks --poolOptions.vmForks.singleFork",
33
33
  "prepublishOnly": "npm run build",
34
34
  "publish:provenance": "np patch --no-tests --any-branch --yolo --message 'release: %s' --provenance"
35
35
  },
36
+ "engines": {
37
+ "node": ">=18 <23"
38
+ },
39
+ "engineStrict": true,
36
40
  "publishConfig": {
37
41
  "provenance": true
38
42
  },