NodeJS Firebase Functions + monorepo -部署不起作用+本地未安装dep

oalqel3c  于 2023-05-12  发布在  Node.js
关注(0)|答案(2)|浏览(130)

看来有人也有同样的问题:https://github.com/firebase/firebase-functions/issues/1050

问题

我已经将我的项目结构移动到具有以下结构的monorepo:

/
|
| - node_modules/
|
| - packages/
| - - app/
| - - - - index.js
| - - - - package.json
| - - functions/
| - - - - src/
| - - - - - - helloWorld.function.js
| - - - - - - index.js
| - - - - package.json
|
| - .firebaserc
| - firebase.json
| - firestore.indexes.json
| - firestore.rules
| - package.json
| - yarn.lock
| - LICENSE.md

据我所知,我们的函数所依赖的所有代码都必须在functions目录中。但是...当我运行yarn install时,我的functionsnode_modules保存在root node_modules folder中。
然后,如果我从项目的根目录运行firebase deploy,我会得到错误:
错误:解析函数触发器时出错。
默认Firebase应用程序不存在。请确保在使用任何Firebase服务之前调用initializeApp()。

  • 注意:我没有做任何代码更改...在将项目配置为monorepo结构之前,一切都运行良好。*

我解决这个问题的尝试

我尝试在根包.json中定义工作空间时使用nohoist

{
  "private": true,
  "name": "@company/project",
  "version": "1.0.0",
  "description": "Project monorepo",
  "repository": "...",
  "license": "MIT",
  "author": {
    "name": "Raul",
    "email": "...
  },
  "scripts": {
    "app": "yarn workspace @company/app start",
    "documentation": "yarn workspace @company/documentation start",
    "server": "yarn workspace @company/server start"
  },
  "workspaces": {
    "packages": ["packages/*"],
    "nohoist": ["**/@company/server"] <---- HERE
  }
}

-* 注意:当我运行yarn server时,函数shell正确启动。*
但是...出于某种原因,在重新安装所有东西之后:

rm -rf node_modules (everywhere)
yarn cache clean
yarn install

函数包的依赖安装在根目录node_modules

有什么想法吗
这是我的函数node_modules在安装了dep之后的样子:

node_modules/ 
  .bin/ 
     eslint
     eslint-config-prettier
     firebase-functions
     functions-framework
     functions-framework-nodejs
     image_size
     uuid
  qs/
  uuid/

这些是所需的依赖项(安装在根package.json中,这不是我期望的行为):

"name": "@company/server",
 ...
 "dependencies": {
    "@google-cloud/functions-framework": "^3.1.1",
    "@google-cloud/translate": "^6.3.1",
    "@google-cloud/vision": "^2.4.2",
    "@googlemaps/google-maps-services-js": "^3.3.3",
    "axios": "^0.27.2",
    "camelcase": "^6.3.0",
    "dotenv": "^16.0.1",
    "expo-server-sdk": "^3.6.0",
    "firebase-admin": "^10.3.0",
    "firebase-functions": "^3.21.2",
    "glob": "^7.2.0",
    "image-size": "^1.0.1",
    "lodash": "^4.17.21",
    "moment": "^2.29.3",
    "qs": "^6.10.3",
    "sharp": "^0.30.6",
    "uuid": "^8.3.2"
  },
  "devDependencies": {
    "eslint": "^8.3.0",
    "eslint-config-google": "^0.14.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-import": "^2.25.3",
    "eslint-plugin-prettier": "^4.0.0",
    "eslint-plugin-promise": "^4.3.1",
    "firebase-functions-test": "^0.2.0"
  }

更新

我没有在根目录中使用nohizing,而是将此段放在packages/functions/package.json中:

workspaces: { nohoist: ["**"] }

而且所有的deps都是本地安装的!
但是...当我做firebase deploy时,同样的错误。如果不是从根文件夹运行命令,而是从packages/functions/package.json运行,则部署成功。
为什么?发生什么事了?
这是我的firebase.json文件:

{
  "functions": {
    "predeploy": ["yarn --cwd \"$RESOURCE_DIR\" lint"],
    "source": "packages/functions"
  },
  "firestore": {
    "rules": "firestore.rules",
    "indexes": "firestore.indexes.json"
  }
}
ddrv8njm

ddrv8njm1#

