配置 OpenNext 以便在 Nx Monorepo 中使用。
这是一个详细的示例,说明如何将 OpenNext + SST 添加到一个现有的 Nx 工作区,其中现有的 NextJS 应用程序位于 apps/next-site
-
安装
open-next:pnpm add —save-dev @opennextjs/aws -
更新你的
apps/next-site/next.config.js添加output: 'standalone',并且你想要添加experimental.outputFileTracingRoot,它看起来应该像这样:
//@ts-check
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { composePlugins, withNx } = require('@nx/next');
const { join } = require('node:path');
/**
* @type {import('@nx/next/plugins/with-nx').WithNxOptions}
**/
const nextConfig = {
nx: {
// 如果你想使用 SVGR 请将此设置为 true
// 参见:https://github.com/gregberge/svgr
svgr: false,
},
+ output: 'standalone',
+ experimental: {
+ // 这应该是你仓库根目录的路径,在这种情况下只是向下两层。open-next 需要它来检测这是一个 monorepo
+ outputFileTracingRoot: join(__dirname, '../../'),
+ },
};
const plugins = [
// 如果需要,将更多 Next.js 插件添加到此列表中。
withNx,
];
module.exports = composePlugins(...plugins)(nextConfig);- 在你的应用根目录内创建
open-next.config.ts,它看起来应该像这样:
import type { OpenNextConfig } from '@opennextjs/aws/types/open-next';
const config = {
default: {},
buildCommand: 'exit 0', // 在我的示例中,我们设置 Nx 任务分发来处理构建顺序。
buildOutputPath: '.',
appPath: '.',
packageJsonPath: '../../', // 同样,指向你仓库根目录的路径(package.json 所在的位置)
} satisfies OpenNextConfig;
export default config;- 设置 nx 的 targets/tasks
现在我们已经设置了 open-next 配置,你可以尝试运行 open-next build,根据你是否已经构建了你的 next 应用,它甚至可能直接工作。
然而,我们不希望依赖每次想要部署更改时都需要手动运行构建,所以我们可以设置一个 target。
我们在项目的 project.json 中执行此操作(在本例中,位于 apps/next-site/project),我们要找到 targets 对象并更新它:
{
"name": "next-site",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/next-site",
"projectType": "application",
"tags": [],
"targets": {
+ "open-next-build": { // target 的名称,这就是你将调用的
+ "executor": "nx:run-commands",
+ "dependsOn": ["build"], // 这确保 Nx 会在运行此命令之前构建我们的 next 应用。
+ "cache": true, // 缓存输出,如果你想使用 DTE/Nx cloud 这很有用
+ "outputs": ["{projectRoot}/.open-next"], // 告诉 nx 输出位于何处
+ "options": {
+ "cwd": "apps/next-site", // 我们运行命令的位置
+ "command": "open-next build" // 我们想要运行的命令
+ }
+ }
}
}
接下来我们需要将 open-next 目录添加到我们的 eslint 的 ignorePatterns 数组中
{
"extends": [
"plugin:@nx/react-typescript",
"next",
"next/core-web-vitals",
"../../.eslintrc.json"
],
"ignorePatterns": [
"!**/*",
+ ".next/**/*",
+ ".open-next/**/*"
],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.spec.ts", "*.spec.tsx", "*.spec.js", "*.spec.jsx"],
"env": {
"jest": true
}
}
]
}现在,当你运行 nx open-next-build next-site 时,nx 将自动构建 next 应用,以及 next 应用所需的任何内容,很棒!
- 使用 SST 部署
现在,我们有了一个构建好的应用,准备部署,那么我们如何将其放到 SST/AWS 上呢?好问题!
在本示例中我们使用 sst ion。我假设你已经安装了 cli,如果没有,请在此处查看如何安装 (opens in a new tab),
但我们不会使用 SST cli 来初始化这个项目,因为它想给你的 next 应用添加一个 package.json,它看起来像是在工作,但你最终会得到一个巨大的服务器错误(都是因为 package.json 覆盖了 nx 认为 应该有的内容,并且它会错过一堆依赖项)。我们将改为手动设置:
- 让我们使用
pnpm add sst@ion添加 sst 包,以及 SST 与 AWS 协作所需的包pnpm add --save-dev aws-cdk-lib constructs @types/aws-lambda - 然后我们想在
apps/next-site中手动创建一个sst.config.ts文件,如下所示:
/// <reference path="./.sst/platform/config.d.ts" />
export default $config({
app(input) {
return {
name: "next-site", // 在这里使用你的项目名称
removal: input?.stage === "production" ? "retain" : "remove",
home: "aws",
};
},
async run() {
new sst.aws.Nextjs("Site", {
buildCommand: "exit 0;", // 同样,我们希望让 Nx 来处理构建
});
},
});- 现在,你可能看到一些类型错误,因为 SST 尚未初始化,但我们可以通过运行来解决这个问题
$ cd apps/next-site && sst install这将解决类型问题并初始化 SST。
-
接下来我们需要将
sst.config.ts添加到我们的tsconfig.json的 excludes 数组中 -
然后我们想将
sst.config.ts和.sst文件夹都添加到 eslint ignorePatterns 中
{
"extends": [
"plugin:@nx/react-typescript",
"next",
"next/core-web-vitals",
"../../.eslintrc.json"
],
"ignorePatterns": [
"!**/*",
".next/**/*",
+ ".open-next/**/*",
+ ".sst/**/*",
+ "sst.config.ts"
],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.spec.ts", "*.spec.tsx", "*.spec.js", "*.spec.jsx"],
"env": {
"jest": true
}
}
]
}- 现在,如果你想运行
sst dev,你可以使用sst dev "nx dev next-site"来完成,类似地部署可以使用sst deploy完成...但你可能想要设置该任务链,同样我们可以通过向你的应用添加一个 target 来实现,并将其dependsOn设置为open-next-build,它可能看起来像这样:
{
"name": "next-site",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/next-site",
"projectType": "application",
"tags": [],
"targets": {
"open-next-build": {
"executor": "nx:run-commands",
"dependsOn": ["build"],
"cache": true,
"outputs": ["{projectRoot}/.open-next"],
"options": {
"cwd": "apps/next-site",
"command": "open-next build"
}
+ },
+ "deploy": {
+ "executor": "nx:run-commands",
+ "dependsOn": ["open-next-build"],
+ "options": {
+ "cwd": "apps/next-site",
+ "command": "sst deploy --stage {args.stage}", // 这里我们使用 nx 的插值来允许传递 --stage,下面有一些配置示例
+ "forwardAllArgs": true
+ },
+ "defaultConfiguration": "dev",
+ "configurations": {
+ "production": {
+ "args": ["--stage=production"]
+ },
+ "staging": {
+ "args": ["--stage=staging"]
+ },
+ "dev": {
+ "args": ["--stage=development"]
+ }
+ }
+ }
+ }
}
现在我们可以运行(或者如果你想要一个自定义阶段,你可以简单地执行 nx deploy next-site --stage this-is-my-stage,它将直接传递给 sst 命令)。
$ nx deploy next-site --configuration dev # 使用 dev 配置(它将阶段设置为 development)
# nx deploy next-site -c dev # 或
# nx deploy next-site --stage my-stage # 自定义阶段