Firebase-tools: 在部署中支持单一存储库

创建于 2018-01-31  ·  47评论  ·  资料来源: firebase/firebase-tools

请参阅: https :

版本信息

3.17.3

重现步骤

预期行为

实际行为

feature request

最有用的评论

+1 对此,云函数通常需要与其他应用程序共享一些通用代码(例如接口),而处理此问题的一个好方法是使用 monorepo(例如 lerna)或直接使用符号链接。 我采用了后者并通过创建一些脚本来解决。 这个概念很简单:我复制函数目录中需要的内容,然后将其删除

以下是我如何使用此目录结构执行此操作:
``

  • 根/
    | - .firebaserc
    | - firebase.json
    | - ...
  • 职能/
    | - 源代码/
    | - 包.json
    | - 预部署.js
    | - 部署后.js
    | - ....
  • 共享/
    | - 源代码/
    | - 包.json
    | - ....
content of `pre-deploy.js`

const fs = require("fs-extra");

const packageJsonPath = "./package.json";
const packageJson = require(packageJsonPath);

(异步()=> {
等待 fs.remove( ./shared );
等待 fs.copy( ../shared , ./shared );

packageJson.dependencies["@project/shared"] = "file:./shared";

await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));

})();

content of `post-deploy.js`

const packageJsonPath = "./package.json";
const packageJson = require(packageJsonPath);
const fs = require("fs-extra");

(异步()=> {
等待 fs.remove( ./shared );

packageJson.dependencies["@project/shared"] = "file:../shared";

await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));

})();

Then update `firebase.json` like this (add the build script if you need, I build before in my pipeline)

“职能”: {
“来源”:“功能”,
“预部署”:[
"npm --prefix "$RESOURCE_DIR" 运行预部署"
],
“部署后”:[
"npm --prefix "$RESOURCE_DIR" 在部署后运行"
]
},
``

如果您进行构建,在distlib目录中,您现在应该有两个兄弟姐妹:functions 和 shared(这是因为共享依赖关系而发生的)。 确保更新函数package.json main以指向lib/functions/src/index.js以使部署工作。

现在它已经解决了,但这是一种解决方法,而不是解决方案。 我认为 firebase 工具应该真正支持符号链接

所有47条评论

这似乎在我的设置中有效,即deploy从根node_modules deploy拾取包,即使package.json位于api/工作区下(我使用了不同的文件夹名称而不是functions/ )。 这里还有什么需要修复的吗?

编辑:此外,我将package.json复制到api/dist以供deploy

// firebase.json
  ...
  "functions": {
    "source": "api/dist"
  },
  ...

因此,2 级嵌套仍然成功解析了根node_modules

@dinvlad你能分享一个回购吗?

@orouz不幸的是还没有,它现在是闭源的。

有没有人设法解决这个问题? 分享简单的示例项目会非常有用。

@audkar目前我只使用lerna.js.org并且它是run命令在具有以下文件夹结构的每个子文件夹中执行 npm 脚本:

- service1/
|  - .firebaserc
|  - firebase.json
- service2/
|  - .firebaserc
|  - firebase.json
- app1/
|  - .firebaserc
|  - firebase.json
- app2/
|  - .firebaserc
|  - firebase.json
- firestore/
|  - firestore.rules
|  - firestore.indexes.json
- etc...

确保每个服务的firebase.json文件不会相互影响由用户决定。 使用功能组多站点名称定位的简单约定意味着云功能和托管解决了这个问题。 仍然没有针对 Firestore/GCS 规则的解决方案,尽管将它们拆分可能并不理想......

之前在这里讨论过 - https://github.com/firebase/firebase-tools/issues/1116

@jthegedus感谢您的回复。 但我认为这张票的问题是不同的。 我正在尝试使用纱线工作区。 并且似乎 firebase 工具在上传函数时不会获取符号链接依赖项

啊,公平,我自己避开了那个兔子洞

