Skip to content

Commit

Permalink
refactor: improve separation of source / output paths in config (#100)
Browse files Browse the repository at this point in the history
* refactor: use source dir to get app path in config

* refactor: restructure config types

* remove source dir change
  • Loading branch information
james-elicx authored Oct 23, 2024
1 parent b1954e9 commit 297e147
Show file tree
Hide file tree
Showing 12 changed files with 88 additions and 63 deletions.
24 changes: 11 additions & 13 deletions packages/cloudflare/src/cli/build/build-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,26 @@ export async function buildWorker(config: Config): Promise<void> {
console.log(`\x1b[35m⚙️ Copying files...\n\x1b[0m`);

// Copy over client-side generated files
await cp(join(config.paths.dotNext, "static"), join(config.paths.outputDir, "assets", "_next", "static"), {
await cp(join(config.paths.source.dotNext, "static"), join(config.paths.output.assets, "_next", "static"), {
recursive: true,
});

// Copy over any static files (e.g. images) from the source project
const publicDir = join(config.paths.sourceDir, "public");
const publicDir = join(config.paths.source.root, "public");
if (existsSync(publicDir)) {
await cp(publicDir, join(config.paths.outputDir, "assets"), {
recursive: true,
});
await cp(publicDir, config.paths.output.assets, { recursive: true });
}

// Copy over prerendered assets (e.g. SSG routes)
copyPrerenderedRoutes(config);

copyPackageCliFiles(packageDistDir, config);

const workerEntrypoint = join(config.paths.internalTemplates, "worker.ts");
const workerOutputFile = join(config.paths.outputDir, "index.mjs");
const workerEntrypoint = join(config.paths.internal.templates, "worker.ts");
const workerOutputFile = join(config.paths.output.root, "index.mjs");

const nextConfigStr =
readFileSync(join(config.paths.standaloneApp, "/server.js"), "utf8")?.match(
readFileSync(join(config.paths.output.standaloneApp, "/server.js"), "utf8")?.match(
/const nextConfig = ({.+?})\n/
)?.[1] ?? {};

Expand All @@ -74,15 +72,15 @@ export async function buildWorker(config: Config): Promise<void> {
// 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": join(config.paths.internalTemplates, "shims", "empty.ts"),
"next/dist/compiled/ws": join(config.paths.internal.templates, "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": join(config.paths.internalTemplates, "shims", "empty.ts"),
"next/dist/compiled/edge-runtime": join(config.paths.internal.templates, "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": join(config.paths.internalTemplates, "shims", "env.ts"),
"@next/env": join(config.paths.internal.templates, "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
Expand Down Expand Up @@ -176,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: join(config.paths.internalTemplates, "shims", "empty.ts"),
path: join(config.paths.internal.templates, "shims", "empty.ts"),
}));
build.onResolve({ filter: /\.\/lib\/node-fs-methods$/ }, () => ({
path: join(config.paths.internalTemplates, "shims", "empty.ts"),
path: join(config.paths.internal.templates, "shims", "empty.ts"),
}));
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Config } from "../../../config";
export function copyPackageCliFiles(packageDistDir: string, config: Config) {
console.log("# copyPackageTemplateFiles");
const sourceDir = join(packageDistDir, "cli");
const destinationDir = join(config.paths.internalPackage, "cli");
const destinationDir = join(config.paths.internal.package, "cli");

cpSync(sourceDir, destinationDir, { recursive: true });
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export async function patchCache(code: string, config: Config): Promise<string>
console.log("# patchCache");

const cacheHandlerFileName = "cache-handler.mjs";
const cacheHandlerEntrypoint = join(config.paths.internalTemplates, "cache-handler", "index.ts");
const cacheHandlerOutputFile = join(config.paths.outputDir, cacheHandlerFileName);
const cacheHandlerEntrypoint = join(config.paths.internal.templates, "cache-handler", "index.ts");
const cacheHandlerOutputFile = join(config.paths.output.root, cacheHandlerFileName);

await build({
entryPoints: [cacheHandlerEntrypoint],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import { getUpdatedWebpackChunksFileContent } from "./get-updated-webpack-chunks
*/
export async function updateWebpackChunksFile(config: Config) {
console.log("# updateWebpackChunksFile");
const webpackRuntimeFile = join(config.paths.standaloneAppServer, "webpack-runtime.js");
const webpackRuntimeFile = join(config.paths.output.standaloneAppServer, "webpack-runtime.js");

const fileContent = readFileSync(webpackRuntimeFile, "utf-8");

const chunks = readdirSync(join(config.paths.standaloneAppServer, "chunks"))
const chunks = readdirSync(join(config.paths.output.standaloneAppServer, "chunks"))
.filter((chunk) => /^\d+\.js$/.test(chunk))
.map((chunk) => {
console.log(` - chunk ${chunk}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,18 @@ import { normalizePath } from "../../utils";
export function inlineEvalManifest(code: string, config: Config): string {
console.log("# inlineEvalManifest");
const manifestJss = globSync(
normalizePath(join(config.paths.standaloneAppDotNext, "**", "*_client-reference-manifest.js"))
).map((file) => normalizePath(file).replace(normalizePath(config.paths.standaloneApp) + posix.sep, ""));
normalizePath(join(config.paths.output.standaloneAppDotNext, "**", "*_client-reference-manifest.js"))
).map((file) =>
normalizePath(file).replace(normalizePath(config.paths.output.standaloneApp) + posix.sep, "")
);
return code.replace(
/function evalManifest\((.+?), .+?\) {/,
`$&
${manifestJss
.map(
(manifestJs) => `
if ($1.endsWith("${manifestJs}")) {
require(${JSON.stringify(join(config.paths.standaloneApp, manifestJs))});
require(${JSON.stringify(join(config.paths.output.standaloneApp, manifestJs))});
return {
__RSC_MANIFEST: {
"${manifestJs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Config } from "../../../config";
export function inlineMiddlewareManifestRequire(code: string, config: Config) {
console.log("# inlineMiddlewareManifestRequire");

const middlewareManifestPath = join(config.paths.standaloneAppServer, "middleware-manifest.json");
const middlewareManifestPath = join(config.paths.output.standaloneAppServer, "middleware-manifest.json");

const middlewareManifest = existsSync(middlewareManifestPath)
? JSON.parse(readFileSync(middlewareManifestPath, "utf-8"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { Config } from "../../../config";
*/
export function inlineNextRequire(code: string, config: Config) {
console.log("# inlineNextRequire");
const pagesManifestFile = join(config.paths.standaloneAppServer, "pages-manifest.json");
const appPathsManifestFile = join(config.paths.standaloneAppServer, "app-paths-manifest.json");
const pagesManifestFile = join(config.paths.output.standaloneAppServer, "pages-manifest.json");
const appPathsManifestFile = join(config.paths.output.standaloneAppServer, "app-paths-manifest.json");

const pagesManifestFiles = existsSync(pagesManifestFile)
? Object.values(JSON.parse(readFileSync(pagesManifestFile, "utf-8"))).map(
Expand All @@ -34,7 +34,7 @@ export function inlineNextRequire(code: string, config: Config) {
.map(
(htmlPage) => `
if (pagePath.endsWith("${htmlPage}")) {
return ${JSON.stringify(readFileSync(join(config.paths.standaloneApp, htmlPage), "utf-8"))};
return ${JSON.stringify(readFileSync(join(config.paths.output.standaloneApp, htmlPage), "utf-8"))};
}
`
)
Expand All @@ -43,7 +43,7 @@ export function inlineNextRequire(code: string, config: Config) {
.map(
(module) => `
if (pagePath.endsWith("${module}")) {
return require(${JSON.stringify(join(config.paths.standaloneApp, module))});
return require(${JSON.stringify(join(config.paths.output.standaloneApp, module))});
}
`
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ export function patchFindDir(code: string, config: Config): string {
`function findDir(dir, name) {
if (dir.endsWith(".next/server")) {
if (name === "app") {
return ${existsSync(`${join(config.paths.standaloneAppServer, "app")}`)};
return ${existsSync(`${join(config.paths.output.standaloneAppServer, "app")}`)};
}
if (name === "pages") {
return ${existsSync(`${join(config.paths.standaloneAppServer, "pages")}`)};
return ${existsSync(`${join(config.paths.output.standaloneAppServer, "pages")}`)};
}
}
throw new Error("Unknown findDir call: " + dir + " " + name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,26 @@ export function patchReadFile(code: string, config: Config): string {
code = code.replace(
"getBuildId() {",
`getBuildId() {
return ${JSON.stringify(readFileSync(join(config.paths.standaloneAppDotNext, "BUILD_ID"), "utf-8"))};
return ${JSON.stringify(readFileSync(join(config.paths.output.standaloneAppDotNext, "BUILD_ID"), "utf-8"))};
`
);

// Same as above, the next-server code loads the manifests with `readFileSync` and we want to avoid that
// (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(join(config.paths.standaloneAppDotNext, "**", "*-manifest.json"))
).map((file) => normalizePath(file).replace(normalizePath(config.paths.standaloneApp) + posix.sep, ""));
normalizePath(join(config.paths.output.standaloneAppDotNext, "**", "*-manifest.json"))
).map((file) =>
normalizePath(file).replace(normalizePath(config.paths.output.standaloneApp) + posix.sep, "")
);
code = code.replace(
/function loadManifest\((.+?), .+?\) {/,
`$&
${manifestJsons
.map(
(manifestJson) => `
if ($1.endsWith("${manifestJson}")) {
return ${readFileSync(join(config.paths.standaloneApp, manifestJson), "utf-8")};
return ${readFileSync(join(config.paths.output.standaloneApp, manifestJson), "utf-8")};
}
`
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function patchWranglerDeps(config: Config) {
* @returns the node_modules/next/dist directory path
*/
function getDistPath(config: Config): string {
for (const root of [config.paths.standaloneApp, config.paths.standaloneRoot]) {
for (const root of [config.paths.output.standaloneApp, config.paths.output.standaloneRoot]) {
try {
const distPath = join(root, "node_modules", "next", "dist");
if (statSync(distPath).isDirectory()) return distPath;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import { readPathsRecursively } from "./read-paths-recursively";
export function copyPrerenderedRoutes(config: Config) {
console.log("# copyPrerenderedRoutes");

const serverAppDirPath = join(config.paths.standaloneAppServer, "app");
const prerenderManifestPath = join(config.paths.standaloneAppDotNext, "prerender-manifest.json");
const outputPath = join(config.paths.outputDir, "assets", SEED_DATA_DIR);
const serverAppDirPath = join(config.paths.output.standaloneAppServer, "app");
const prerenderManifestPath = join(config.paths.output.standaloneAppDotNext, "prerender-manifest.json");
const outputPath = join(config.paths.output.assets, SEED_DATA_DIR);

const prerenderManifest: PrerenderManifest = existsSync(prerenderManifestPath)
? JSON.parse(readFileSync(prerenderManifestPath, "utf8"))
Expand Down
77 changes: 50 additions & 27 deletions packages/cloudflare/src/cli/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,36 @@ export type Config = {
};

paths: {
// Path to the next application
sourceDir: string;
// Path to the output folder
outputDir: string;
// Path to the app's `.next` directory (where `next build` saves the build output)
dotNext: string;
// Path to the application standalone root directory
standaloneRoot: string;
// Path to the application standalone directory (where `next build` saves the standalone app)
standaloneApp: string;
// Path to the `.next` directory specific to the standalone application
standaloneAppDotNext: string;
// Path to the `server` directory specific to the standalone application
standaloneAppServer: string;
// Package in the standalone node_modules
internalPackage: string;
// Templates in the package in the standalone node_modules
internalTemplates: string;
source: {
// Path to the next application
root: string;
// Path to the app's `.next` directory (where `next build` saves the build output)
dotNext: string;
// Path to the application standalone root directory
standaloneRoot: string;
};
output: {
// Path to the output directory
root: string;
// Path to the OpenNext static assets directory
assets: string;
// Path to the app's `.next` directory in the OpenNext output directory
dotNext: string;
// Path to the application standalone root directory
standaloneRoot: string;
// Path to the application standalone directory (where `next build` saves the standalone app)
standaloneApp: string;
// Path to the `.next` directory specific to the standalone application
standaloneAppDotNext: string;
// Path to the `server` directory specific to the standalone application
standaloneAppServer: string;
};
internal: {
// Package in the standalone node_modules
package: string;
// Templates in the package in the standalone node_modules
templates: string;
};
};

cache: {
Expand All @@ -49,6 +61,8 @@ export type Config = {
* @returns The configuration, see `Config`
*/
export function getConfig(projectOpts: ProjectOptions): Config {
const sourceDirDotNext = join(projectOpts.sourceDir, ".next");

const dotNext = join(projectOpts.outputDir, ".next");
const appPath = getNextjsApplicationPath(dotNext).replace(/\/$/, "");
const standaloneRoot = join(dotNext, "standalone");
Expand All @@ -70,15 +84,24 @@ export function getConfig(projectOpts: ProjectOptions): Config {
},

paths: {
sourceDir: projectOpts.sourceDir,
outputDir: projectOpts.outputDir,
dotNext,
standaloneRoot,
standaloneApp,
standaloneAppDotNext,
standaloneAppServer,
internalPackage,
internalTemplates,
source: {
root: projectOpts.sourceDir,
dotNext: sourceDirDotNext,
standaloneRoot: join(sourceDirDotNext, "standalone"),
},
output: {
root: projectOpts.outputDir,
assets: join(projectOpts.outputDir, "assets"),
dotNext,
standaloneRoot,
standaloneApp,
standaloneAppDotNext,
standaloneAppServer,
},
internal: {
package: internalPackage,
templates: internalTemplates,
},
},

cache: {
Expand Down

0 comments on commit 297e147

Please sign in to comment.