As per Firebase Functions with Yarn workspaces, I'd like to deploy a monorepo to Firebase using Yarn Workspaces. I can successfully deploy the "web" aspect to Firebase Hosting, which references a shared "core" workspace. However, attempts to do the same with the "functions" workspace on Firebase Functions fails.
I'm using Yarn Workspaces with the following folder structure:
packages/
core/ // name: firebase-monorepo-core: custom core package
functions/ // name: firebase-monorepo-functions: firebase functions package
web/ // name: firebase-monorepo-web: react package
These have been configured in the root package.json file:
"workspaces": {
"packages": [
"packages/*"
],
"nohoist": [
"**/firebase-monorepo-core"
]
}
In an attempt to enable Yarn workspaces in Firebase (and therefore share my core package) I've used the nohoist feature to create symlinks to the core workspace in functions and web as per twiz's Stackoverflow answer.
The core package also exists as a dependency in functions and web:
"dependencies": {
"firebase-monorepo-core": "*"
}
There are no problems when any of this runs locally, and in fact deployment of the web package to Firebase hosting works fine. However, deployment of the functions package to Firebase functions throws an error:

I've created a Github repository https://github.com/cjmyles/firebase-monorepo to demonstrate this, and the resultant web workspace can be viewed at https://fir-monorepo.firebaseapp.com.
I can get the functions package to deploy if I yarn pack the core workspace and reference it as a tarball in the package.json file, but I really don't like that solution and it will require more overhead to automate it.
Any advice would be appreciated to resolve this issue.
one potential solution is to use Lerna.
You can then publish your core package to a private GitHub package and use Lerna to create a symlink to the core package for local development.
The issue is that firebase-tools assumes that all packages used by your function are available in npm.
A quick solution is to use firelink.
cd ./packages/functions
yarn add -D @rxdi/firelink
Add an equivalent section to your functions package.json for all shared dependencies.
"fireDependencies": {
"@org/common": "../../packages/common"
},
Change your deploy script to use firelink binary
"scripts": {
"build": "tsc --build tsconfig.build.json",
"predeploy": "yarn build",
"deploy:functions": "firelink deploy --only functions"
}
Just a heads up does not work with "devDependencies", but you should not be needing them in production anyway.
Update your firebase.json to run the predeploy target.
"functions": {
"predeploy": ["npm --prefix \"$RESOURCE_DIR\" run predeploy"],
"source": "packages/functions"
},
Lastly remove any local yarn.lock files in the workspace package. If you wish to lock the dependencies add npm install to your predeploy step. If anyone has a better solution to this let me know. It seem unfortunate that yarn does not have the ability to generate lock files on demand in a workspace.
I got this to work but it was a pain and hacky. So far it's working well though. I'm using NPM Workspaces, but the concept should apply to any project where node modules are hoisted to the root folder. I'm also using GitHub Workflows / Actions for CI and TypeScript, so all of this works within those environments.
tsc
functions/src/domain to be exact) so that they get uploaded by Firebase CLIcp -r packages/domain firebase/functions/src/domain
sed, which GitHub actions supports:sed -i 's/"dependencies": {/"dependencies": { "@mycompany\/domain": "file:src\/domain",/' package.json
Before that command, my package.json looks like this:
"dependencies": {
"lodash": "^4.17.20",
...
After that command, it looks like this:
"dependencies": { "@mycompany/domain": "file:src/domain",
"lodash": "^4.17.20",
...
firebase deploy --only functions
Not pretty, but it works with full automation. 😁
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With