feat(functions): add full support for Yarn 2+ (PnP and pnpm nodeLinkers)#10846
feat(functions): add full support for Yarn 2+ (PnP and pnpm nodeLinkers)#10846ajperel wants to merge 1 commit into
Conversation
Wiz Scan Summary
To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension. |
There was a problem hiding this comment.
Code Review
This pull request introduces support for emulating and deploying Cloud Functions in Yarn 2+ Plug'n'Play and pnpm nodeLinker environments by adding Yarn resolution hooks and fallback search paths for the firebase-functions binary. The review feedback highlights a critical issue where paths containing spaces in NODE_OPTIONS can cause execution failures on Windows, which can be fixed by wrapping the path in double quotes. Additionally, several improvements are suggested to align with the repository style guide, including reducing nested conditionals and avoiding the explicit use of any in catch blocks.
| if (resolved) { | ||
| const { pkgPath, packageJson } = resolved; | ||
| const relativeBinPath = packageJson.bin?.["firebase-functions"]; | ||
| if (relativeBinPath) { | ||
| const jsBinPath = path.join(pkgPath, relativeBinPath); | ||
| if (fileExistsSync(jsBinPath)) { | ||
| logger.debug(`Found firebase-functions binary internally via Yarn PnP at '${jsBinPath}'`); | ||
| // When we return this zip path, spawnFunctionsProcess constructs a new `spawn(process.execPath)` | ||
| // containing our payload. Because the child environment correctly inherits `NODE_OPTIONS`, it | ||
| // boots up with an identically patched `fs` architecture, safely executing the zipped script! | ||
| return jsBinPath; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
We can reduce nesting by flattening the conditional checks and combining the path resolution and existence check. This adheres to the repository style guide's rule to reduce nesting as much as possible.
if (resolved) {
const { pkgPath, packageJson } = resolved;
const relativeBinPath = packageJson.bin?.["firebase-functions"];
const jsBinPath = relativeBinPath ? path.join(pkgPath, relativeBinPath) : undefined;
if (jsBinPath && fileExistsSync(jsBinPath)) {
logger.debug("Found firebase-functions binary internally via Yarn PnP at '" + jsBinPath + "'");
// When we return this zip path, spawnFunctionsProcess constructs a new `spawn(process.execPath)`
// containing our payload. Because the child environment correctly inherits `NODE_OPTIONS`, it
// boots up with an identically patched `fs` architecture, safely executing the zipped script!
return jsBinPath;
}
}References
- Reduce nesting as much as possible Code should avoid unnecessarily deep nesting or long periods of nesting. Handle edge cases early and exit or fold them into the general case. (link)
| } catch (e: any) { | ||
| logger.debug( | ||
| `resolvePnpModulePath encountered error querying Yarn PnP API for ${moduleName}:`, | ||
| e, | ||
| ); | ||
| } |
There was a problem hiding this comment.
Using catch (e: any) explicitly uses any as an escape hatch, which violates the repository style guide. Since logger.debug accepts unknown or any, we can simply use catch (e) or catch (e: unknown) to avoid using any.
| } catch (e: any) { | |
| logger.debug( | |
| `resolvePnpModulePath encountered error querying Yarn PnP API for ${moduleName}:`, | |
| e, | |
| ); | |
| } | |
| } catch (e) { | |
| logger.debug( | |
| "resolvePnpModulePath encountered error querying Yarn PnP API for " + moduleName + ":", | |
| e, | |
| ); | |
| } |
References
- Never use
anyorunknownas an escape hatch. Define proper interfaces/types or use type guards. (link)
af4f976 to
0a2f3ee
Compare
This commit comprehensively adds native CLI support for executing and resolving Firebase Functions inside strict Yarn 2+ ecosystems (specifically Plug'n'Play zero-install mechanisms and pnpm-emulated physical symlinked layouts). - Introduces robust `.pnp.cjs` hook parsing to resolve `node_modules` maps instantly in strictly sandboxed Yarn architectures. - Aggressively injects `--require .pnp.cjs` into `env.NODE_OPTIONS` to guarantee spawned emulator processes correctly inherit virtual file system patching. - Simplifies dependency resolution search to securely evaluate the function's structural payload inside `[sourceDir, projectDir]`. - Implements strict `PackageJson` typing to ensure safe SDK version queries. - Fixes `#10813` by bypassing unhandled `require.resolve()` traps that historically crashed direct natively booted CLI processes. - Updates SdkVersion checks to also handle Yarn2+ cases. Scenarios Tested - Yarn Workspaces strict `pnp` nodeLinker mode. - Yarn Workspaces `pnpm` nodeLinker mode. - Direct native Node CLI executions (`node firebase.js`) over varied isolated virtual package boundaries. - Emulator successful startup - Tested with both latest and older firebase-functions SDK versions Test Commands - `firebase deploy --only functions` - `firebase emulators:start` TAG=agy CONV=a5e2bab9-4423-4b50-82ed-188ecaca31ab
Description
This commit comprehensively adds native CLI support for executing and resolving Firebase Functions inside strict Yarn 2+ ecosystems (specifically Plug'n'Play zero-install mechanisms and pnpm-emulated physical symlinked layouts).
.pnp.cjshook parsing to resolvenode_modulesmaps instantly in strictly sandboxed Yarn architectures.--require .pnp.cjsintoenv.NODE_OPTIONSto guarantee spawned emulator processes correctly inherit virtual file system patching.PackageJsontyping to ensure safe SDK version queries.#10813by bypassing unhandledrequire.resolve()traps that historically crashed direct natively booted CLI processes.Scenarios Tested
pnpnodeLinker mode.pnpmnodeLinker mode.npm testSample Commands
firebase deploy --only functionsfirebase emulators:startTAG=agy
CONV=a5e2bab9-4423-4b50-82ed-188ecaca31ab