1- import { readFile , access , constants } from 'node:fs/promises'
2- import stripJsonComments from 'strip-json-comments'
31import type { ConsolaInstance } from 'consola'
42import type { VueFireNuxtModuleOptionsResolved } from './options'
53
6- export async function willUseEmulators (
7- { emulators } : VueFireNuxtModuleOptionsResolved ,
8- firebaseJsonPath : string ,
4+ /**
5+ * Detects the emulators to enable based on their API. Returns an object of all the emulators that should be enabled.
6+ *
7+ * @param options - The module options
8+ * @param logger - The logger instance
9+ */
10+ export async function autodetectEmulators (
11+ { emulators : options , auth } : VueFireNuxtModuleOptionsResolved ,
912 logger : ConsolaInstance
10- ) : Promise < NonNullable < FirebaseEmulatorsJSON [ 'emulators' ] > | null > {
13+ ) {
14+ const defaultHost : string = options . host || '127.0.0.1'
15+
1116 const isEmulatorEnabled =
1217 // emulators is always defined
13- emulators . enabled &&
18+ options . enabled &&
1419 // Disable emulators on production unless the user explicitly enables them
1520 ( process . env . NODE_ENV !== 'production' ||
1621 ( process . env . VUEFIRE_EMULATORS &&
@@ -21,66 +26,33 @@ export async function willUseEmulators(
2126 return null
2227 }
2328
24- // return true if the file doesn't exist instead of throwing
25- if ( await access ( firebaseJsonPath , constants . F_OK ) . catch ( ( ) => true ) ) {
26- logger . warn (
27- `The "firebase.json" file doesn't exist at "${ firebaseJsonPath } ".`
28- )
29- return null
30- }
31-
32- let firebaseJson : FirebaseEmulatorsJSON | null = null
33- try {
34- firebaseJson = JSON . parse (
35- stripJsonComments ( await readFile ( firebaseJsonPath , 'utf8' ) , {
36- trailingCommas : true ,
37- } )
38- )
39- } catch ( err ) {
40- logger . error ( 'Error parsing the `firebase.json` file' , err )
41- logger . error ( 'Cannot enable Emulators' )
42- }
43-
44- return firebaseJson ?. emulators ?? null
45- }
46-
47- /**
48- * Detects the emulators to enable based on the `firebase.json` file. Returns an object of all the emulators that should
49- * be enabled based on the `firebase.json` file and other options and environment variables.
50- *
51- * @param options - The module options
52- * @param firebaseJsonPath - resolved path to the `firebase.json` file
53- * @param logger - The logger instance
54- */
55- export function detectEmulators (
56- {
57- emulators : _vuefireEmulatorsOptions ,
58- auth,
59- } : VueFireNuxtModuleOptionsResolved ,
60- firebaseEmulatorsConfig : NonNullable < FirebaseEmulatorsJSON [ 'emulators' ] > ,
61- logger : ConsolaInstance
62- ) {
63- // normalize the emulators option
64- const vuefireEmulatorsOptions =
65- typeof _vuefireEmulatorsOptions === 'object'
66- ? _vuefireEmulatorsOptions
67- : {
68- enabled : _vuefireEmulatorsOptions ,
69- }
29+ const emulatorsResponse : EmulatorsAPIResponse | null = await fetch (
30+ `http://${ defaultHost } :4400/emulators`
31+ )
32+ . then ( ( res ) => {
33+ return res . status === 200 ? res . json ( ) : null
34+ } )
35+ . catch ( ( err : Error ) => {
36+ // skip errors of emulators not running
37+ if (
38+ err instanceof Error &&
39+ typeof err . cause === 'object' &&
40+ // @ts -expect-error: not in the types
41+ err . cause ?. code !== 'ECONNREFUSED'
42+ ) {
43+ logger . error ( 'Error fetching emulators' , err )
44+ }
45+ return null
46+ } )
7047
71- if ( ! firebaseEmulatorsConfig ) {
72- if ( vuefireEmulatorsOptions . enabled !== false ) {
73- logger . warn (
74- 'You enabled emulators but there is no `emulators` key in your `firebase.json` file. Emulators will not be enabled.'
75- )
76- }
77- return
48+ if ( ! emulatorsResponse ) {
49+ return null
7850 }
7951
80- const defaultHost : string = vuefireEmulatorsOptions . host || '127.0.0.1'
81-
8252 const emulatorsToEnable = services . reduce ( ( acc , service ) => {
83- if ( firebaseEmulatorsConfig [ service ] ) {
53+ if ( emulatorsResponse [ service ] ) {
54+ let { host, port } = emulatorsResponse [ service ] !
55+
8456 // these env variables are automatically picked up by the admin SDK too
8557 // https://firebase.google.com/docs/emulator-suite/connect_rtdb?hl=en&authuser=0#admin_sdks
8658 // Also, Firestore is the only one that has a different env variable
@@ -89,8 +61,6 @@ export function detectEmulators(
8961 ? 'FIRESTORE_EMULATOR_HOST'
9062 : `FIREBASE_${ service . toUpperCase ( ) } _EMULATOR_HOST`
9163
92- let host : string | undefined
93- let port : number | undefined
9464 // Pick up the values from the env variables if set by the user
9565 if ( process . env [ envKey ] ) {
9666 logger . debug (
@@ -110,52 +80,7 @@ export function detectEmulators(
11080 }
11181 }
11282
113- // take the values from the firebase.json file
114- const emulatorsServiceConfig = firebaseEmulatorsConfig [ service ]
115- // they might be picked up from the environment variables
116- host ??= emulatorsServiceConfig ?. host || defaultHost
117- port ??= emulatorsServiceConfig ?. port
118-
119- const missingHostServices : FirebaseEmulatorService [ ] = [ ]
120- if ( emulatorsServiceConfig ?. host == null ) {
121- // we push to warn later in one single warning
122- missingHostServices . push ( service )
123- } else if ( emulatorsServiceConfig . host !== host ) {
124- logger . error (
125- `The "${ service } " emulator is enabled but the "host" property in the "emulators.${ service } " section of your "firebase.json" file is different from the "vuefire.emulators.host" value. You might encounter errors in your app if this is not fixed.`
126- )
127- }
128-
129- // The default value is 127.0.0.1, so it's fine if the user doesn't set it at all
130- if ( missingHostServices . length > 0 && host !== '127.0.0.1' ) {
131- logger . warn (
132- `The "${ service . at (
133- 0
134- ) ! } " emulator is enabled but there is no "host" key in the "emulators.${ service } " key of your "firebase.json" file. It is recommended to set it to avoid mismatches between origins. You should probably set it to "${ defaultHost } " ("vuefire.emulators.host" value).` +
135- ( missingHostServices . length > 1
136- ? ` The following emulators are also missing the "host" key: ${ missingHostServices
137- . slice ( 1 )
138- . join ( ', ' ) } .`
139- : '' )
140- )
141- }
142-
143- if ( ! port ) {
144- logger . error (
145- `The "${ service } " emulator is enabled but there is no "port" property in the "emulators" section of your "firebase.json" file. It must be specified to enable emulators. The "${ service } " emulator won't be enabled.`
146- )
147- return acc
148- // if the port is set in the config, it must match the env variable
149- } else if (
150- emulatorsServiceConfig &&
151- emulatorsServiceConfig . port !== port
152- ) {
153- logger . error (
154- `The "${ service } " emulator is enabled but the "port" property in the "emulators.${ service } " section of your "firebase.json" file is different from the "${ envKey } " env variable. You might encounter errors in your app if this is not fixed.`
155- )
156- }
157-
158- // add the emulator to the list
83+ // add them
15984 acc [ service ] = { host, port }
16085 }
16186 return acc
@@ -177,67 +102,6 @@ export function detectEmulators(
177102 return emulatorsToEnable
178103}
179104
180- /**
181- * Extracted from as we cannot install firebase-tools just for the types
182- * - https://github.com/firebase/firebase-tools/blob/master/src/firebaseConfig.ts#L183
183- * - https://github.com/firebase/firebase-tools/blob/master/schema/firebase-config.json
184- * @internal
185- */
186- export interface FirebaseEmulatorsJSON {
187- emulators ?: {
188- auth ?: {
189- host ?: string
190- port ?: number
191- }
192- database ?: {
193- host ?: string
194- port ?: number
195- }
196- eventarc ?: {
197- host ?: string
198- port ?: number
199- }
200- extensions ?: {
201- [ k : string ] : unknown
202- }
203- firestore ?: {
204- host ?: string
205- port ?: number
206- websocketPort ?: number
207- }
208- functions ?: {
209- host ?: string
210- port ?: number
211- }
212- hosting ?: {
213- host ?: string
214- port ?: number
215- }
216- hub ?: {
217- host ?: string
218- port ?: number
219- }
220- logging ?: {
221- host ?: string
222- port ?: number
223- }
224- pubsub ?: {
225- host ?: string
226- port ?: number
227- }
228- singleProjectMode ?: boolean
229- storage ?: {
230- host ?: string
231- port ?: number
232- }
233- ui ?: {
234- enabled ?: boolean
235- host ?: string
236- port ?: string | number
237- }
238- }
239- }
240-
241105export type FirebaseEmulatorService =
242106 | 'auth'
243107 | 'database'
@@ -266,3 +130,32 @@ export interface FirebaseEmulatorsToEnable
266130 options ?: Parameters < typeof import ( 'firebase/auth' ) . connectAuthEmulator > [ 2 ]
267131 }
268132}
133+
134+ interface EmulatorServiceAddressInfo {
135+ address : string
136+ family : string // Assuming this will contain valid IPv4 or IPv6 strings
137+ port : number
138+ }
139+
140+ interface EmulatorService {
141+ listen : EmulatorServiceAddressInfo [ ]
142+ name : string
143+ host : string
144+ port : number
145+ pid ?: number // Assuming this field is optional
146+ reservedPorts ?: number [ ] // Assuming this field is optional and can be an array of numbers
147+ webSocketHost ?: string // Assuming this field is optional
148+ webSocketPort ?: number // Assuming this field is optional
149+ }
150+
151+ interface EmulatorsAPIResponse {
152+ hub ?: EmulatorService
153+ ui ?: EmulatorService
154+ logging ?: EmulatorService
155+ auth ?: EmulatorService
156+ functions ?: EmulatorService
157+ firestore ?: EmulatorService
158+ database ?: EmulatorService
159+ hosting ?: EmulatorService
160+ storage ?: EmulatorService
161+ }
0 commit comments