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