你能详细说明是什么问题吗? 如上所述,我只使用带有apiapp工作区的裸 Yarn,然后我使用yarn workspace api build && yarn workspace app build (使用build脚本特定于每个工作区)构建它们工作区)。 构建脚本
1)将outDir TS代码分别编译成api/distapp/dist
2)将对应的package.json文件复制到dist目录中
3) 将yarn.lock从 _root_ 文件夹复制到dist目录中

然后我只是从 _root_ 文件夹中运行yarn firebase deploy ,它会同时获取api/distapp/dist没有任何问题。 我的firebase.json看起来像

  "functions": {
    "source": "api/dist"
  },
  "hosting": {
    "public": "app/dist",

不幸的是,我仍然不能分享完整的代码,但是这个设置很重要,afaik。

另外,我可能是错的,但我认为firebase deploy脚本实际上并没有使用您的node_modules目录。 我认为它只是从dist目录中提取代码package.jsonyarn.lock ,然后完成其余的工作。

确实如此。 firebase.json 中“functions.ignore”的默认值为
["node_modules"] 所以它没有上传。 我相信你可以覆盖它
但是如果你想发布一些本地模块。

在星期一,2019年6月17日,下午6时58分丹尼斯·洛吉诺弗[email protected]
写道:

另外,我可能错了,但我认为 firebase 部署脚本没有
实际上使用您的 node_modules 目录。 我认为它只是拿起
来自 dist 目录的 cod、package.json 和 yarn.lock,并执行
休息。


您收到此消息是因为您订阅了此线程。
直接回复本邮件,在GitHub上查看
https://github.com/firebase/firebase-tools/issues/653?email_source=notifications&email_token=ACATB2U73VS2KIILUVRFFB3P3A6NPA5CNFSM4EOR24GKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LODMX5HW20000000DXG43VMVBW63LODMX520000000D
或静音线程
https://github.com/notifications/unsubscribe-auth/ACATB2U3Q2TLVBICRJ3B5OLP3A6NPANCNFSM4EOR24GA
.

@dinvlad是的,它需要package.json以及您在云后期部署中安装 deps 时使用的任何锁定文件。

我相信在另一个问题中最初概述的场景是在工作区中使用共享包以及范围提升的一些问题。 由于我没有以这种方式使用纱线,因此我只能根据我在那里阅读的内容进行推测。

@samtstern @jthegedus谢谢,很高兴知道!

似乎我们都在谈论不同的问题。 我将尝试描述yarn workspaces问题。

有问题的项目

项目布局

- utilities/
|  - package.json
- functions/
|  - package.json
- package.json

_./package.json_

{
  "private": true,
  "workspaces": ["functions", "utilities"]
}

_functions/package.json_

{
  <...>
  "dependencies": {
    "utilities": "1.0.0",
    <...>
  }
}

问题

函数部署时出错:

Deployment error.
Build failed: {"error": {"canonicalCode": "INVALID_ARGUMENT", "errorMessage": "`gen_package_lock` had stderr output:\nnpm WARN deprecated [email protected]: use String.prototype.padStart()\nnpm ERR! code E404\nnpm ERR! 404 Not Found: [email protected]\n\nnpm ERR! A complete log of this run can be found in:\nnpm ERR!     /builder/home/.npm/_logs/2019-06-18T07_10_42_472Z-debug.log\n\nerror: `gen_package_lock` returned code: 1", "errorType": "InternalError", "errorId": "1971BEF9"}}

函数在模拟器上本地运行良好。

尝试过的解决方案

上传node_modules (在 _firebase.json_ 中使用functions.ignore )。 结果是一样的。

我猜这是因为utilities在 _node-modules_ node_modules/utilities -> ../../utilities创建为系统链接

是不是 firebase-tools 在上传时不包含符号链接模块的内容(没有取消引用)?

抱歉,您能否说明一下您的firebase.json所在的文件夹(并显示functions配置部分)?

_firebase.json_ 在根文件夹中。 配置是标准的。 像这样:

  "functions": {
    "predeploy": [
      "yarn --cwd \"$RESOURCE_DIR\" run lint",
      "yarn --cwd \"$RESOURCE_DIR\" run build"
    ],
    "source": "functions",
    "ignore": []
  },
  <...>

除了作为符号链接的node_modules/utilities之外,一切都按预期部署(包括 _node_modules_)。


我设法通过编写一些脚本来解决这个问题:

  • 为每个工作区创建包 ( yarn pack )。 例如,这会创建 _utilities.tgz_。
  • 将所有输出移动到某个特定目录。
  • 修改 _package.json_ 以将 tgz 文件用于工作区依赖项。 例如dependencies { "utilities": "1.0.0" -> dependencies { "utilities": "file:./utilities.tgz"
  • 将该目录部署到 firebase

上传前输出目录内容:

- dist
|  - lib
|  | -index.js
|  - utilities.tgz
|  - package.json <---------- This is modified to use *.tgz for workspaces

@audkar今天我遇到了和你一样的问题。

我是 Lerna 和 Yarn 工作区的新手。 据我了解,您也可以只使用 Lerna。 这会有所帮助吗?

你的解决方法对我来说似乎有点复杂🤔

还想知道,`--cwd "$RESOURCE_DIR" 是什么?

--cwd代表“当前工作目录”, $RESOURCE_DIR保存源目录的值(在本例中functions )。 添加此标志将使yarnfunctions目录而不是 root 中执行

@audkar啊,我明白了。 所以你可以用yarn workspace functions lintyarn workspace functions build做同样的事情

@dinvlad我不清楚你为什么要瞄准 dist 文件夹并在那里复制东西。 如果您构建到 dist,但将 package.json 保留在原处并将 main 指向dist/index.js那么事情应该一样吗? 然后,您应该将源设置为 api 而不是 api/dist。

@dinvlad我从您的评论中学到了yarn workspace命令,但由于某种原因似乎无法使其工作。 见这里。 任何的想法?

抱歉在这里有点跑题了。 也许在 SO 中发表评论,以尽量减少噪音。

@0x80我将package.json复制到api/dist并将firebase.json指向api/dist因此只有“构建”文件被打包在云函数中。 我不确定如果我将firebase.json指向api会发生什么 - 也许它仍然足够聪明,只打包api/dist里面的东西(基于main package.json main属性)。 但我认为只指向api/dist更干净。

重新yarn workspace ,我在SO 上做出了回应;)

@dinvlad它将捆绑您指向它的根目录,但是您可以将您不希望包含在 firebase.json 忽略列表中的所有内容。

我现在使用了与@audkar 类似的解决方法。

{
  "functions": {
    "source": "packages/cloud-functions",
    "predeploy": ["./scripts/pre-deploy-cloud-functions"],
    "ignore": [
      "src",
      "node_modules"
    ]
  }
}

然后 pre-deploy-cloud-functions 脚本是:

#!/usr/bin/env bash

set -e

yarn workspace @gemini/common lint
yarn workspace @gemini/common build

cd packages/common
yarn pack --filename gemini-common.tgz
mv gemini-common.tgz ../cloud-functions/
cd -

cp yarn.lock packages/cloud-functions/

yarn workspace @gemini/cloud-functions lint
yarn workspace @gemini/cloud-functions build

并且包/云功能有一个额外的 gitignore 文件:

yarn.lock
*.tgz

这对我有用

- root/
|  - .firebaserc
|  - firebase.json
- packages/
  | - package1/
  | - functions/
    | - dist/
    | - src/
    | packages.json

并在root/firebase.json
``
{
“职能”: {
"predeploy": "npm --prefix "$RESOURCE_DIR" 运行构建",
"source": "包/函数"
}
}
````

@kaminskypavel是您的包/功能取决于包/包1(或其他一些兄弟包)吗?

@0x80正。

我认为我对 monorepos 有一些基本的误解。 我假设您可以共享一个包并使用该包部署应用程序,而无需实际将共享包发布到 NPM。

这似乎是不可能的,因为像 Firebase 或 Now.sh 这样的部署通常会上传代码,然后在云中进行安装和构建。 我对么?

@kaminskypavel我尝试了你的方法并且它有效,但这里

@audkar您是将公共包发布到 NPM,还是像我一样尝试使用未发布的共享代码进行部署?

@0x80我同意你的理解——我认为 Firebase 函数部署只是(错误地)假设 package.json 中命名的所有包都将在 npm 上可用,以加快部署的速度。

随着 yarn 工作区设置变得越来越流行,我想更多的人会惊讶于他们不能在 Firebase Functions 中使用符号链接包 - 特别是因为它们在您部署之前工作正常。

随着npm 添加对 workspaces 的支持,我们有了一个关于本地包应该如何工作的生态系统标准。

由于这个问题已经一年多了,Firebase 方面是否有任何关于计划(或缺乏计划)的更新?

我认为这是一个很酷的机会——Firebase 的一系列服务需要一个好的 monorepo 设置。

+1 对此,云函数通常需要与其他应用程序共享一些通用代码(例如接口),而处理此问题的一个好方法是使用 monorepo(例如 lerna)或直接使用符号链接。 我采用了后者并通过创建一些脚本来解决。 这个概念很简单:我复制函数目录中需要的内容,然后将其删除

以下是我如何使用此目录结构执行此操作:
``

  • 根/
    | - .firebaserc
    | - firebase.json
    | - ...
  • 职能/
    | - 源代码/
    | - 包.json
    | - 预部署.js
    | - 部署后.js
    | - ....
  • 共享/
    | - 源代码/
    | - 包.json
    | - ....
content of `pre-deploy.js`

const fs = require("fs-extra");

const packageJsonPath = "./package.json";
const packageJson = require(packageJsonPath);

(异步()=> {
等待 fs.remove( ./shared );
等待 fs.copy( ../shared , ./shared );

packageJson.dependencies["@project/shared"] = "file:./shared";

await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));

})();

content of `post-deploy.js`

const packageJsonPath = "./package.json";
const packageJson = require(packageJsonPath);
const fs = require("fs-extra");

(异步()=> {
等待 fs.remove( ./shared );

packageJson.dependencies["@project/shared"] = "file:../shared";

await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));

})();

Then update `firebase.json` like this (add the build script if you need, I build before in my pipeline)

“职能”: {
“来源”:“功能”,
“预部署”:[
"npm --prefix "$RESOURCE_DIR" 运行预部署"
],
“部署后”:[
"npm --prefix "$RESOURCE_DIR" 在部署后运行"
]
},
``

如果您进行构建,在distlib目录中,您现在应该有两个兄弟姐妹:functions 和 shared(这是因为共享依赖关系而发生的)。 确保更新函数package.json main以指向lib/functions/src/index.js以使部署工作。

现在它已经解决了,但这是一种解决方法,而不是解决方案。 我认为 firebase 工具应该真正支持符号链接

@michelepatrassi受到您提醒我的启发,我创建了firelink库来管理此案例。 它在内部使用rsync来复制递归文件。

https://github.com/rxdi/firelink

npm i -g @rxdi/firelink

基本用法
假设您有 monorepo 方法并且您的包位于package.json所在的当前目录下 2 个级别。

package.json

  "fireDependencies": {
    "@graphql/database": "../../packages/database",
    "@graphql/shared": "../../packages/shared",
    "@graphql/introspection": "../../packages/introspection"
  },

执行firelink它将复制与文件夹相关的包,然后将现有的包映射到本地模块 install "@graphql/database": "file:./.packages/database",然后将执行命令firebase并将传递firelink其余参数
基本上firelinkfirebase CLI 的替代品,因为它在完成复制packages并修改package.json工作后在最后生成firebase package.json

问候!

我们只是被这个咬了,我认为 monorepos 将成为标准,默认情况下应该支持。

伙计们,这个问题的一个简单解决方案是使用 webpack 来捆绑您的云功能并从捆绑目录中进行部署。 这里附上一个我正在使用的基本 webpack 文件。 在构建期间,这将打包所有函数代码,包括在 mono-repo 中解析的依赖项到顶级文件夹(在这种情况下,它的webpack/cloud-functions ,它可以是您配置的任何内容)

const path = require('path');

module.exports = {
  target: 'node',
  mode: 'production',
  entry: './src/index.ts',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js', '.json']
  },
  output: {
    filename: 'index.js',
    path: path.resolve(__dirname, '../../webpack/cloud-functions/dist'),
    libraryTarget: 'commonjs'
  },
  externals: {
    'firebase-admin': 'firebase-admin',
    'firebase-functions': 'firebase-functions'
  }
};

最后在您的firebase.json文件中,请参考此文件夹进行部署。

{
  "functions": {
    "source": "webpack/cloud-functions"
  }
}

请记住在webpack/cloud-functions文件夹中也有一个package.json文件。

{
  "name": "cloud-functions",
  "version": "1.0.0",
  "scripts": {
    "deploy": "firebase deploy --only functions"
  },
  "engines": {
    "node": "10"
  },
  "main": "dist/index.js",
  "dependencies": {
    "firebase-admin": "8.9.1",
    "firebase-functions": "3.3.0"
  },
  "devDependencies": {},
  "private": true
}

这是经过测试和工作的。 我正在使用谷歌云构建。 如果需要,请询问更多信息。

谢谢,

@sowdri感谢分享! 您是从 firebase.json functions.predeploy步骤触发 webpack 构建,还是在调用 firebase deploy 之前从云构建脚本触发它?

@0x80我正在cloud build步骤中构建它。 所以根据firebase cli它只是一组用JS构建的函数

@sowdri谢谢你的例子。 我们最终在过去的 AWS Lambdas / Serverless 项目中使用了相同的方法:上传 (Webpack) 包比强制工具使用 yarn monorepo 更容易。

我也坚持这个……一切都很好(甚至是 firebase 模拟器),直到我尝试部署功能(react 中的 web 应用程序构建部署正常)。 :( 希望 firebase 团队可以尽快添加对 monorepos 的支持。

我将 babel 用于我的 monorepo 中的其他包,所以我更愿意继续使用它。 如果不出意外,我可以尝试使用 webpack ……这似乎对某些人有用。

编辑:我通过使用 GitHub package registry解决了这个问题。 所以我在那里发布我所有的包,然后对于travisfunctions服务器,我必须设置注册表并提供一个用于身份验证的令牌(通过.npmrc )。 ...似乎是一个优雅的解决方案。 我在这里得到了这种方法的想法: https :

是的,也被这个咬了。 在firebase serve --only functions一切正常,但是在部署时,它无法找到模块。

我最终构建了一个小脚本来为我构建包。 虽然这反映了我的环境的细节,比如使用模块和我的包名与我的目录名相匹配,但我希望它对其他人有用。

```从“fs”导入fs;
从“child_process”导入 child_process;

const internalPackagesFull = new Map();

// 查找包的所有 deps
const getDepsForPackage = (packageName) => {
const packageDir = packageName.split("/")[1]; // 这可能需要为您改变
const packageSpecFileName = ../${packageDir}/package.json ;
const packageSpecFile = fs.readFileSync(packageSpecFileName);
const packageSpec = JSON.parse(packageSpecFile);
const packageInternalDeps = Object.keys(
packageSpec.dependencies
).filter((key) => key.includes("图灵")); // 这需要为你改变

const packageTgzName = ${packageName.replace("@", "").replace("/", "-")}-v${ packageSpec.version }.tgz ;

internalPackagesFull.set(packageName, {
包规格文件名,
包装规格,
包目录,
包InternalDeps,
包Tgz名称,
});

const packagesToProcess = packageInternalDeps.filter(
(internalDepName) => !internalPackagesFull.has(internalDepName)
);

packagesToProcess.forEach((internalPackageName) =>
getDepsForPackage(internalPackageName)
);
};

const packageName = JSON.parse(fs.readFileSync("./package.json")).name;
child_process.execSync( cp ./package.json ./package.json.org );
getDepsForPackage(packageName);

// 编写更新的包 - 使用通用 js 并且引用是本地 tgz 文件
[...internalPackagesFull.values()].forEach((internalDep) => {
const { packageSpec, packageSpecFileName, packageInternalDeps } = internalDep;

// 改变包类型
packageSpec.type = "commonjs"; // 这可能需要为您改变

// 指定dep所在的位置为打包后的zip文件
packageInternalDeps.forEach((internalDepOfPackage) => {
const { packageTgzName } = internalPackagesFull.get(internalDepOfPackage);
packageSpec.dependencies[internalDepOfPackage] = ./${packageTgzName} ;
});

fs.writeFileSync(
包规格文件名,
JSON.stringify(packageSpec, null, " ")
);
});

// 运行纱线构建和打包
[...internalPackagesFull.values()].forEach((internalDep) => {
尝试 {
console.log( Buliding ${internalDep.packageDir} );
child_process.execSync("纱线构建", {
cwd: ../${internalDep.packageDir} ,
});
console.log( Packaging ${internalDep.packageDir} );
child_process.execSync("纱线包", {
cwd: ../${internalDep.packageDir} ,
});

if (packageName !== internalDep.packageSpec.name) {
  console.log(`Move to current directory ${internalDep.packageDir}`);
  child_process.execSync(
    `cp ../${internalDep.packageDir}/${internalDep.packageTgzName} .`,
    {
      cwd: ".",
    }
  );
}

}赶上(e){
控制台日志(e);
}
});

// 回到标准包结构
[...internalPackagesFull.values()]
.filter((internalDep) => packageName !== internalDep.packageSpec.name)
.forEach((internalDep) => {
常量{
包装规格,
包规格文件名,
包InternalDeps,
} = internalDep;

// change the package type
packageSpec.type = "module"; // THIS MAY NEED TO CHANGE FOR YOU

// specify the location of the dep to be the packaged zip file
packageInternalDeps.forEach((internalDepOfPackage) => {
  packageSpec.dependencies[internalDepOfPackage] = "*";
});

fs.writeFileSync(
  packageSpecFileName,
  JSON.stringify(packageSpec, null, "  ")
);

});
``

我使用了@sowdri提供的解决方案(感谢您!),稍作调整,以便我可以自由删除和重新生成整个dist/目录,包括辅助package.json

const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');

module.exports = {
  ...,
  plugins: [
    new CopyPlugin({
      patterns: [{ from: 'package.dist.json', to: 'package.json' }],
    }),
  ],
};

然后我将以下package.dist.json保留在包根目录中:

{
  "name": "@package/name",
  "version": "0.0.1",
  "engines": {
    "node": "10"
  },
  "main": "index.js",
  "dependencies": {
    "firebase-admin": "8.9.1",
    "firebase-functions": "3.3.0"
  },
  "private": true
}

可能可以从此文件中删除依赖项并依靠 Webpack 将它们捆绑在一起(无需使这些依赖项与主package.json保持同步),但我还没有尝试过。

我使用带有脚本的单个存储库为所有相关目录或firebase项目复制.firebasercfirebase.json使用 npm cpy 包保留目录结构以输出我编译代码所在的目录。

我从原始 package.json 创建package.json用于函数部署。
image

这是copyFiles.js脚本:

const cpy = require('cpy');
const fs = require('fs');
const package = require('./package.json');
(async () => {
    await cpy(['./../package.json', './**/*.json', './**/.firebaserc'], '../out/', {
        parents: true,
        cwd: 'src'
    });
    const dirs = fs.readdirSync('./out/');
    const newPkg = {
        main: package.main,
        dependencies: package.dependencies,
        engines: package.engines,
    }
    dirs.forEach(dir => {
        fs.writeFileSync(`./out/${dir}/package.json`, JSON.stringify({ name: dir, ...newPkg }));
    })
    console.log('Files copied!', dirs);
})();

部署去./out/[project-name]/firebase deploy --only=functions或为它写一个脚本。

我遇到了一些像这样的 Webpack 警告:

/Users/me/Development/myproject/node_modules/firebase-functions/lib/config.js 61:23-42 中的警告
关键依赖:依赖的请求是一个表达式

您是否找到了解决这些问题的方法,或者您是否忽略/抑制了它们?

我设法让事情在没有警告的情况下工作🥳 我最终得到了下面的配置。 特别是使用外部的正则表达式模式会有所不同,因为如果您的外部只有“firebase-functions”,则您从子模块进行的任何导入都不会匹配,并且该库仍包含在您的包中。

由于捆绑问题,我在部署时也遇到了一些神秘的@grpc错误。 我忘了保留它们以供参考。

const path = require("path");
const CopyPlugin = require("copy-webpack-plugin");

module.exports = {
  target: "node",
  mode: "production",
  entry: "./src/index.ts",
  devtool: "inline-source-map",
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js", ".json"],
    alias: {
      "~": path.resolve(__dirname, "src"),
    },
  },
  output: {
    filename: "index.js",
    path: path.resolve(__dirname, "dist/bundled"),
    libraryTarget: "commonjs",
  },
  externals: ["express", /^firebase.+$/, /^@google.+$/],
  plugins: [
    new CopyPlugin({
      patterns: [{ from: "package.dist.json", to: "package.json" }],
    }),
  ],
};

结果证明不需要单独的 package.dist.json。 拥有此文件的烦人之处当然是每次更新其中列出的任何依赖项时都需要手动更新它。 所以很容易忘记这一点。

相反,将您在 package.dist.json 中_不会_列出的所有包移动到 devDependencies 列表,然后在 webpack 复制插件中使用该文件。 现在你只需要处理一个 package.json 🎉

另外,我不希望我的本地 nodejs 版本必须与部署的节点版本的功能相同。 我发现您现在可以在 firebase.json 文件中指定functions.runtime 。 因此,从 package.json 中取出engines字段,并在您的 firebase 配置中将functions.runtime为“10”或“12”。

这对我来说是这样的:

{
  "functions": {
    "source": "packages/cloud-functions/dist/bundled",
    "runtime": "12"
  },
  "firestore": {
    "rules": "firestore.rules",
    "indexes": "firestore.indexes.json"
  },
  "emulators": {
    "functions": {
      "port": 5001
    },
    "firestore": {
      "port": 8080
    },
    "pubsub": {
      "port": 8085
    }
  }
}

你如何处理源映射? 如果我使用devtool: "inline-source-map"将我的函数与 webpack 捆绑在一起,它不会在堆栈驱动程序错误报告中被发现。 @sowdri

我在我的项目中遇到了同样的事情,这是一个非常糟糕的事情,因为我不想为一个副项目发布每个包。 跟上多个package.json文件或不得不在包之外构建是很烦人的。 事实证明,如果您将 deps 包含为可选,Lerna 仍然会选择它们,并且 Firebase 在上传时不会抱怨。

下面的配置将通过单个 package.json 为您提供符号链接的 dep 支持!

包.json

{
  "name": "@your-package-name/functions",
  "version": "0.1.0",
  "scripts": {
    "build": "webpack"
  },
  "engines": {
    "node": "10"
  },
  "main": "dist/index.js",
  "dependencies": {
    "firebase-admin": "^8.10.0",
    "firebase-functions": "^3.6.1"
  },
  "optionalDependencies": {
    "@your-package-name/shared": "^0.1.0",
    "@your-package-name/utils": "^0.1.0"
  }
}

webpack.config.js

const path = require('path')

// The cost of being fancy I suppose
// https://github.com/firebase/firebase-tools/issues/653

module.exports = {
  target: 'node',
  mode: 'production',
  entry: './src/index.ts',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
        exclude: /node_modules/,
        options: {
          configFile: 'tsconfig.build.json',
        },
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js', '.json'],
  },
  output: {
    filename: 'index.js',
    path: path.resolve(__dirname, 'dist'),
    libraryTarget: 'commonjs',
  },
  externals: {
    'firebase-admin': 'firebase-admin',
    'firebase-functions': 'firebase-functions',
  },
}

firebase.json

{
  "firestore": {
    "rules": "firestore.rules",
    "indexes": "firestore.indexes.json"
  },
  "functions": {
    "source": "packages/functions"
  },
  "emulators": {
    "functions": {
      "port": 5476
    },
    "firestore": {
      "port": 4565
    },
    "ui": {
      "enabled": true
    }
  }
}

我正在使用 yarn 工作区,但我也不需要在其他 package.json 文件中提及我的本地包名称。 我在没有它的情况下成功部署到 Firebase 和 Vercel。

我不确定是什么让它起作用。 只需在我的顶级 package.json 中有这个标准配置:

"workspaces": {
    "packages": [
      "packages/*"
    ]
  },

我不介意在每个 /packages/* 文件夹中都有一个 package.json,因为运行yarn upgrade-interactive将一次性处理它们。 在某些包中,我发现能够专门为该范围添加脚本很有用。

- - 编辑 - -

我忘了提到我正在使用带有项目引用的 Typescript。 这可能与它有关。 对于 Vercel,我使用 next-transpile-modules 将我的共享代码包含在包中。

我正在使用与@0x80@sowdri类似的设置来完成这项工作,而不是将我的本地依赖项移动到 devDependencies(这实际上对我不起作用,我认为我错过了一步)并使用copy-webpack-plugin ,我使用generate-package-json-webpack-plugin在 dist 文件夹中构建我的 package.json 。 这根据生成的代码实际需要的内容构建我的依赖项列表,因此如果它是捆绑的或开发依赖项,则不会包含在生成的 package.json 中。

我还使用webpack-node-externals设置了我的外部,除了我使用正则表达式匹配项目名称前缀的符号链接的 monorepo 包外,所有内容都是外部的。 我还为 firebase 包@0x80添加了正则表达式,作为额外的外部

这是我的配置

/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path");
const nodeExternals = require("webpack-node-externals");
const GeneratePackageJsonPlugin = require("generate-package-json-webpack-plugin");

const basePackage = {
  name: "@project/functions",
  version: "1.0.0",
  main: "./index.js",
  scripts: {
    start: "yarn run shell"
  },
  engines: {
    node: "12"
  }
};

module.exports = {
  target: "node",
  mode: "production",
  entry: "./src/index.ts",
  devtool: "inline-source-map",
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/
      }
    ]
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js", ".json"],
    alias: {
      "@": path.resolve(__dirname, "src"),
      "@root": path.resolve(__dirname, "./"),
      "@types": path.resolve(__dirname, "src/@types"),
      "@utils": path.resolve(__dirname, "src/utils")
    }
  },
  output: {
    filename: "index.js",
    path: path.resolve(__dirname, "dist"),
    libraryTarget: "commonjs"
  },
  externals: [
    /^firebase.+$/,
    /^@google.+$/,
    nodeExternals({
      allowlist: [/^@project/]
    })
  ],
  plugins: [new GeneratePackageJsonPlugin(basePackage)]
};

使用 webpack 捆绑代码的另一个好处是我终于可以使用模块别名:)

此页面是否有帮助?
0 / 5 - 0 等级