我已经开发了一个解决方案,并就此写了一篇文章。下面是摘录,但这里是full article

Firebase的问题

当部署到Firebase时,它希望上传一个文件夹,就像传统的单个包存储库一样,包含源文件以及声明其外部依赖关系的清单文件。在其云部署管道中接收到文件后,它会检测包管理器并运行安装和构建。
在一个monorepo中,尤其是一个私有的,你的Firebase代码通常依赖于同一个仓库中的一个或多个共享包,你不想在任何地方发布它们。
一旦Firebase尝试在云中查找这些依赖项,就无法找到它们,部署就会失败。

杀出一条血路

使用bundler

为了解决这个问题,你可以尝试使用Webpack这样的打包器将Firebase代码与共享包代码结合起来,然后从发送到Firebase的package.json清单中删除这些包,这样它就不知道这些包的存在。
不幸的是,这种策略很快就成了问题。。
如果共享包本身没有在其输出中捆绑所有依赖项,Firebase将不知道共享代码依赖于什么,因为您没有包含或安装这些清单。
您可以尝试捆绑所有内容,但是如果您的共享包依赖于您的Firebase包也依赖的东西,那么您现在有一部分代码运行依赖项的内部捆绑副本,而另一部分则使用来自包管理器安装的不同位置的相同依赖项。
此外,有些库确实不喜欢捆绑,根据我的经验,包括Firebase和Google客户端库。你会很快发现自己试图通过bundler设置将事情外部化,以便让事情工作。
即使您设法完成了所有这些工作,您可能正在创建大型捆绑包,这可能会导致云功能的冷启动时间出现问题。
不完全是可靠或可扩展的解决方案。

打包和链接本地依赖

一种可以说是更优雅的方法涉及将本地依赖项打包到tarball中(类似于将包发布到NPM的方式),并将结果复制到构建输出,然后将其链接到更改的清单文件中。
这可以很好地工作,因为它基本上类似于如果从外部域安装这些包,Firebase代码将如何工作。
无论您是手动执行此操作,还是编写shell脚本来处理这些事情,对我来说仍然感觉非常繁琐和脆弱,但我认为如果您的本地依赖关系很简单,这是一个可行的解决方案。
然而,一旦您的共享包依赖于其他共享包,这种方法很快就会变得棘手,因为这样您就需要打包和调整多个级别的东西。

我的解决方案

我创建了isolate-package。这个名字是通用的,因为它不包含任何特定于Firebase的东西,尽管我目前不知道任何其他隔离输出的用例。
它采用了与前面在打包和链接依赖项中描述的方法类似的方法,但是采用了更复杂的方法。它被设计为处理不同的设置和包管理器,它完全隐藏了用户的复杂性。
它公开的隔离二进制文件可以简单地添加到Firebase predeploy钩子中,就是这样!
这还允许您从多个不同的包部署到Firebase,并保持配置位于同一位置,而不是分散在monorepo根目录中。
对于绝大多数用例来说,它应该是零配置的,并且被设计为与所有包管理器兼容。

fgw7neuy

fgw7neuy2#

解决方法(不是正确的解决方案,只是一个技巧):

与其使用nohoistpackages/functions目录中本地安装所有dep,不如将其从yarn workspaces中排除?
为此,请将root的package.json更新为:

"workspaces": [
    "packages/app",
    "packages/documentation"
  ]

packages/functions未作为工作区包含在内。)

测试:

现在,如果您运行(在项目中的任何位置)firebase deploy,一切都很好...部署成功,我们所有的后端DEP都安装在本地(这是必需的,因为所有的云函数代码都必须在函数文件夹中)。
这个error也解决了!

优点:

您可以维护单一存储库结构并毫无问题地部署云功能。

缺点:

这是一个棘手的解决方案。。当你从项目的根目录运行yarn install时,packages/functions/package.json依赖项不会被安装(因为我们已经从工作区中删除了它,以便在本地安装它们)。
为了解决这个小问题,在根目录package.json中,编写以下代码:

{
  "scripts": {
    ...,
    "postinstall": "cd path/to/your/functions && yarn install", // automatically executed after running `yarn install`
  }
}

在我的例子中:

{
  "scripts": {
    ...,
    "postinstall": "cd packages/functions && yarn install", // automatically executed after running `yarn install`
  }
}

相关问题