Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletions examples/app-router/open-next.config.local.ts

This file was deleted.

31 changes: 22 additions & 9 deletions examples/app-router/open-next.config.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
const config = {
import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js";

export default {
default: {
override: {
wrapper: "aws-lambda-streaming",
queue: "sqs-lite",
incrementalCache: "s3-lite",
tagCache: "dynamodb-lite",
wrapper: "express-dev",
converter: "node",
incrementalCache: "fs-dev",
queue: "direct",
tagCache: "fs-dev-nextMode",
},
},
functions: {},

dangerous: {
middlewareHeadersOverrideNextConfigHeaders: true,
useAdapterOutputs: true,
enableCacheInterception: true,
},

imageOptimization: {
override: {
wrapper: "dummy",
converter: "dummy",
},
loader: "fs-dev",
},
buildCommand: "npx turbo build",
};

export default config;
// You can override the build command here so that you don't have to rebuild next every time you make a change
//buildCommand: "echo 'No build command'",
} satisfies OpenNextConfig;
1 change: 0 additions & 1 deletion examples/app-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"private": true,
"scripts": {
"openbuild": "node ../../packages/open-next/dist/index.js build",
"openbuild:local": "node ../../packages/open-next/dist/index.js build --config-path open-next.config.local.ts",
"openbuild:local:start": "PORT=3001 OPEN_NEXT_REQUEST_ID_HEADER=true node .open-next/server-functions/default/index.mjs",
"dev": "next dev --turbopack --port 3001",
"build": "next build",
Expand Down
10 changes: 7 additions & 3 deletions packages/open-next/src/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,11 @@ export default {

const cache = compileCache(buildOpts);

const packagePath = buildHelper.getPackagePath(buildOpts);

// We then have to copy the cache files to the .next dir so that they are available at runtime
//TODO: use a better path, this one is temporary just to make it work
const tempCachePath = `${buildOpts.outputDir}/server-functions/default/.open-next/.build`;
const tempCachePath = path.join(buildOpts.outputDir, "server-functions/default", packagePath, ".open-next/.build");
fs.mkdirSync(tempCachePath, { recursive: true });
fs.copyFileSync(cache.cache, path.join(tempCachePath, "cache.cjs"));
fs.copyFileSync(
Expand Down Expand Up @@ -139,8 +141,10 @@ function getAdditionalPluginsFactory(
buildOpts: buildHelper.BuildOptions,
outputs: NextAdapterOutputs,
) {
//TODO: we should make this a property of buildOpts
const packagePath = buildHelper.getPackagePath(buildOpts);
return (updater: ContentUpdater) => [
inlineRouteHandler(updater, outputs),
externalChunksPlugin(outputs),
inlineRouteHandler(updater, outputs, packagePath),
externalChunksPlugin(outputs, packagePath),
];
}
4 changes: 2 additions & 2 deletions packages/open-next/src/adapters/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default class Cache {
kind?: "FETCH";
},
) {
if (globalThis.openNextConfig.dangerous?.disableIncrementalCache) {
if (globalThis.openNextConfig && globalThis.openNextConfig.dangerous?.disableIncrementalCache) {
return null;
}

Expand Down Expand Up @@ -204,7 +204,7 @@ export default class Cache {
data?: IncrementalCacheValue,
ctx?: IncrementalCacheContext,
): Promise<void> {
if (globalThis.openNextConfig.dangerous?.disableIncrementalCache) {
if (globalThis.openNextConfig && globalThis.openNextConfig.dangerous?.disableIncrementalCache) {
return;
}
// This one might not even be necessary anymore
Expand Down
12 changes: 12 additions & 0 deletions packages/open-next/src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import * as buildHelper from "./build/helper.js";
import { patchOriginalNextConfig } from "./build/patch/patches/index.js";
import { printHeader, showWarningOnWindows } from "./build/utils.js";
import logger from "./logger.js";
import { createRequire } from "node:module";

const require = createRequire(import.meta.url);

export type PublicFiles = {
files: string[];
Expand Down Expand Up @@ -54,9 +57,18 @@ export async function build(
// Build Next.js app
printHeader("Building Next.js app");
setStandaloneBuildMode(options);
if(config.dangerous?.useAdapterOutputs) {
logger.info("Using adapter outputs for building OpenNext bundle.");
process.env.NEXT_ADAPTER_PATH = require.resolve("./adapter.js");
}
buildHelper.initOutputDir(options);
buildNextjsApp(options);

if(config.dangerous?.useAdapterOutputs) {
logger.info("Using adapter outputs for building OpenNext bundle.");
return;
}

// Generate deployable bundle
printHeader("Generating bundle");

Expand Down
27 changes: 16 additions & 11 deletions packages/open-next/src/build/copyAdapterFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,41 @@ import type * as buildHelper from "./helper.js";
export async function copyAdapterFiles(
options: buildHelper.BuildOptions,
fnName: string,
packagePath: string,
outputs: NextAdapterOutputs,
) {
const filesToCopy = new Map<string, string>();

// Copying the files from outputs to the output dir
for (const [key, value] of Object.entries(outputs)) {
if (["pages", "pagesApi", "appPages", "appRoutes"].includes(key)) {
for (const route of value as any[]) {
if (["pages", "pagesApi", "appPages", "appRoutes", "middleware"].includes(key)) {

const setFileToCopy = (route: any) => {
const assets = route.assets;
// We need to copy the filepaths to the output dir
const relativeFilePath = path.relative(options.appPath, route.filePath);
// console.log(
// "route.filePath",
// route.filePath,
// "relativeFilePath",
// relativeFilePath,
// );
const relativeFilePath = path.join(packagePath, path.relative(options.appPath, route.filePath));
filesToCopy.set(
route.filePath,
`${options.outputDir}/server-functions/${fnName}/${relativeFilePath}`,
);

for (const [relative, from] of Object.entries(assets || {})) {
// console.log("route.assets", from, relative);
// console.log("route.assets", from, relative, packagePath);
filesToCopy.set(
from as string,
`${options.outputDir}/server-functions/${fnName}/${relative}`,
);
}
// copyFileSync(from, `${options.outputDir}/${relative}`);
}
if(key === "middleware") {
// Middleware is a single object
setFileToCopy(value as any);
} else {
// The rest are arrays
for (const route of value as any[]) {
setFileToCopy(route);
// copyFileSync(from, `${options.outputDir}/${relative}`);
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/open-next/src/build/createAssets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ export function createCacheAssets(options: buildHelper.BuildOptions) {
const buildId = buildHelper.getBuildId(options);
let useTagCache = false;

const dotNextPath = path.join(
const dotNextPath = options.config.dangerous?.useAdapterOutputs ? appBuildOutputPath : path.join(
appBuildOutputPath,
options.config.dangerous?.useAdapterOutputs ? "" : ".next/standalone",
".next/standalone",
packagePath,
);

Expand Down
2 changes: 1 addition & 1 deletion packages/open-next/src/build/createServerBundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ async function generateBundle(

// Copy all necessary traced files
if (config.dangerous?.useAdapterOutputs) {
tracedFiles = await copyAdapterFiles(options, name, nextOutputs!);
tracedFiles = await copyAdapterFiles(options, name, packagePath, nextOutputs!);
//TODO: we should load manifests here
} else {
const oldTracedFileOutput = await copyTracedFiles({
Expand Down
44 changes: 42 additions & 2 deletions packages/open-next/src/core/routing/adapterHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export async function adapterHandler(
) {
let resolved = false;

const pendingPromiseRunner =
globalThis.__openNextAls.getStore()?.pendingPromiseRunner;
const waitUntil = options.waitUntil ?? pendingPromiseRunner?.add.bind(pendingPromiseRunner);

//TODO: replace this at runtime with a version precompiled for the cloudflare adapter.
for (const route of routingResult.resolvedRoutes) {
const module = getHandler(route);
Expand All @@ -27,17 +31,53 @@ export async function adapterHandler(
try {
console.log("## adapterHandler trying route", route, req.url);
const result = await module.handler(req, res, {
waitUntil: options.waitUntil,
waitUntil,
});
await finished(res); // Not sure this one is necessary.
console.log("## adapterHandler route succeeded", route);
resolved = true;
return result;
//If it doesn't throw, we are done
} catch (e) {
console.log("## adapterHandler route failed", route, e);
// I'll have to run some more tests, but in theory, we should not have anything special to do here, and we should return the 500 page here.
// TODO: find the correct one to use.
const module = getHandler({ route: "/_global-error", type: "app" });
try {
if (module) {
await module.handler(req, res, {
waitUntil,
});
resolved = true;
return;
}
}catch (e2) {
console.log("## adapterHandler global error route also failed", e2);
}
res.statusCode = 500;
res.end("Internal Server Error");
await finished(res);
resolved = true;
return;
}
}
if (!resolved) {
console.log("## adapterHandler no route resolved for", req.url);
// TODO: find the correct one to use.
const module = getHandler({ route: "/_not-found", type: "app" });
try {
if (module) {
await module.handler(req, res, {
waitUntil,
});
return;
}
}catch (e2) {
console.log("## adapterHandler not found route also failed", e2);
}
res.statusCode = 404;
res.end("Not Found");
await finished(res);
return;
}
}

Expand Down
17 changes: 10 additions & 7 deletions packages/open-next/src/plugins/inlineRouteHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { ContentUpdater, Plugin } from "./content-updater.js";
export function inlineRouteHandler(
updater: ContentUpdater,
outputs: NextAdapterOutputs,
packagePath: string,
): Plugin {
console.log("## inlineRouteHandler");
return updater.updateContent("inlineRouteHandler", [
Expand All @@ -32,7 +33,7 @@ export function inlineRouteHandler(
callback: ({ contents }) => {
const result = patchCode(contents, inlineChunksRule);
//TODO: Maybe find another way to do that.
return `${result}\n${inlineChunksFn(outputs)}`;
return `${result}\n${inlineChunksFn(outputs, packagePath)}`;
},
},
]);
Expand Down Expand Up @@ -72,15 +73,17 @@ fix:
requireChunk(chunkPath)
`;

function getInlinableChunks(outputs: NextAdapterOutputs, prefix?: string) {
function getInlinableChunks(outputs: NextAdapterOutputs, packagePath: string, prefix?: string) {
const chunks = new Set<string>();
// TODO: handle middleware
for (const type of ["pages", "pagesApi", "appPages", "appRoutes"] as const) {
for (const { assets } of outputs[type]) {
for (const asset of Object.keys(assets)) {
for (let asset of Object.keys(assets)) {
if (
asset.includes(".next/server/chunks/") &&
!asset.includes("[turbopack]_runtime.js")
) {
asset = packagePath !== "" ? asset.replace(`${packagePath}/`, "") : asset;
chunks.add(prefix ? `${prefix}${asset}` : asset);
}
}
Expand All @@ -89,9 +92,9 @@ function getInlinableChunks(outputs: NextAdapterOutputs, prefix?: string) {
return chunks;
}

function inlineChunksFn(outputs: NextAdapterOutputs) {
function inlineChunksFn(outputs: NextAdapterOutputs, packagePath: string) {
// From the outputs, we extract every chunks
const chunks = getInlinableChunks(outputs);
const chunks = getInlinableChunks(outputs, packagePath);
return `
function requireChunk(chunk) {
const chunkPath = ".next/" + chunk;
Expand All @@ -109,8 +112,8 @@ ${Array.from(chunks)
/**
* Esbuild plugin to mark all chunks that we inline as external.
*/
export function externalChunksPlugin(outputs: NextAdapterOutputs): Plugin {
const chunks = getInlinableChunks(outputs, "./");
export function externalChunksPlugin(outputs: NextAdapterOutputs, packagePath: string): Plugin {
const chunks = getInlinableChunks(outputs, packagePath, `./`);
return {
name: "external-chunks",
setup(build) {
Expand Down
5 changes: 4 additions & 1 deletion packages/open-next/src/utils/promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ export class DetachedPromiseRunner {
public add<T>(promise: Promise<T>): void {
const detachedPromise = new DetachedPromise<T>();
this.promises.push(detachedPromise);
promise.then(detachedPromise.resolve, detachedPromise.reject);
promise.then(detachedPromise.resolve).catch((e) => {
// We just want to log the error here to avoid unhandled promise rejections
error("Detached promise rejected:", e);
});
}

public async await(): Promise<void> {
Expand Down
Loading
Loading