diff --git a/packages/cloudflare/src/cli/build/build-worker.ts b/packages/cloudflare/src/cli/build/build-worker.ts index 0fe9dd9..e2b2b37 100644 --- a/packages/cloudflare/src/cli/build/build-worker.ts +++ b/packages/cloudflare/src/cli/build/build-worker.ts @@ -1,5 +1,6 @@ import { Plugin, build } from "esbuild"; import { cp, readFile, writeFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; import { existsSync, readFileSync } from "node:fs"; import { Config } from "../config"; import { copyPackageCliFiles } from "./patches/investigated/copy-package-cli-files"; @@ -14,11 +15,10 @@ import { patchFindDir } from "./patches/to-investigate/patch-find-dir"; import { patchReadFile } from "./patches/to-investigate/patch-read-file"; import { patchRequire } from "./patches/investigated/patch-require"; import { patchWranglerDeps } from "./patches/to-investigate/wrangler-deps"; -import path from "node:path"; import { updateWebpackChunksFile } from "./patches/investigated/update-webpack-chunks-file"; /** The dist directory of the Cloudflare adapter package */ -const packageDistDir = path.join(path.dirname(fileURLToPath(import.meta.url)), ".."); +const packageDistDir = join(dirname(fileURLToPath(import.meta.url)), ".."); /** * Using the Next.js build output in the `.next` directory builds a workerd compatible output @@ -30,18 +30,14 @@ export async function buildWorker(config: Config): Promise { console.log(`\x1b[35m⚙️ Copying files...\n\x1b[0m`); // Copy over client-side generated files - await cp( - path.join(config.paths.dotNext, "static"), - path.join(config.paths.outputDir, "assets", "_next", "static"), - { - recursive: true, - } - ); + await cp(join(config.paths.dotNext, "static"), join(config.paths.outputDir, "assets", "_next", "static"), { + recursive: true, + }); // Copy over any static files (e.g. images) from the source project - const publicDir = path.join(config.paths.sourceDir, "public"); + const publicDir = join(config.paths.sourceDir, "public"); if (existsSync(publicDir)) { - await cp(publicDir, path.join(config.paths.outputDir, "assets"), { + await cp(publicDir, join(config.paths.outputDir, "assets"), { recursive: true, }); } @@ -51,11 +47,11 @@ export async function buildWorker(config: Config): Promise { copyPackageCliFiles(packageDistDir, config); - const workerEntrypoint = path.join(config.paths.internalTemplates, "worker.ts"); - const workerOutputFile = path.join(config.paths.outputDir, "index.mjs"); + const workerEntrypoint = join(config.paths.internalTemplates, "worker.ts"); + const workerOutputFile = join(config.paths.outputDir, "index.mjs"); const nextConfigStr = - readFileSync(path.join(config.paths.standaloneApp, "/server.js"), "utf8")?.match( + readFileSync(join(config.paths.standaloneApp, "/server.js"), "utf8")?.match( /const nextConfig = ({.+?})\n/ )?.[1] ?? {}; @@ -76,15 +72,15 @@ export async function buildWorker(config: Config): Promise { // Note: we apply an empty shim to next/dist/compiled/ws because it generates two `eval`s: // eval("require")("bufferutil"); // eval("require")("utf-8-validate"); - "next/dist/compiled/ws": path.join(config.paths.internalTemplates, "shims", "empty.ts"), + "next/dist/compiled/ws": join(config.paths.internalTemplates, "shims", "empty.ts"), // Note: we apply an empty shim to next/dist/compiled/edge-runtime since (amongst others) it generated the following `eval`: // eval(getModuleCode)(module, module.exports, throwingRequire, params.context, ...Object.values(params.scopedContext)); // which comes from https://github.com/vercel/edge-runtime/blob/6e96b55f/packages/primitives/src/primitives/load.js#L57-L63 // QUESTION: Why did I encountered this but mhart didn't? - "next/dist/compiled/edge-runtime": path.join(config.paths.internalTemplates, "shims", "empty.ts"), + "next/dist/compiled/edge-runtime": join(config.paths.internalTemplates, "shims", "empty.ts"), // `@next/env` is a library Next.js uses for loading dotenv files, for obvious reasons we need to stub it here // source: https://github.com/vercel/next.js/tree/0ac10d79720/packages/next-env - "@next/env": path.join(config.paths.internalTemplates, "shims", "env.ts"), + "@next/env": join(config.paths.internalTemplates, "shims", "env.ts"), }, define: { // config file used by Next.js, see: https://github.com/vercel/next.js/blob/68a7128/packages/next/src/build/utils.ts#L2137-L2139 @@ -178,10 +174,10 @@ function createFixRequiresESBuildPlugin(config: Config): Plugin { setup(build) { // Note: we (empty) shim require-hook modules as they generate problematic code that uses requires build.onResolve({ filter: /^\.\/require-hook$/ }, () => ({ - path: path.join(config.paths.internalTemplates, "shims", "empty.ts"), + path: join(config.paths.internalTemplates, "shims", "empty.ts"), })); build.onResolve({ filter: /\.\/lib\/node-fs-methods$/ }, () => ({ - path: path.join(config.paths.internalTemplates, "shims", "empty.ts"), + path: join(config.paths.internalTemplates, "shims", "empty.ts"), })); }, }; diff --git a/packages/cloudflare/src/cli/build/patches/investigated/copy-package-cli-files.ts b/packages/cloudflare/src/cli/build/patches/investigated/copy-package-cli-files.ts index 544b8d4..77c5314 100644 --- a/packages/cloudflare/src/cli/build/patches/investigated/copy-package-cli-files.ts +++ b/packages/cloudflare/src/cli/build/patches/investigated/copy-package-cli-files.ts @@ -1,14 +1,14 @@ import { Config } from "../../../config"; import { cpSync } from "node:fs"; -import path from "node:path"; +import { join } from "node:path"; /** * Copies the template files present in the cloudflare adapter package into the standalone node_modules folder */ export function copyPackageCliFiles(packageDistDir: string, config: Config) { console.log("# copyPackageTemplateFiles"); - const sourceDir = path.join(packageDistDir, "cli"); - const destinationDir = path.join(config.paths.internalPackage, "cli"); + const sourceDir = join(packageDistDir, "cli"); + const destinationDir = join(config.paths.internalPackage, "cli"); cpSync(sourceDir, destinationDir, { recursive: true }); } diff --git a/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/index.ts b/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/index.ts index c1fe1ba..b6c4902 100644 --- a/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/index.ts +++ b/packages/cloudflare/src/cli/build/patches/investigated/update-webpack-chunks-file/index.ts @@ -1,7 +1,7 @@ import { readFileSync, readdirSync, writeFileSync } from "node:fs"; import { Config } from "../../../../config"; import { getUpdatedWebpackChunksFileContent } from "./get-updated-webpack-chunks-file-content"; -import path from "node:path"; +import { join } from "node:path"; /** * Fixes the webpack-runtime.js file by removing its webpack dynamic requires. @@ -11,11 +11,11 @@ import path from "node:path"; */ export async function updateWebpackChunksFile(config: Config) { console.log("# updateWebpackChunksFile"); - const webpackRuntimeFile = path.join(config.paths.standaloneAppServer, "webpack-runtime.js"); + const webpackRuntimeFile = join(config.paths.standaloneAppServer, "webpack-runtime.js"); const fileContent = readFileSync(webpackRuntimeFile, "utf-8"); - const chunks = readdirSync(path.join(config.paths.standaloneAppServer, "chunks")) + const chunks = readdirSync(join(config.paths.standaloneAppServer, "chunks")) .filter((chunk) => /^\d+\.js$/.test(chunk)) .map((chunk) => { console.log(` - chunk ${chunk}`); diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-eval-manifest.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-eval-manifest.ts index 654ee98..8181a94 100644 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-eval-manifest.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-eval-manifest.ts @@ -1,6 +1,6 @@ +import { join, posix } from "node:path"; import { Config } from "../../../config"; import { globSync } from "glob"; -import path from "node:path"; import { normalizePath } from "../../utils"; /** @@ -13,10 +13,8 @@ import { normalizePath } from "../../utils"; export function inlineEvalManifest(code: string, config: Config): string { console.log("# inlineEvalManifest"); const manifestJss = globSync( - normalizePath(path.join(config.paths.standaloneAppDotNext, "**", "*_client-reference-manifest.js")) - ).map((file) => - normalizePath(file).replace(normalizePath(config.paths.standaloneApp) + path.posix.sep, "") - ); + normalizePath(join(config.paths.standaloneAppDotNext, "**", "*_client-reference-manifest.js")) + ).map((file) => normalizePath(file).replace(normalizePath(config.paths.standaloneApp) + posix.sep, "")); return code.replace( /function evalManifest\((.+?), .+?\) {/, `$& @@ -24,7 +22,7 @@ export function inlineEvalManifest(code: string, config: Config): string { .map( (manifestJs) => ` if ($1.endsWith("${manifestJs}")) { - require(${JSON.stringify(path.join(config.paths.standaloneApp, manifestJs))}); + require(${JSON.stringify(join(config.paths.standaloneApp, manifestJs))}); return { __RSC_MANIFEST: { "${manifestJs diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts index a630294..513eb84 100644 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-middleware-manifest-require.ts @@ -1,6 +1,6 @@ import { existsSync, readFileSync } from "node:fs"; import { Config } from "../../../config"; -import path from "node:path"; +import { join } from "node:path"; /** * Inlines the middleware manifest from the build output to prevent a dynamic require statement @@ -9,7 +9,7 @@ import path from "node:path"; export function inlineMiddlewareManifestRequire(code: string, config: Config) { console.log("# inlineMiddlewareManifestRequire"); - const middlewareManifestPath = path.join(config.paths.standaloneAppServer, "middleware-manifest.json"); + const middlewareManifestPath = join(config.paths.standaloneAppServer, "middleware-manifest.json"); const middlewareManifest = existsSync(middlewareManifestPath) ? JSON.parse(readFileSync(middlewareManifestPath, "utf-8")) diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-next-require.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-next-require.ts index 1f0c86b..532d837 100644 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/inline-next-require.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/inline-next-require.ts @@ -1,6 +1,6 @@ import { existsSync, readFileSync } from "node:fs"; import { Config } from "../../../config"; -import path from "node:path"; +import { join } from "node:path"; /** * The following avoid various Next.js specific files `require`d at runtime since we can just read @@ -8,8 +8,8 @@ import path from "node:path"; */ export function inlineNextRequire(code: string, config: Config) { console.log("# inlineNextRequire"); - const pagesManifestFile = path.join(config.paths.standaloneAppServer, "pages-manifest.json"); - const appPathsManifestFile = path.join(config.paths.standaloneAppServer, "app-paths-manifest.json"); + const pagesManifestFile = join(config.paths.standaloneAppServer, "pages-manifest.json"); + const appPathsManifestFile = join(config.paths.standaloneAppServer, "app-paths-manifest.json"); const pagesManifestFiles = existsSync(pagesManifestFile) ? Object.values(JSON.parse(readFileSync(pagesManifestFile, "utf-8"))).map( @@ -33,7 +33,7 @@ export function inlineNextRequire(code: string, config: Config) { .map( (htmlPage) => ` if (pagePath.endsWith("${htmlPage}")) { - return ${JSON.stringify(readFileSync(path.join(config.paths.standaloneApp, htmlPage), "utf-8"))}; + return ${JSON.stringify(readFileSync(join(config.paths.standaloneApp, htmlPage), "utf-8"))}; } ` ) @@ -42,7 +42,7 @@ export function inlineNextRequire(code: string, config: Config) { .map( (module) => ` if (pagePath.endsWith("${module}")) { - return require(${JSON.stringify(path.join(config.paths.standaloneApp, module))}); + return require(${JSON.stringify(join(config.paths.standaloneApp, module))}); } ` ) diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/patch-find-dir.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-find-dir.ts index cbc78d1..481cbda 100644 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/patch-find-dir.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-find-dir.ts @@ -1,6 +1,6 @@ import { Config } from "../../../config"; import { existsSync } from "node:fs"; -import path from "node:path"; +import { join } from "node:path"; /** * Here we patch `findDir` so that the next server can detect whether the `app` or `pages` directory exists @@ -15,10 +15,10 @@ export function patchFindDir(code: string, config: Config): string { `function findDir(dir, name) { if (dir.endsWith(".next/server")) { if (name === "app") { - return ${existsSync(`${path.join(config.paths.standaloneAppServer, "app")}`)}; + return ${existsSync(`${join(config.paths.standaloneAppServer, "app")}`)}; } if (name === "pages") { - return ${existsSync(`${path.join(config.paths.standaloneAppServer, "pages")}`)}; + return ${existsSync(`${join(config.paths.standaloneAppServer, "pages")}`)}; } } throw new Error("Unknown findDir call: " + dir + " " + name); diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/patch-read-file.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-read-file.ts index 97a265f..d149c44 100644 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/patch-read-file.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/patch-read-file.ts @@ -1,6 +1,6 @@ +import { join, posix } from "node:path"; import { Config } from "../../../config"; import { globSync } from "glob"; -import path from "node:path"; import { normalizePath } from "../../utils"; import { readFileSync } from "node:fs"; @@ -13,7 +13,7 @@ export function patchReadFile(code: string, config: Config): string { code = code.replace( "getBuildId() {", `getBuildId() { - return ${JSON.stringify(readFileSync(path.join(config.paths.standaloneAppDotNext, "BUILD_ID"), "utf-8"))}; + return ${JSON.stringify(readFileSync(join(config.paths.standaloneAppDotNext, "BUILD_ID"), "utf-8"))}; ` ); @@ -21,10 +21,8 @@ export function patchReadFile(code: string, config: Config): string { // (source: https://github.com/vercel/next.js/blob/15aeb92e/packages/next/src/server/load-manifest.ts#L34-L56) // Note: we could/should probably just patch readFileSync here or something! const manifestJsons = globSync( - normalizePath(path.join(config.paths.standaloneAppDotNext, "**", "*-manifest.json")) - ).map((file) => - normalizePath(file).replace(normalizePath(config.paths.standaloneApp) + path.posix.sep, "") - ); + normalizePath(join(config.paths.standaloneAppDotNext, "**", "*-manifest.json")) + ).map((file) => normalizePath(file).replace(normalizePath(config.paths.standaloneApp) + posix.sep, "")); code = code.replace( /function loadManifest\((.+?), .+?\) {/, `$& @@ -32,7 +30,7 @@ export function patchReadFile(code: string, config: Config): string { .map( (manifestJson) => ` if ($1.endsWith("${manifestJson}")) { - return ${readFileSync(path.join(config.paths.standaloneApp, manifestJson), "utf-8")}; + return ${readFileSync(join(config.paths.standaloneApp, manifestJson), "utf-8")}; } ` ) diff --git a/packages/cloudflare/src/cli/build/patches/to-investigate/wrangler-deps.ts b/packages/cloudflare/src/cli/build/patches/to-investigate/wrangler-deps.ts index 3cf82de..5651cc8 100644 --- a/packages/cloudflare/src/cli/build/patches/to-investigate/wrangler-deps.ts +++ b/packages/cloudflare/src/cli/build/patches/to-investigate/wrangler-deps.ts @@ -1,6 +1,6 @@ import { readFileSync, statSync, writeFileSync } from "node:fs"; import { Config } from "../../../config"; -import path from "node:path"; +import { join } from "node:path"; export function patchWranglerDeps(config: Config) { console.log("# patchWranglerDeps"); @@ -13,7 +13,7 @@ export function patchWranglerDeps(config: Config) { // [alias] // # critters is `require`d from `pages.runtime.prod.js` when running wrangler dev, so we need to stub it out // "critters" = "./.next/standalone/node_modules/cf/templates/shims/empty.ts" - const pagesRuntimeFile = path.join(distPath, "compiled", "next-server", "pages.runtime.prod.js"); + const pagesRuntimeFile = join(distPath, "compiled", "next-server", "pages.runtime.prod.js"); const patchedPagesRuntime = readFileSync(pagesRuntimeFile, "utf-8").replace( `e.exports=require("critters")`, @@ -32,7 +32,7 @@ export function patchWranglerDeps(config: Config) { // # try block here: https://github.com/vercel/next.js/blob/9e8266a7/packages/next/src/server/lib/trace/tracer.ts#L27-L31 // # causing the code to require the 'next/dist/compiled/@opentelemetry/api' module instead (which properly works) // #"@opentelemetry/api" = "./.next/standalone/node_modules/cf/templates/shims/throw.ts" - const tracerFile = path.join(distPath, "server", "lib", "trace", "tracer.js"); + const tracerFile = join(distPath, "server", "lib", "trace", "tracer.js"); const patchedTracer = readFileSync(tracerFile, "utf-8").replaceAll( /\w+\s*=\s*require\([^/]*opentelemetry.*\)/g, @@ -57,7 +57,7 @@ export function patchWranglerDeps(config: Config) { function getDistPath(config: Config): string { for (const root of [config.paths.standaloneApp, config.paths.standaloneRoot]) { try { - const distPath = path.join(root, "node_modules", "next", "dist"); + const distPath = join(root, "node_modules", "next", "dist"); if (statSync(distPath).isDirectory()) return distPath; } catch { /* empty */ diff --git a/packages/cloudflare/src/cli/config.ts b/packages/cloudflare/src/cli/config.ts index 59256e8..f52ff2c 100644 --- a/packages/cloudflare/src/cli/config.ts +++ b/packages/cloudflare/src/cli/config.ts @@ -1,4 +1,4 @@ -import path, { relative } from "node:path"; +import { join, relative } from "node:path"; import { readdirSync, statSync } from "node:fs"; const PACKAGE_NAME = "@opennextjs/cloudflare"; @@ -49,16 +49,16 @@ export type Config = { * @returns The configuration, see `Config` */ export function getConfig(projectOpts: ProjectOptions): Config { - const dotNext = path.join(projectOpts.outputDir, ".next"); + const dotNext = join(projectOpts.outputDir, ".next"); const appPath = getNextjsApplicationPath(dotNext).replace(/\/$/, ""); - const standaloneRoot = path.join(dotNext, "standalone"); - const standaloneApp = path.join(standaloneRoot, appPath); - const standaloneAppDotNext = path.join(standaloneApp, ".next"); - const standaloneAppServer = path.join(standaloneAppDotNext, "server"); + const standaloneRoot = join(dotNext, "standalone"); + const standaloneApp = join(standaloneRoot, appPath); + const standaloneAppDotNext = join(standaloneApp, ".next"); + const standaloneAppServer = join(standaloneAppDotNext, "server"); - const nodeModules = path.join(standaloneApp, "node_modules"); - const internalPackage = path.join(nodeModules, ...PACKAGE_NAME.split("/")); - const internalTemplates = path.join(internalPackage, "cli", "templates"); + const nodeModules = join(standaloneApp, "node_modules"); + const internalPackage = join(nodeModules, ...PACKAGE_NAME.split("/")); + const internalTemplates = join(internalPackage, "cli", "templates"); process.env.__OPENNEXT_KV_BINDING_NAME ??= "NEXT_CACHE_WORKERS_KV"; @@ -91,7 +91,7 @@ export function getConfig(projectOpts: ProjectOptions): Config { export function containsDotNextDir(folder: string): boolean { try { - return statSync(path.join(folder, ".next")).isDirectory(); + return statSync(join(folder, ".next")).isDirectory(); } catch { return false; } @@ -124,12 +124,12 @@ function getNextjsApplicationPath(dotNextDir: string): string { throw new Error(`Unexpected Error: no \`.next/server\` folder could be found in \`${serverPath}\``); } - return relative(path.join(dotNextDir, "standalone"), serverPath); + return relative(join(dotNextDir, "standalone"), serverPath); } function findServerParentPath(parentPath: string): string | undefined { try { - if (statSync(path.join(parentPath, ".next", "server")).isDirectory()) { + if (statSync(join(parentPath, ".next", "server")).isDirectory()) { return parentPath; } } catch { @@ -139,8 +139,8 @@ function findServerParentPath(parentPath: string): string | undefined { const folders = readdirSync(parentPath); for (const folder of folders) { - const subFolder = path.join(parentPath, folder); - if (statSync(path.join(parentPath, folder)).isDirectory()) { + const subFolder = join(parentPath, folder); + if (statSync(join(parentPath, folder)).isDirectory()) { const dirServerPath = findServerParentPath(subFolder); if (dirServerPath) { return dirServerPath;