@@ -17,6 +17,7 @@ import * as runtimes from "..";
17
17
import * as validate from "./validate" ;
18
18
import * as versioning from "./versioning" ;
19
19
import * as parseTriggers from "./parseTriggers" ;
20
+ import { fileExistsSync } from "../../../../fsutils" ;
20
21
21
22
const MIN_FUNCTIONS_SDK_VERSION = "3.20.0" ;
22
23
@@ -169,33 +170,51 @@ export class Delegate {
169
170
if ( Object . keys ( config || { } ) . length ) {
170
171
env . CLOUD_RUNTIME_CONFIG = JSON . stringify ( config ) ;
171
172
}
172
- // At this point, we've already confirmed that we found supported firebase functions sdk.
173
+ // Location of the binary included in the Firebase Functions SDK
174
+ // differs depending on the developer's setup and choice of package manager.
175
+ //
176
+ // We'll try few routes in the following order:
177
+ //
178
+ // 1. $SOURCE_DIR/node_modules/.bin/firebase-functions
179
+ // 2. node_modules closest to the resolved path ${require.resolve("firebase-functions")}
180
+ //
181
+ // (1) works for most package managers (npm, yarn[no-hoist],pnpm).
182
+ // (2) handles cases where developer prefers monorepo setup or bundled function code.
183
+ const sourceNodeModulesPath = path . join ( this . sourceDir , "node_modules" ) ;
173
184
const sdkPath = require . resolve ( "firebase-functions" , { paths : [ this . sourceDir ] } ) ;
174
- // Find location of the closest node_modules/ directory where we found the sdk.
175
- const binPath = sdkPath . substring ( 0 , sdkPath . lastIndexOf ( "node_modules" ) + 12 ) ;
176
- // And execute the binary included in the sdk.
177
- const childProcess = spawn ( path . join ( binPath , ".bin" , "firebase-functions" ) , [ this . sourceDir ] , {
178
- env,
179
- cwd : this . sourceDir ,
180
- stdio : [ /* stdin=*/ "ignore" , /* stdout=*/ "pipe" , /* stderr=*/ "inherit" ] ,
181
- } ) ;
182
- childProcess . stdout ?. on ( "data" , ( chunk ) => {
183
- logger . debug ( chunk . toString ( ) ) ;
184
- } ) ;
185
- return Promise . resolve ( async ( ) => {
186
- const p = new Promise < void > ( ( resolve , reject ) => {
187
- childProcess . once ( "exit" , resolve ) ;
188
- childProcess . once ( "error" , reject ) ;
189
- } ) ;
190
-
191
- await fetch ( `http://localhost:${ port } /__/quitquitquit` ) ;
192
- setTimeout ( ( ) => {
193
- if ( ! childProcess . killed ) {
194
- childProcess . kill ( "SIGKILL" ) ;
195
- }
196
- } , 10_000 ) ;
197
- return p ;
198
- } ) ;
185
+ const sdkNodeModulesPath = sdkPath . substring ( 0 , sdkPath . lastIndexOf ( "node_modules" ) + 12 ) ;
186
+ for ( const nodeModulesPath of [ sourceNodeModulesPath , sdkNodeModulesPath ] ) {
187
+ const binPath = path . join ( nodeModulesPath , ".bin" , "firebase-functions" ) ;
188
+ if ( fileExistsSync ( binPath ) ) {
189
+ logger . debug ( `Found firebase-functions binary at '${ binPath } '` ) ;
190
+ const childProcess = spawn ( binPath , [ this . sourceDir ] , {
191
+ env,
192
+ cwd : this . sourceDir ,
193
+ stdio : [ /* stdin=*/ "ignore" , /* stdout=*/ "pipe" , /* stderr=*/ "inherit" ] ,
194
+ } ) ;
195
+ childProcess . stdout ?. on ( "data" , ( chunk ) => {
196
+ logger . debug ( chunk . toString ( ) ) ;
197
+ } ) ;
198
+ return Promise . resolve ( async ( ) => {
199
+ const p = new Promise < void > ( ( resolve , reject ) => {
200
+ childProcess . once ( "exit" , resolve ) ;
201
+ childProcess . once ( "error" , reject ) ;
202
+ } ) ;
203
+
204
+ await fetch ( `http://localhost:${ port } /__/quitquitquit` ) ;
205
+ setTimeout ( ( ) => {
206
+ if ( ! childProcess . killed ) {
207
+ childProcess . kill ( "SIGKILL" ) ;
208
+ }
209
+ } , 10_000 ) ;
210
+ return p ;
211
+ } ) ;
212
+ }
213
+ }
214
+ throw new FirebaseError (
215
+ "Failed to find location of Firebase Functions SDK. " +
216
+ "Please file a bug on Github (https://github.com/firebase/firebase-tools/)."
217
+ ) ;
199
218
}
200
219
201
220
// eslint-disable-next-line require-await
0 commit comments