diff --git a/templates/arc/.eslintrc.js b/templates/arc/.eslintrc.cjs
similarity index 100%
rename from templates/arc/.eslintrc.js
rename to templates/arc/.eslintrc.cjs
diff --git a/templates/arc/.gitignore b/templates/arc/.gitignore
index 592d769e5d8..c8f4835d63e 100644
--- a/templates/arc/.gitignore
+++ b/templates/arc/.gitignore
@@ -1,7 +1,9 @@
+.DS_Store
node_modules
/.cache
-/server/index.js
+/server/index.mjs
+/server/index.mjs.map
/public/build
preferences.arc
sam.json
diff --git a/templates/arc/README.md b/templates/arc/README.md
index bf6f90a08d3..2c43f6611c6 100644
--- a/templates/arc/README.md
+++ b/templates/arc/README.md
@@ -4,6 +4,18 @@
## Development
+Create a `preferences.arc` file in the root with the following contents:
+
+```
+@sandbox
+livereload false
+
+# NODE_ENV development is required when running the dev server
+@env
+testing
+ NODE_ENV development
+```
+
The following command will run two processes during development when using Architect as your server.
- Your Architect server sandbox
diff --git a/templates/arc/app.arc b/templates/arc/app.arc
index 4e472fca56b..79428bd94fe 100644
--- a/templates/arc/app.arc
+++ b/templates/arc/app.arc
@@ -8,6 +8,10 @@ remix-architect-app
@static
+@plugins
+plugin-remix
+ src plugin-remix.js
+
# @aws
# profile default
# region us-west-1
diff --git a/templates/arc/app/root.tsx b/templates/arc/app/root.tsx
index 8cb74a167f8..f10a63cf481 100644
--- a/templates/arc/app/root.tsx
+++ b/templates/arc/app/root.tsx
@@ -19,6 +19,7 @@ export default function App() {
+
diff --git a/templates/arc/package.json b/templates/arc/package.json
index e2d5ad3866b..90233f2cab3 100644
--- a/templates/arc/package.json
+++ b/templates/arc/package.json
@@ -1,11 +1,10 @@
{
"private": true,
"sideEffects": false,
+ "type": "module",
"scripts": {
"build": "remix build",
- "dev:remix": "remix watch",
- "dev:arc": "cross-env NODE_ENV=development arc sandbox",
- "dev": "npm-run-all build --parallel \"dev:*\"",
+ "dev": "remix dev -c \"arc sandbox -e testing\"",
"start": "cross-env NODE_ENV=production arc sandbox",
"typecheck": "tsc"
},
@@ -20,13 +19,12 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
- "@architect/architect": "^10.11.2",
+ "@architect/architect": "^10.12.1",
"@remix-run/dev": "*",
"@remix-run/eslint-config": "*",
"@types/react": "^18.0.35",
"@types/react-dom": "^18.0.11",
"eslint": "^8.38.0",
- "npm-run-all": "^4.1.5",
"typescript": "^5.0.4"
},
"engines": {
diff --git a/templates/arc/plugin-remix.js b/templates/arc/plugin-remix.js
new file mode 100644
index 00000000000..4de7ebaf6df
--- /dev/null
+++ b/templates/arc/plugin-remix.js
@@ -0,0 +1,37 @@
+// This should eventually be a npm package, but for now it lives here.
+// It's job is to notify the remix dev server of the version of the running
+// app to trigger HMR / HDR.
+
+import * as fs from "node:fs";
+import * as path from "node:path";
+import { logDevReady } from "@remix-run/node";
+
+const buildPath = "server/index.mjs";
+
+let lastTimeout;
+
+export default {
+ sandbox: {
+ async watcher() {
+ if (lastTimeout) {
+ clearTimeout(lastTimeout);
+ }
+
+ lastTimeout = setTimeout(async () => {
+ const contents = fs.readFileSync(
+ path.resolve(process.cwd(), buildPath),
+ "utf8"
+ );
+ const manifestMatches = contents.matchAll(/manifest-([A-f0-9]+)\.js/g);
+ const sent = new Set();
+ for (const match of manifestMatches) {
+ const buildHash = match[1];
+ if (!sent.has(buildHash)) {
+ sent.add(buildHash);
+ logDevReady({ assets: { version: buildHash } });
+ }
+ }
+ }, 300);
+ },
+ },
+};
diff --git a/templates/arc/remix.config.js b/templates/arc/remix.config.js
index b76efda0f3a..c0345763aae 100644
--- a/templates/arc/remix.config.js
+++ b/templates/arc/remix.config.js
@@ -1,12 +1,14 @@
/** @type {import('@remix-run/dev').AppConfig} */
-module.exports = {
+export default {
ignoredRouteFiles: ["**/.*"],
publicPath: "/_static/build/",
- server: "./server.ts",
- serverBuildPath: "server/index.js",
+ server: "server.ts",
+ serverBuildPath: "server/index.mjs",
// appDirectory: "app",
// assetsBuildDirectory: "public/build",
+ serverModuleFormat: "esm",
future: {
+ v2_dev: true,
v2_errorBoundary: true,
v2_headers: true,
v2_meta: true,
diff --git a/templates/arc/server/config.arc b/templates/arc/server/config.arc
index 3d11ce0fc30..990ffe2fbe5 100644
--- a/templates/arc/server/config.arc
+++ b/templates/arc/server/config.arc
@@ -1,5 +1,5 @@
@aws
-runtime nodejs14.x
+runtime nodejs16.x
# memory 1152
# timeout 30
# concurrency 1
diff --git a/templates/cloudflare-pages/.eslintrc.js b/templates/cloudflare-pages/.eslintrc.cjs
similarity index 100%
rename from templates/cloudflare-pages/.eslintrc.js
rename to templates/cloudflare-pages/.eslintrc.cjs
diff --git a/templates/cloudflare-pages/.gitignore b/templates/cloudflare-pages/.gitignore
index 7c0736ebf5a..e542b26b368 100644
--- a/templates/cloudflare-pages/.gitignore
+++ b/templates/cloudflare-pages/.gitignore
@@ -1,3 +1,4 @@
+.DS_Store
node_modules
/.cache
diff --git a/templates/cloudflare-pages/package.json b/templates/cloudflare-pages/package.json
index 7e91782b468..982c8cd68ce 100644
--- a/templates/cloudflare-pages/package.json
+++ b/templates/cloudflare-pages/package.json
@@ -1,21 +1,18 @@
{
"private": true,
"sideEffects": false,
+ "type": "module",
"scripts": {
"build": "remix build",
- "dev:remix": "remix watch",
- "dev:wrangler": "cross-env NODE_ENV=development npm run wrangler",
- "dev": "npm-run-all build --parallel \"dev:*\"",
- "start": "cross-env NODE_ENV=production npm run wrangler",
- "typecheck": "tsc",
- "wrangler": "wrangler pages dev ./public"
+ "dev": "remix dev --no-restart -c \"npm run start\"",
+ "start": "wrangler pages dev --compatibility-date=2023-06-21 ./public",
+ "typecheck": "tsc"
},
"dependencies": {
"@remix-run/cloudflare": "*",
"@remix-run/cloudflare-pages": "*",
"@remix-run/css-bundle": "*",
"@remix-run/react": "*",
- "cross-env": "^7.0.3",
"isbot": "^3.6.8",
"react": "^18.2.0",
"react-dom": "^18.2.0"
@@ -27,9 +24,8 @@
"@types/react": "^18.0.35",
"@types/react-dom": "^18.0.11",
"eslint": "^8.38.0",
- "npm-run-all": "^4.1.5",
"typescript": "^5.0.4",
- "wrangler": "^2.15.1"
+ "wrangler": "^3.1.1"
},
"engines": {
"node": ">=16.13.0"
diff --git a/templates/cloudflare-pages/public/_headers b/templates/cloudflare-pages/public/_headers
index dd69b93b392..c5129f35cd3 100644
--- a/templates/cloudflare-pages/public/_headers
+++ b/templates/cloudflare-pages/public/_headers
@@ -1,2 +1,4 @@
+/favicon.ico
+ Cache-Control: public, max-age=3600, s-maxage=3600
/build/*
Cache-Control: public, max-age=31536000, immutable
diff --git a/templates/cloudflare-pages/public/_routes.json b/templates/cloudflare-pages/public/_routes.json
index 5826b059c08..4b57270dae9 100644
--- a/templates/cloudflare-pages/public/_routes.json
+++ b/templates/cloudflare-pages/public/_routes.json
@@ -1,5 +1,5 @@
{
"version": 1,
"include": ["/*"],
- "exclude": ["/build/*"]
+ "exclude": ["/favicon.ico", "/build/*"]
}
diff --git a/templates/cloudflare-pages/remix.config.js b/templates/cloudflare-pages/remix.config.js
index 6f5ffe0fffe..d31efea86fe 100644
--- a/templates/cloudflare-pages/remix.config.js
+++ b/templates/cloudflare-pages/remix.config.js
@@ -1,5 +1,5 @@
/** @type {import('@remix-run/dev').AppConfig} */
-module.exports = {
+export default {
devServerBroadcastDelay: 1000,
ignoredRouteFiles: ["**/.*"],
server: "./server.ts",
@@ -14,6 +14,7 @@ module.exports = {
// assetsBuildDirectory: "public/build",
// publicPath: "/build/",
future: {
+ v2_dev: true,
v2_errorBoundary: true,
v2_headers: true,
v2_meta: true,
diff --git a/templates/cloudflare-pages/server.ts b/templates/cloudflare-pages/server.ts
index b1681c47979..d8f4dcf1425 100644
--- a/templates/cloudflare-pages/server.ts
+++ b/templates/cloudflare-pages/server.ts
@@ -1,8 +1,13 @@
+import { logDevReady } from "@remix-run/cloudflare";
import { createPagesFunctionHandler } from "@remix-run/cloudflare-pages";
import * as build from "@remix-run/dev/server-build";
+if (process.env.NODE_ENV === "development") {
+ logDevReady(build);
+}
+
export const onRequest = createPagesFunctionHandler({
build,
- getLoadContext: (context) => context.env,
+ getLoadContext: (context) => ({ env: context.env }),
mode: process.env.NODE_ENV,
});
diff --git a/templates/cloudflare-pages/wrangler.toml b/templates/cloudflare-pages/wrangler.toml
deleted file mode 100644
index ecd695e5a65..00000000000
--- a/templates/cloudflare-pages/wrangler.toml
+++ /dev/null
@@ -1,2 +0,0 @@
-compatibility_date = "2022-04-05"
-compatibility_flags = ["streams_enable_constructors"]
diff --git a/templates/cloudflare-workers/.eslintrc.js b/templates/cloudflare-workers/.eslintrc.cjs
similarity index 100%
rename from templates/cloudflare-workers/.eslintrc.js
rename to templates/cloudflare-workers/.eslintrc.cjs
diff --git a/templates/cloudflare-workers/.gitignore b/templates/cloudflare-workers/.gitignore
index f0421bd7025..7c29f45a232 100644
--- a/templates/cloudflare-workers/.gitignore
+++ b/templates/cloudflare-workers/.gitignore
@@ -1,3 +1,4 @@
+.DS_Store
node_modules
/.cache
diff --git a/templates/cloudflare-workers/package.json b/templates/cloudflare-workers/package.json
index 9ca72d4bfcb..7cca279a472 100644
--- a/templates/cloudflare-workers/package.json
+++ b/templates/cloudflare-workers/package.json
@@ -1,21 +1,18 @@
{
"private": true,
"sideEffects": false,
+ "type": "module",
"scripts": {
"build": "remix build",
- "deploy": "wrangler publish",
- "dev:remix": "remix watch",
- "dev:miniflare": "cross-env NODE_ENV=development miniflare ./build/index.js --watch",
- "dev": "npm-run-all build --parallel \"dev:*\"",
- "start": "cross-env NODE_ENV=production miniflare ./build/index.js",
+ "deploy": "remix build && wrangler publish",
+ "dev": "remix dev --no-restart -c \"npm start\"",
+ "start": "wrangler dev ./build/index.js",
"typecheck": "tsc"
},
"dependencies": {
"@remix-run/cloudflare": "*",
- "@remix-run/cloudflare-workers": "*",
"@remix-run/css-bundle": "*",
"@remix-run/react": "*",
- "cross-env": "^7.0.3",
"isbot": "^3.6.8",
"react": "^18.2.0",
"react-dom": "^18.2.0"
@@ -27,10 +24,8 @@
"@types/react": "^18.0.35",
"@types/react-dom": "^18.0.11",
"eslint": "^8.38.0",
- "miniflare": "^2.13.0",
- "npm-run-all": "^4.1.5",
"typescript": "^5.0.4",
- "wrangler": "^2.15.1"
+ "wrangler": "^3.1.1"
},
"engines": {
"node": ">=16.13.0"
diff --git a/templates/cloudflare-workers/remix.config.js b/templates/cloudflare-workers/remix.config.js
index c4210dd979a..b971b575863 100644
--- a/templates/cloudflare-workers/remix.config.js
+++ b/templates/cloudflare-workers/remix.config.js
@@ -1,10 +1,12 @@
/** @type {import('@remix-run/dev').AppConfig} */
-module.exports = {
- devServerBroadcastDelay: 1000,
+export default {
ignoredRouteFiles: ["**/.*"],
server: "./server.ts",
serverConditions: ["worker"],
- serverDependenciesToBundle: "all",
+ serverDependenciesToBundle: [
+ // bundle verything except the virtual module for the static content manifest provided by wrangler
+ /^(?!.*\b__STATIC_CONTENT_MANIFEST\b).*$/,
+ ],
serverMainFields: ["browser", "module", "main"],
serverMinify: true,
serverModuleFormat: "esm",
@@ -14,6 +16,7 @@ module.exports = {
// serverBuildPath: "build/index.js",
// publicPath: "/build/",
future: {
+ v2_dev: true,
v2_errorBoundary: true,
v2_headers: true,
v2_meta: true,
diff --git a/templates/cloudflare-workers/remix.env.d.ts b/templates/cloudflare-workers/remix.env.d.ts
index 425870ae632..b5be9ba3bbe 100644
--- a/templates/cloudflare-workers/remix.env.d.ts
+++ b/templates/cloudflare-workers/remix.env.d.ts
@@ -1,3 +1,8 @@
///
///
///
+
+declare module "__STATIC_CONTENT_MANIFEST" {
+ const manifest: string;
+ export default manifest;
+}
diff --git a/templates/cloudflare-workers/server.ts b/templates/cloudflare-workers/server.ts
index 4f4b2ad0aff..d80fdf4cd2c 100644
--- a/templates/cloudflare-workers/server.ts
+++ b/templates/cloudflare-workers/server.ts
@@ -1,7 +1,53 @@
-import { createEventHandler } from "@remix-run/cloudflare-workers";
+import { getAssetFromKV } from "@cloudflare/kv-asset-handler";
+import type { AppLoadContext } from "@remix-run/cloudflare";
+import { createRequestHandler, logDevReady } from "@remix-run/cloudflare";
import * as build from "@remix-run/dev/server-build";
+import __STATIC_CONTENT_MANIFEST from "__STATIC_CONTENT_MANIFEST";
-addEventListener(
- "fetch",
- createEventHandler({ build, mode: process.env.NODE_ENV })
-);
+const MANIFEST = JSON.parse(__STATIC_CONTENT_MANIFEST);
+const handleRemixRequest = createRequestHandler(build, process.env.NODE_ENV);
+
+if (build.dev) {
+ logDevReady(build);
+}
+
+export default {
+ async fetch(
+ request: Request,
+ env: {
+ __STATIC_CONTENT: Fetcher;
+ },
+ ctx: ExecutionContext
+ ): Promise {
+ try {
+ const url = new URL(request.url);
+ const ttl = url.pathname.startsWith("/build/")
+ ? 60 * 60 * 24 * 365 // 1 year
+ : 60 * 5; // 5 minutes
+ return await getAssetFromKV(
+ {
+ request,
+ waitUntil: ctx.waitUntil.bind(ctx),
+ } as FetchEvent,
+ {
+ ASSET_NAMESPACE: env.__STATIC_CONTENT,
+ ASSET_MANIFEST: MANIFEST,
+ cacheControl: {
+ browserTTL: ttl,
+ edgeTTL: ttl,
+ },
+ }
+ );
+ } catch (error) {}
+
+ try {
+ const loadContext: AppLoadContext = {
+ env,
+ };
+ return await handleRemixRequest(request, loadContext);
+ } catch (error) {
+ console.log(error);
+ return new Response("An unexpected error occurred", { status: 500 });
+ }
+ },
+};
diff --git a/templates/cloudflare-workers/wrangler.toml b/templates/cloudflare-workers/wrangler.toml
index 6dae0da58da..b4ddc4387f8 100644
--- a/templates/cloudflare-workers/wrangler.toml
+++ b/templates/cloudflare-workers/wrangler.toml
@@ -3,12 +3,7 @@ name = "remix-cloudflare-workers"
workers_dev = true
main = "./build/index.js"
# https://developers.cloudflare.com/workers/platform/compatibility-dates
-compatibility_date = "2022-04-05"
-compatibility_flags = ["streams_enable_constructors"]
+compatibility_date = "2023-04-20"
[site]
bucket = "./public"
-
-[build]
- command = "npm run build"
-
diff --git a/templates/express/.eslintrc.js b/templates/express/.eslintrc.cjs
similarity index 100%
rename from templates/express/.eslintrc.js
rename to templates/express/.eslintrc.cjs
diff --git a/templates/express/package.json b/templates/express/package.json
index e13c9097316..486f497a099 100644
--- a/templates/express/package.json
+++ b/templates/express/package.json
@@ -1,11 +1,10 @@
{
"private": true,
"sideEffects": false,
+ "type": "module",
"scripts": {
"build": "remix build",
- "dev": "npm-run-all build --parallel \"dev:*\"",
- "dev:node": "cross-env NODE_ENV=development nodemon --require dotenv/config ./server.js --watch ./server.js",
- "dev:remix": "remix watch",
+ "dev": "remix dev --no-restart -c \"node server.js\"",
"start": "cross-env NODE_ENV=production node ./server.js",
"typecheck": "tsc"
},
@@ -30,10 +29,8 @@
"@types/morgan": "^1.9.4",
"@types/react": "^18.0.35",
"@types/react-dom": "^18.0.11",
- "dotenv": "^16.0.3",
+ "chokidar": "^3.5.3",
"eslint": "^8.38.0",
- "nodemon": "^2.0.22",
- "npm-run-all": "^4.1.5",
"typescript": "^5.0.4"
},
"engines": {
diff --git a/templates/express/remix.config.js b/templates/express/remix.config.js
index 29287a0d890..378d0fc051f 100644
--- a/templates/express/remix.config.js
+++ b/templates/express/remix.config.js
@@ -1,12 +1,13 @@
/** @type {import('@remix-run/dev').AppConfig} */
-module.exports = {
+export default {
ignoredRouteFiles: ["**/.*"],
// appDirectory: "app",
// assetsBuildDirectory: "public/build",
// serverBuildPath: "build/index.js",
// publicPath: "/build/",
- serverModuleFormat: "cjs",
+ serverModuleFormat: "esm",
future: {
+ v2_dev: true,
v2_errorBoundary: true,
v2_headers: true,
v2_meta: true,
diff --git a/templates/express/server.js b/templates/express/server.js
index 8d3d41a639d..10b59924ea4 100644
--- a/templates/express/server.js
+++ b/templates/express/server.js
@@ -1,14 +1,19 @@
-const path = require("path");
+import * as fs from "node:fs";
-const { createRequestHandler } = require("@remix-run/express");
-const { installGlobals } = require("@remix-run/node");
-const compression = require("compression");
-const express = require("express");
-const morgan = require("morgan");
+import { createRequestHandler } from "@remix-run/express";
+import { broadcastDevReady, installGlobals } from "@remix-run/node";
+import chokidar from "chokidar";
+import compression from "compression";
+import express from "express";
+import morgan from "morgan";
installGlobals();
-const BUILD_DIR = path.join(process.cwd(), "build");
+const BUILD_PATH = "./build/index.js";
+/**
+ * @type { import('@remix-run/node').ServerBuild | Promise }
+ */
+let build = await import(BUILD_PATH);
const app = express();
@@ -32,34 +37,42 @@ app.use(morgan("tiny"));
app.all(
"*",
process.env.NODE_ENV === "development"
- ? (req, res, next) => {
- purgeRequireCache();
-
- return createRequestHandler({
- build: require(BUILD_DIR),
- mode: process.env.NODE_ENV,
- })(req, res, next);
- }
+ ? createDevRequestHandler()
: createRequestHandler({
- build: require(BUILD_DIR),
+ build,
mode: process.env.NODE_ENV,
})
);
-const port = process.env.PORT || 3000;
-app.listen(port, () => {
+const port = process.env.PORT || 3000;
+app.listen(port, async () => {
console.log(`Express server listening on port ${port}`);
+
+ if (process.env.NODE_ENV === "development") {
+ broadcastDevReady(build);
+ }
});
-function purgeRequireCache() {
- // purge require cache on requests for "server side HMR" this won't let
- // you have in-memory objects between requests in development,
- // alternatively you can set up nodemon/pm2-dev to restart the server on
- // file changes, but then you'll have to reconnect to databases/etc on each
- // change. We prefer the DX of this, so we've included it for you by default
- for (const key in require.cache) {
- if (key.startsWith(BUILD_DIR)) {
- delete require.cache[key];
+function createDevRequestHandler() {
+ const watcher = chokidar.watch(BUILD_PATH, { ignoreInitial: true });
+
+ watcher.on("all", async () => {
+ // 1. purge require cache && load updated server build
+ const stat = fs.statSync(BUILD_PATH);
+ build = import(BUILD_PATH + "?t=" + stat.mtimeMs);
+ // 2. tell dev server that this app server is now ready
+ broadcastDevReady(await build);
+ });
+
+ return async (req, res, next) => {
+ try {
+ //
+ return createRequestHandler({
+ build: await build,
+ mode: "development",
+ })(req, res, next);
+ } catch (error) {
+ next(error);
}
- }
+ };
}
diff --git a/templates/fly/remix.config.js b/templates/fly/remix.config.js
index cf359205c5e..60aa1cad1d6 100644
--- a/templates/fly/remix.config.js
+++ b/templates/fly/remix.config.js
@@ -5,7 +5,9 @@ module.exports = {
// assetsBuildDirectory: "public/build",
// serverBuildPath: "build/index.js",
// publicPath: "/build/",
+ serverModuleFormat: "cjs",
future: {
+ v2_dev: true,
v2_errorBoundary: true,
v2_headers: true,
v2_meta: true,
diff --git a/templates/netlify/netlify.toml b/templates/netlify/netlify.toml
index 6e33122159d..2ec142b0b1f 100644
--- a/templates/netlify/netlify.toml
+++ b/templates/netlify/netlify.toml
@@ -2,10 +2,6 @@
command = "remix build"
publish = "public"
-[dev]
- command = "remix watch"
- port = 3000
-
[[redirects]]
from = "/*"
to = "/.netlify/functions/server"
diff --git a/templates/netlify/package.json b/templates/netlify/package.json
index 904d242a7ea..16d1d91e585 100644
--- a/templates/netlify/package.json
+++ b/templates/netlify/package.json
@@ -4,7 +4,7 @@
"scripts": {
"build": "remix build",
"dev": "remix dev",
- "start": "cross-env NODE_ENV=production netlify dev",
+ "start": "netlify serve",
"typecheck": "tsc"
},
"dependencies": {
diff --git a/templates/netlify/remix.config.js b/templates/netlify/remix.config.js
index c510d3cc2d2..f54a5a8d198 100644
--- a/templates/netlify/remix.config.js
+++ b/templates/netlify/remix.config.js
@@ -9,7 +9,9 @@ module.exports = {
// appDirectory: "app",
// assetsBuildDirectory: "public/build",
// publicPath: "/build/",
+ serverModuleFormat: "cjs",
future: {
+ v2_dev: true,
v2_errorBoundary: true,
v2_headers: true,
v2_meta: true,
diff --git a/templates/remix/remix.config.js b/templates/remix/remix.config.js
index 29287a0d890..60aa1cad1d6 100644
--- a/templates/remix/remix.config.js
+++ b/templates/remix/remix.config.js
@@ -7,6 +7,7 @@ module.exports = {
// publicPath: "/build/",
serverModuleFormat: "cjs",
future: {
+ v2_dev: true,
v2_errorBoundary: true,
v2_headers: true,
v2_meta: true,
diff --git a/templates/vercel/remix.config.js b/templates/vercel/remix.config.js
index 79e2b95fd7e..f688ad4857b 100644
--- a/templates/vercel/remix.config.js
+++ b/templates/vercel/remix.config.js
@@ -9,7 +9,9 @@ module.exports = {
// appDirectory: "app",
// assetsBuildDirectory: "public/build",
// publicPath: "/build/",
+ serverModuleFormat: "cjs",
future: {
+ v2_dev: true,
v2_errorBoundary: true,
v2_headers: true,
v2_meta: true,