Skip to content
Merged
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
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"editor.formatOnSave": true
"editor.formatOnSave": true,
"cSpell.words": ["nextjs"]
}
14 changes: 9 additions & 5 deletions packages/cloudflare/src/build/build-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { NextjsAppPaths } from "../nextjs-paths";
import { build, Plugin } from "esbuild";
import { existsSync, readFileSync } from "node:fs";
import { cp, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";

import { patchRequire } from "./patches/investigated/patch-require";
import { copyTemplates } from "./patches/investigated/copy-templates";
Expand All @@ -13,17 +15,19 @@ import { inlineEvalManifest } from "./patches/to-investigate/inline-eval-manifes
import { patchWranglerDeps } from "./patches/to-investigate/wrangler-deps";
import { updateWebpackChunksFile } from "./patches/investigated/update-webpack-chunks-file";

/** The directory containing the Cloudflare template files. */
const templateSrcDir = path.join(path.dirname(fileURLToPath(import.meta.url)), "templates");

/**
* Using the Next.js build output in the `.next` directory builds a workerd compatible output
*
* @param outputDir the directory where to save the output
* @param nextjsAppPaths
*/
export async function buildWorker(
inputNextAppDir: string,
appDir: string,
outputDir: string,
nextjsAppPaths: NextjsAppPaths,
templateSrcDir: string
nextjsAppPaths: NextjsAppPaths
): Promise<void> {
const templateDir = copyTemplates(templateSrcDir, nextjsAppPaths);

Expand Down Expand Up @@ -132,8 +136,8 @@ Request = globalThis.Request;
});

// Copy over any static files (e.g. images) from the source project
if (existsSync(`${inputNextAppDir}/public`)) {
await cp(`${inputNextAppDir}/public`, `${outputDir}/assets`, {
if (existsSync(`${appDir}/public`)) {
await cp(`${appDir}/public`, `${outputDir}/assets`, {
recursive: true,
});
}
Expand Down
39 changes: 12 additions & 27 deletions packages/cloudflare/src/build/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,33 @@ import { rm } from "node:fs/promises";
import { buildNextjsApp } from "./build-next-app";
import { buildWorker } from "./build-worker";
import { getNextjsAppPaths } from "../nextjs-paths";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { cpSync, rmSync } from "node:fs";

const SAVE_DIR = ".save.next";
import { cpSync } from "node:fs";
import { resolve } from "node:path";

/**
* Builds the application in a format that can be passed to workerd
*
* It saves the output in a `.worker-next` directory
*
* @param inputNextAppDir the directory of the Next.js app to build
* @param appDir the directory of the Next.js app to build
* @param opts.outputDir the directory where to save the output (defaults to the app's directory)
* @param opts.skipBuild boolean indicating whether the Next.js build should be skipped (i.e. if the `.next` dir is already built)
*/
export async function build(inputNextAppDir: string, opts: BuildOptions): Promise<void> {
export async function build(appDir: string, opts: BuildOptions): Promise<void> {
if (!opts.skipBuild) {
// Build the next app and save a copy in .save.next
buildNextjsApp(inputNextAppDir);
rmSync(`${inputNextAppDir}/${SAVE_DIR}`, {
recursive: true,
force: true,
});
cpSync(`${inputNextAppDir}/.next`, `${inputNextAppDir}/${SAVE_DIR}`, {
recursive: true,
});
} else {
// Skip the next build and restore the copy from .next.save
rmSync(`${inputNextAppDir}/.next`, { recursive: true, force: true });
cpSync(`${inputNextAppDir}/${SAVE_DIR}`, `${inputNextAppDir}/.next`, {
recursive: true,
});
// Build the next app
buildNextjsApp(appDir);
}

const outputDir = `${opts.outputDir ?? inputNextAppDir}/.worker-next`;
// Create a clean output directory
const outputDir = resolve(opts.outputDir ?? appDir, ".worker-next");
await cleanDirectory(outputDir);

const nextjsAppPaths = getNextjsAppPaths(inputNextAppDir);

const templateDir = path.join(path.dirname(fileURLToPath(import.meta.url)), "templates");
// Copy the .next directory to the output directory so it can be mutated.
cpSync(resolve(`${appDir}/.next`), resolve(`${outputDir}/.next`), { recursive: true });
const nextjsAppPaths = getNextjsAppPaths(outputDir);

await buildWorker(inputNextAppDir, outputDir, nextjsAppPaths, templateDir);
await buildWorker(appDir, outputDir, nextjsAppPaths);
}

type BuildOptions = {
Expand Down
8 changes: 3 additions & 5 deletions packages/cloudflare/src/nextjs-paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import path, { relative } from "node:path";
* NOTE: WIP, we still need to discern which paths are relevant here!
*/
export type NextjsAppPaths = {
appDir: string;
/**
* The path to the application's `.next` directory (where `next build` saves the build output)
*/
Expand All @@ -32,18 +31,17 @@ export type NextjsAppPaths = {
/**
* Collects all the paths necessary for dealing with the Next.js applications output
*
* @param nextAppDir The path to the Next.js app
* @param baseDir The path to the directory that contains the .next directory
* @returns the various paths.
*/
export function getNextjsAppPaths(nextAppDir: string): NextjsAppPaths {
const dotNextDir = getDotNextDirPath(nextAppDir);
export function getNextjsAppPaths(baseDir: string): NextjsAppPaths {
const dotNextDir = getDotNextDirPath(baseDir);

const appPath = getNextjsApplicationPath(dotNextDir).replace(/\/$/, "");

const standaloneAppDir = path.join(dotNextDir, "standalone", appPath);

return {
appDir: nextAppDir,
dotNextDir,
standaloneAppDir,
standaloneAppDotNextDir: path.join(standaloneAppDir, ".next"),
Expand Down